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