lib/bitcoin/protocol/tx.rb in bitcoin-ruby-0.0.12 vs lib/bitcoin/protocol/tx.rb in bitcoin-ruby-0.0.13
- old
+ new
@@ -79,28 +79,23 @@
@ver = buf.read(4).unpack("V")[0]
return false if buf.eof?
- in_size = Protocol.unpack_var_int_from_io(buf)
-
# segwit serialization format is defined by https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
- witness = false
- if in_size.zero?
- @marker = 0
- @flag = buf.read(1).unpack('c').first
+ # Also note that it is impossible to parse 0 input transactions. Regular transactions with 0 inputs look
+ # like malformed segwit transactions.
+ @marker = buf.read(1).unpack('c').first
+ @flag = buf.read(1).unpack('c').first
- # marker must be zero and flag must be non-zero to be valid segwit
- if @marker == 0 && @flag != 0
- in_size = Protocol.unpack_var_int_from_io(buf)
- witness = true
- else
- # Undo the last read in case this isn't a segwit transaction
- buf.seek(buf.pos - 1)
- end
- end
+ witness = @marker == 0 && @flag != 0
+ # Non-segwit format does not contain marker or flag fields.
+ buf.seek(buf.pos - 2) unless witness
+
+ in_size = Protocol.unpack_var_int_from_io(buf)
+
@in = []
in_size.times{
break if buf.eof?
@in << TxIn.from_io(buf)
}
@@ -269,39 +264,30 @@
signature_hash_for_input_bip143(input_idx, script_code, prev_out_value, hash_type)
end
# verify input signature +in_idx+ against the corresponding
# output in +outpoint_tx+
- # outpoint
+ # outpoint. This arg can also be a Script or TxOut.
#
# options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc, fork_id
- def verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={})
+ def verify_input_signature(in_idx, outpoint_data, block_timestamp=Time.now.to_i, opts={})
if @enable_bitcoinconsensus
- return bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp, opts)
+ return bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp, opts)
end
# If FORKID is enabled, we also ensure strict encoding.
opts[:verify_strictenc] ||= !!opts[:fork_id]
outpoint_idx = @in[in_idx].prev_out_index
script_sig = @in[in_idx].script_sig
- amount = nil
- script_pubkey = nil
- if outpoint_tx_or_script.respond_to?(:out)
- # If given an entire previous transaction, take the script from it
- prevout = outpoint_tx_or_script.out[outpoint_idx]
- amount = prevout.value
- script_pubkey = prevout.pk_script
- else
- if opts[:fork_id]
- raise "verify_input_signature must be called with a previous transaction if " \
- "SIGHASH_FORKID is enabled"
- end
+ amount = amount_from_outpoint_data(outpoint_data, outpoint_idx)
+ script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
- # Otherwise, it's already a script.
- script_pubkey = outpoint_tx_or_script
+ if opts[:fork_id] && amount.nil?
+ raise "verify_input_signature must be called with a previous transaction or " \
+ "transaction output if SIGHASH_FORKID is enabled"
end
@scripts[in_idx] = Bitcoin::Script.new(script_sig, script_pubkey)
return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
@@ -315,28 +301,23 @@
return sig_valid
end
# verify witness input signature +in_idx+ against the corresponding
# output in +outpoint_tx+
- # outpoint
+ # outpoint. This arg can also be a Script or TxOut
#
# options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc
- def verify_witness_input_signature(in_idx, outpoint_tx_or_script, prev_out_amount, block_timestamp=Time.now.to_i, opts={})
+ def verify_witness_input_signature(in_idx, outpoint_data, prev_out_amount, block_timestamp=Time.now.to_i, opts={})
if @enable_bitcoinconsensus
- return bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp, opts)
+ return bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp, opts)
end
outpoint_idx = @in[in_idx].prev_out_index
script_sig = ''
- # If given an entire previous transaction, take the script from it
- script_pubkey = if outpoint_tx_or_script.respond_to?(:out)
- Bitcoin::Script.new(outpoint_tx_or_script.out[outpoint_idx].pk_script)
- else
- # Otherwise, it's already a script.
- Bitcoin::Script.new(outpoint_tx_or_script)
- end
+ script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
+ script_pubkey = Bitcoin::Script.new(script_pubkey)
if script_pubkey.is_p2sh?
redeem_script = Bitcoin::Script.new(@in[in_idx].script_sig).get_pubkey
script_pubkey = Bitcoin::Script.new(redeem_script.htb) if Bitcoin.hash160(redeem_script) == script_pubkey.get_hash160 # P2SH-P2WPKH or P2SH-P2WSH
end
@@ -370,21 +351,15 @@
return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
return sig_valid
end
- def bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={})
+ def bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp=Time.now.to_i, opts={})
raise "Bitcoin::BitcoinConsensus shared library not found" unless Bitcoin::BitcoinConsensus.lib_available?
- # If given an entire previous transaction, take the script from it
- script_pubkey = if outpoint_tx_or_script.respond_to?(:out)
- outpoint_idx = @in[in_idx].prev_out_index
- outpoint_tx_or_script.out[outpoint_idx].pk_script
- else
- # Otherwise, it's already a script.
- outpoint_tx_or_script
- end
+ outpoint_idx = @in[in_idx].prev_out_index
+ script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
flags = Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_NONE
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_P2SH if block_timestamp >= 1333238400
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_SIGPUSHONLY if opts[:verify_sigpushonly]
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_MINIMALDATA if opts[:verify_minimaldata]
@@ -580,9 +555,33 @@
buf = [ [@ver].pack("V"), hash_prevouts, hash_sequence, outpoint,
script_code, amount, nsequence, hash_outputs, [@lock_time, hash_type].pack("VV")].join
Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
+ end
+
+ def script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
+ if outpoint_data.respond_to?(:out)
+ # If given an entire previous transaction, take the script from it
+ outpoint_data.out[outpoint_idx].pk_script
+ elsif outpoint_data.respond_to?(:pk_script)
+ # If given an transaction output, take the script
+ outpoint_data.pk_script
+ else
+ # Otherwise, we assume it's already a script.
+ outpoint_data
+ end
+ end
+
+ def amount_from_outpoint_data(outpoint_data, outpoint_idx)
+ if outpoint_data.respond_to?(:out)
+ # If given an entire previous transaction, take the amount from the
+ # output at the outpoint_idx
+ outpoint_data.out[outpoint_idx].amount
+ elsif outpoint_data.respond_to?(:pk_script)
+ # If given an transaction output, take the amount
+ outpoint_data.amount
+ end
end
end
end
end