lib/bitcoin/protocol/tx.rb in bitcoin-ruby-0.0.6 vs lib/bitcoin/protocol/tx.rb in bitcoin-ruby-0.0.7

- old
+ new

@@ -42,10 +42,11 @@ end # create tx from raw binary +data+ def initialize(data=nil) @ver, @lock_time, @in, @out, @scripts = 1, 0, [], [], [] + @enable_bitcoinconsensus = !!ENV['USE_BITCOINCONSENSUS'] parse_data_from_io(data) if data end # generate the tx hash for given +payload+ in hex format def hash_from_payload(payload) @@ -158,11 +159,17 @@ end # verify input signature +in_idx+ against the corresponding # output in +outpoint_tx+ # outpoint - def verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i) + # + # options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc + def verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={}) + if @enable_bitcoinconsensus + return bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp, opts) + end + outpoint_idx = @in[in_idx].prev_out_index script_sig = @in[in_idx].script_sig # If given an entire previous transaction, take the script from it script_pubkey = if outpoint_tx_or_script.respond_to?(:out) @@ -171,16 +178,45 @@ # Otherwise, it's already a script. outpoint_tx_or_script end @scripts[in_idx] = Bitcoin::Script.new(script_sig, script_pubkey) - @scripts[in_idx].run(block_timestamp) do |pubkey,sig,hash_type,subscript| + 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? + sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript| hash = signature_hash_for_input(in_idx, subscript, hash_type) Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] ) end + # BIP62 rule #6 + 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={}) + 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 + + 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] + flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_CLEANSTACK if opts[:verify_cleanstack] + flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_LOW_S if opts[:verify_low_s] + + payload ||= to_payload + Bitcoin::BitcoinConsensus.verify_script(in_idx, script_pubkey, payload, flags) + end + # convert to ruby hash (see also #from_hash) def to_hash(options = {}) @hash ||= hash_from_payload(to_payload) h = { 'hash' => @hash, 'ver' => @ver, # 'nid' => normalized_hash, @@ -204,13 +240,15 @@ end # parse ruby hash (see also #to_hash) def self.from_hash(h) tx = new(nil) - tx.ver, tx.lock_time = *h.values_at('ver', 'lock_time') - h['in'] .each{|input| tx.add_in TxIn.from_hash(input) } - h['out'].each{|output| tx.add_out TxOut.from_hash(output) } + tx.ver, tx.lock_time = (h['ver'] || h['version']), h['lock_time'] + ins = h['in'] || h['inputs'] + outs = h['out'] || h['outputs'] + ins .each{|input| tx.add_in TxIn.from_hash(input) } + outs.each{|output| tx.add_out TxOut.from_hash(output) } tx.instance_eval{ @hash = hash_from_payload(@payload = to_payload) } tx end # convert ruby hash to raw binary @@ -226,17 +264,10 @@ def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end # read json block from a file def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end - # Get a Bitcoin::Validation object to validate this block. It needs a +store+ - # to validate against, a block to validate tx chains inside one block, and - # optionally takes the +block_validator+ as an optimization. - def validator(store, block = nil, block_validator = nil) - @validator ||= Bitcoin::Validation::Tx.new(self, store, block, block_validator) - end - def size payload.bytesize end # Checks if transaction is final taking into account height and time @@ -314,10 +345,10 @@ def is_coinbase? inputs.size == 1 and inputs.first.coinbase? end def normalized_hash - signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).unpack("H*")[0] + signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).reverse.hth end alias :nhash :normalized_hash end end