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