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