lib/bitcoin/script.rb in bitcoin-ruby-0.0.2 vs lib/bitcoin/script.rb in bitcoin-ruby-0.0.3

- old
+ new

@@ -990,13 +990,19 @@ # asking +check_callback+ to do the actual signature verification. # This is used by Protocol::Tx#verify_input_signature def op_checksig(check_callback) return invalid if @stack.size < 2 pubkey = @stack.pop + #return (@stack << 0) unless Bitcoin::Script.is_canonical_pubkey?(pubkey) # only for isStandard drop_sigs = [ @stack[-1] ] - sig, hash_type = parse_sig(@stack.pop) + signature = cast_to_string(@stack.pop) + #return (@stack << 0) unless Bitcoin::Script.is_canonical_signature?(signature) # only for isStandard + return (@stack << 0) if signature == "" + + sig, hash_type = parse_sig(signature) + if inner_p2sh? script_code = @inner_script_code || to_binary_without_signatures(drop_sigs) drop_sigs = nil else script_code, drop_sigs = nil, nil @@ -1036,24 +1042,26 @@ pubkeys = pop_string(n_pubkeys) n_sigs = pop_int return invalid unless (0..n_pubkeys).include?(n_sigs) return invalid unless @stack.last(n_sigs).all?{|e| e.is_a?(String) && e != '' } - sigs = (drop_sigs = pop_string(n_sigs)).map{|s| parse_sig(s) } + sigs = drop_sigs = pop_string(n_sigs) - @stack.pop if @stack[-1] == '' # remove OP_NOP from stack + @stack.pop if @stack[-1] && cast_to_bignum(@stack[-1]) == 0 # remove OP_0 from stack if inner_p2sh? script_code = @inner_script_code || to_binary_without_signatures(drop_sigs) drop_sigs = nil else script_code, drop_sigs = nil, nil end valid_sigs = 0 - sigs.each{|sig, hash_type| pubkeys.each{|pubkey| - valid_sigs += 1 if check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code) + sigs.each{|sig| pubkeys.each{|pubkey| + next if sig == "" + signature, hash_type = parse_sig(sig) + valid_sigs += 1 if check_callback.call(pubkey, signature, hash_type, drop_sigs, script_code) }} @stack << ((valid_sigs >= n_sigs) ? 1 : (invalid; 0)) end @@ -1078,9 +1086,30 @@ else return false # "Non-canonical public key: compressed nor uncompressed" end true end + + + SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 } + + def self.is_canonical_signature?(sig) + return false if sig.bytesize < 9 # Non-canonical signature: too short + return false if sig.bytesize > 73 # Non-canonical signature: too long + + s = sig.unpack("C*") + + hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay])) + return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single] # Non-canonical signature: unknown hashtype byte + + return false if s[0] != 0x30 # Non-canonical signature: wrong type + return false if s[1] != s.size-3 # Non-canonical signature: wrong length marker + + # TODO: add/port rest from bitcoind + + true + end + private def parse_sig(sig) hash_type = sig[-1].unpack("C")[0]