Skip to content

Commit

Permalink
script debugger: allow user to set verification flags (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhanne committed May 22, 2015
1 parent 0f300a4 commit c4e9d74
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 4 deletions.
26 changes: 24 additions & 2 deletions app/controllers/blocks_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'timeout'
require 'method_source'

class BlocksController < ApplicationController

Expand Down Expand Up @@ -72,7 +73,14 @@ def address
# custom script. when :id is given, the :sig_hash can be overridden, and when
# no :sig_hash is given or found, signature validation will be skipped.
def script
require 'method_source'
@options = {
verify_dersig: true,
verify_low_s: true,
verify_strictenc: true,
verify_sigpushonly: true,
verify_minimaldata: true,
verify_cleanstack: true }
@options.keys.each {|k| @options[k] = params[k] == "1" if params[k] }

# if tx_hash:idx is given, fetch tx from db and use those scripts
if params[:id] =~ /([0-9a-fA-F]{64}):(\d+)/
Expand All @@ -92,14 +100,28 @@ def script
# if there is a script, execute it
if @script_sig && @pk_script
@script = Bitcoin::Script.new(@script_sig, @pk_script)
@result = @script.run do |pubkey, sig, hash_type, subscript|

if @options[:verify_sigpushonly] && !@script.is_push_only?(@script_sig)
return (@result, @debug = false, [[], :verify_sigpushonly])
end

if @options[:verify_minimaldata] && !@script.pushes_are_canonical?
return (@result, @debug = false, [[], :verify_minimaldata])
end

@result = @script.run(Time.now.to_i, @options) do |pubkey, sig, hash_type, subscript|
# get sig_hash from tx if tx is given and sig_hash isn't already set
@sig_hash ||= @tx.signature_hash_for_input(@txin.tx_idx, subscript, hash_type) if @tx && @txin
# if there is a sig_hash (either computed or passed as parameter), verify signature,
# else assume it's valid.
@sig_hash ? Bitcoin.verify_signature(@sig_hash, sig, pubkey.unpack("H*")[0]) : true
end
@debug = @script.debug

if @options[:verify_cleanstack] && !@script.stack.empty?
@result = false
@debug += [@script.stack, :verify_cleanstack]
end
end

@page_title = "Debug Script Execution"
Expand Down
23 changes: 23 additions & 0 deletions app/views/blocks/script.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@
%td
= text_field_tag :sig_hash, @sig_hash_str, size: 64
Optional; If empty, all signatures are assumed to be valid.

%tr
%th Verify
%td
- { minimaldata: "Check that all numbers are encoded with the minimum possible number of bytes",
sigpushonly: "Check that the script_sig only contains pushes, no opcodes",
cleanstack: "Check that the scripts leaves a clean stack",
dersig: "Check that all signatures are in valid DER encoding",
low_s: "Check that all signatures have non-negative S values",
strictenc: "Check that all signatures and pubkeys are in canonical encoding",
}.each do |flag, desc|
= hidden_field_tag "verify_#{flag}", "0"
= check_box_tag "verify_#{flag}", "1", @options[:"verify_#{flag}"], id: "verify_#{flag}_cb"
- klass = (@debug && @debug[-1] == :"verify_#{flag}") ? "error" : ""
= label_tag "verify_#{flag}_cb", flag, title: desc, class: klass
(hover for info)

%tr
%th Result
%th
Expand Down Expand Up @@ -57,6 +74,12 @@
%pre The number #{op.split("_")[1]} is pushed onto the stack.
- when "RESULT"
%pre True if the remaining stack is anything other than empty or 0.
- when :verify_minimaldata
%pre Not all numbers are encoded with the minimum possible number of bytes!
- when :verify_sigpushonly
%pre The script_sig may only contain PUSHDATA operations, no other opcodes!
- when :verify_cleanstack
%pre The script may not leave any values on the stack after successful execution!
- else
- comment = Bitcoin::Script.instance_method(@debug[i+1].downcase).comment rescue ""
%pre= comment.split(/\n?#\s/).map(&:strip).join("\n")
Expand Down
50 changes: 48 additions & 2 deletions spec/controllers/blocks_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@
end

it "should execute arbitrary script with sighash" do
get :script, script_sig: script_sig, pk_script: pk_script, sig_hash: sig_hash
get :script, script_sig: script_sig, pk_script: pk_script, sig_hash: sig_hash,
verify_low_s: "0"
assigns(:result).should == true
end

Expand All @@ -162,10 +163,55 @@

it "should ignore signatures when no sighash is given" do
s, p = script_sig.split(" "); s[140] = "a"; s[141] = "a"; script_sig = [s, p].join(" ")
get :script, script_sig: script_sig, pk_script: pk_script, sig_hash: ""
get :script, script_sig: script_sig, pk_script: pk_script, sig_hash: "", verify_low_s: "0"
assigns(:result).should == true
end

describe :verify do

it "should verify minimaldata" do
opts = { script_sig: "01", pk_script: "", sig_hash: "" }

get :script, opts
assigns(:result).should == false
assigns(:debug).should == [[], :verify_minimaldata]

get :script, opts.merge(verify_minimaldata: "0")
assigns(:result).should == true
end

it "should verify sigpushonly" do
opts = { script_sig: "1 OP_DROP 1", pk_script: "", sig_hash: "" }

get :script, opts
assigns(:result).should == false
assigns(:debug).should == [[], :verify_sigpushonly]

get :script, opts.merge(verify_sigpushonly: "0")
assigns(:result).should == true
end

it "should verify cleanstack" do
get :script, script_sig: "1 1", pk_script: "", sig_hash: ""
assigns(:result).should == false
assigns(:debug).should == [[], "OP_1", [1], "OP_1", [1, 1], "OP_CODESEPARATOR", [1, 1], "RESULT", [1], :verify_cleanstack]

get :script, script_sig: "1 1", pk_script: "", sig_hash: "", verify_cleanstack: "0"
assigns(:result).should == true
end

it "should verify low_s" do
opts = { script_sig: script_sig, pk_script: pk_script, sighash: sig_hash }

get :script, opts
assigns(:result).should == false

get :script, opts.merge(verify_low_s: "0")
assigns(:result).should == true
end

end

end

describe :scripts do
Expand Down

0 comments on commit c4e9d74

Please sign in to comment.