lib/bitcoin/script.rb in bitcoin-ruby-0.0.11 vs lib/bitcoin/script.rb in bitcoin-ruby-0.0.12

- old
+ new

@@ -148,11 +148,17 @@ OPCODES_ALIAS.each{|k,v| OPCODES_PARSE_STRING[k] = v } 2.upto(16).each{|i| OPCODES_PARSE_STRING["OP_#{i}"] = OP_2_16[i-2] } 2.upto(16).each{|i| OPCODES_PARSE_STRING["#{i}" ] = OP_2_16[i-2] } [1,2,4].each{|i| OPCODES_PARSE_STRING.delete("OP_PUSHDATA#{i}") } - SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 } + SIGHASH_TYPE = { + all: 1, + none: 2, + single: 3, + forkid: 64, + anyonecanpay: 128 + }.freeze attr_reader :raw, :chunks, :debug, :stack # create a new script. +bytes+ is typically input_script + output_script def initialize(input_script, previous_output_script=nil) @@ -318,10 +324,23 @@ raise "Opcode should be within [0x00, 0xff]" end self end + # Adds the opcode corresponding to the given number. Returns self. + def append_number(number) + opcode = + case number + when -1 then OP_1NEGATE + when 0 then OP_0 + when 1 then OP_1 + when 2..16 then OP_2 + (16 - number) + end + raise "No opcode for number #{number}" if opcode.nil? + append_opcode(opcode) + end + # Adds binary string as pushdata. Pushdata will be encoded in the most compact form # (unless the string contains internal info about serialization that's added by Script class) # Returns self. def append_pushdata(pushdata_string) raise "Pushdata should be a string" if !pushdata_string.is_a?(String) @@ -1401,21 +1420,28 @@ return invalid unless Bitcoin::Script.check_signature_encoding?(signature, opts) return (@stack << 0) if signature == "" sig, hash_type = parse_sig(signature) - subscript = sighash_subscript(drop_sigs) + subscript = sighash_subscript(drop_sigs, opts) if check_callback == nil # for tests @stack << 1 else # real signature check callback @stack << ((check_callback.call(pubkey, sig, hash_type, subscript) == true) ? 1 : 0) end end - def sighash_subscript(drop_sigs) + def sighash_subscript(drop_sigs, opts = {}) + if opts[:fork_id] + drop_sigs.reject! do |signature| + _, hash_type = parse_sig(signature) + (hash_type&SIGHASH_TYPE[:forkid]) != 0 + end + end + if inner_p2sh? && @inner_script_code ::Bitcoin::Script.new(@inner_script_code).to_binary_without_signatures(drop_sigs) else to_binary_without_signatures(drop_sigs) end @@ -1456,11 +1482,11 @@ drop_sigs = sigs.dup # Bitcoin-core removes an extra item from the stack @stack.pop - subscript = sighash_subscript(drop_sigs) + subscript = sighash_subscript(drop_sigs, opts) success = true while success && n_sigs > 0 sig, pub = sigs.pop, pubkeys.pop return (@stack << 0) unless Bitcoin::Script.check_pubkey_encoding?(pub, opts) @@ -1520,11 +1546,20 @@ # Loosely matches CheckSignatureEncoding() def self.check_signature_encoding?(sig, opts={}) return true if sig.bytesize == 0 return false if (opts[:verify_dersig] || opts[:verify_low_s] || opts[:verify_strictenc]) and !is_der_signature?(sig) return false if opts[:verify_low_s] && !is_low_der_signature?(sig) - return false if opts[:verify_strictenc] && !is_defined_hashtype_signature?(sig) + + if opts[:verify_strictenc] + return false unless is_defined_hashtype_signature?(sig) + + hash_type = sig.unpack('C*')[-1] + uses_forkid = (hash_type&SIGHASH_TYPE[:forkid]) != 0 + return false if opts[:fork_id] && !uses_forkid + return false if !opts[:fork_id] && uses_forkid + end + true end # Loosely correlates with IsDERSignature() from interpreter.cpp def self.is_der_signature?(sig) @@ -1600,10 +1635,10 @@ def self.is_defined_hashtype_signature?(sig) return false if sig.empty? s = sig.unpack("C*") - hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay])) + hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay] | SIGHASH_TYPE[:forkid])) return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single] # Non-canonical signature: unknown hashtype byte true end