Class: Bitcoin::Protocol::Tx
- Inherits:
-
Object
- Object
- Bitcoin::Protocol::Tx
- Defined in:
- lib/bitcoin/protocol/tx.rb
Direct Known Subclasses
Constant Summary collapse
- SIGHASH_TYPE =
{ all: 1, none: 2, single: 3, anyonecanpay: 128 }
- DEFAULT_BLOCK_PRIORITY_SIZE =
27000
Instance Attribute Summary collapse
-
#hash ⇒ Object
readonly
transaction hash.
-
#in ⇒ Object
(also: #inputs)
readonly
inputs (Array of TxIn).
-
#lock_time ⇒ Object
lock time.
-
#out ⇒ Object
(also: #outputs)
readonly
outputs (Array of TxOut).
-
#payload ⇒ Object
readonly
raw protocol payload.
-
#ver ⇒ Object
version (usually 1).
Class Method Summary collapse
-
.binary_from_hash(h) ⇒ Object
convert ruby hash to raw binary.
-
.binary_from_json(json_string) ⇒ Object
convert json representation to raw binary.
-
.from_file(path) ⇒ Object
read binary block from a file.
-
.from_hash(h) ⇒ Object
parse ruby hash (see also #to_hash).
-
.from_json(json_string) ⇒ Object
parse json representation.
-
.from_json_file(path) ⇒ Object
read json block from a file.
Instance Method Summary collapse
-
#==(other) ⇒ Object
compare to another tx.
-
#add_in(input) ⇒ Object
add an input.
-
#add_out(output) ⇒ Object
add an output.
-
#binary_hash ⇒ Object
return the tx hash in binary format.
- #calculate_minimum_fee(allow_free = true, mode = :block) ⇒ Object
-
#hash_from_payload(payload) ⇒ Object
(also: #generate_hash)
generate the tx hash for given
payloadin hex format. -
#initialize(data = nil) ⇒ Tx
constructor
create tx from raw binary
data. - #is_coinbase? ⇒ Boolean
- #minimum_block_fee ⇒ Object
- #minimum_relay_fee ⇒ Object
-
#parse_data_from_io(data) ⇒ Object
(also: #parse_data)
parse raw binary data.
-
#signature_hash_for_input(input_idx, outpoint_tx, script_pubkey = nil, hash_type = nil, drop_sigs = nil, script = nil) ⇒ Object
generate a signature hash for input
input_idx. -
#to_hash(options = {}) ⇒ Object
convert to ruby hash (see also #from_hash).
-
#to_json(options = {:space => ''}, *a) ⇒ Object
generates rawblock json as seen in the block explorer.
-
#to_json_file(path) ⇒ Object
write json representation to a file (see also #to_json).
-
#to_payload ⇒ Object
output transaction in raw binary format.
- #validator(store, block = nil) ⇒ Object
-
#verify_input_signature(in_idx, outpoint_tx, block_timestamp = Time.now.to_i) ⇒ Object
verify input signature
in_idxagainst the corresponding output inoutpoint_tx.
Constructor Details
#initialize(data = nil) ⇒ Tx
create tx from raw binary data
42 43 44 45 |
# File 'lib/bitcoin/protocol/tx.rb', line 42 def initialize(data=nil) @ver, @lock_time, @in, @out = 1, 0, [], [] parse_data_from_io(data) if data end |
Instance Attribute Details
#hash ⇒ Object (readonly)
transaction hash
11 12 13 |
# File 'lib/bitcoin/protocol/tx.rb', line 11 def hash @hash end |
#in ⇒ Object (readonly) Also known as: inputs
inputs (Array of TxIn)
14 15 16 |
# File 'lib/bitcoin/protocol/tx.rb', line 14 def in @in end |
#lock_time ⇒ Object
lock time
26 27 28 |
# File 'lib/bitcoin/protocol/tx.rb', line 26 def lock_time @lock_time end |
#out ⇒ Object (readonly) Also known as: outputs
outputs (Array of TxOut)
17 18 19 |
# File 'lib/bitcoin/protocol/tx.rb', line 17 def out @out end |
#payload ⇒ Object (readonly)
raw protocol payload
20 21 22 |
# File 'lib/bitcoin/protocol/tx.rb', line 20 def payload @payload end |
#ver ⇒ Object
version (usually 1)
23 24 25 |
# File 'lib/bitcoin/protocol/tx.rb', line 23 def ver @ver end |
Class Method Details
.binary_from_hash(h) ⇒ Object
convert ruby hash to raw binary
204 |
# File 'lib/bitcoin/protocol/tx.rb', line 204 def self.binary_from_hash(h); from_hash(h).to_payload; end |
.binary_from_json(json_string) ⇒ Object
convert json representation to raw binary
210 |
# File 'lib/bitcoin/protocol/tx.rb', line 210 def self.binary_from_json(json_string); from_json(json_string).to_payload; end |
.from_file(path) ⇒ Object
read binary block from a file
213 |
# File 'lib/bitcoin/protocol/tx.rb', line 213 def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end |
.from_hash(h) ⇒ Object
parse ruby hash (see also #to_hash)
194 195 196 197 198 199 200 201 |
# File 'lib/bitcoin/protocol/tx.rb', line 194 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.instance_eval{ @hash = hash_from_payload(@payload = to_payload) } tx end |
.from_json(json_string) ⇒ Object
parse json representation
207 |
# File 'lib/bitcoin/protocol/tx.rb', line 207 def self.from_json(json_string); from_hash( JSON.load(json_string) ); end |
.from_json_file(path) ⇒ Object
read json block from a file
216 |
# File 'lib/bitcoin/protocol/tx.rb', line 216 def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end |
Instance Method Details
#==(other) ⇒ Object
compare to another tx
32 33 34 |
# File 'lib/bitcoin/protocol/tx.rb', line 32 def ==(other) @hash == other.hash end |
#add_in(input) ⇒ Object
add an input
54 |
# File 'lib/bitcoin/protocol/tx.rb', line 54 def add_in(input); (@in ||= []) << input; end |
#add_out(output) ⇒ Object
add an output
57 |
# File 'lib/bitcoin/protocol/tx.rb', line 57 def add_out(output); (@out ||= []) << output; end |
#binary_hash ⇒ Object
return the tx hash in binary format
37 38 39 |
# File 'lib/bitcoin/protocol/tx.rb', line 37 def binary_hash [@hash].pack("H*").reverse end |
#calculate_minimum_fee(allow_free = true, mode = :block) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/bitcoin/protocol/tx.rb', line 227 def calculate_minimum_fee(allow_free=true, mode=:block) # Base fee is either nMinTxFee or nMinRelayTxFee base_fee = (mode == :relay) ? Bitcoin.network[:min_relay_tx_fee] : Bitcoin.network[:min_tx_fee] tx_size = to_payload.bytesize min_fee = (1 + tx_size / 1_000) * base_fee if allow_free # There is a free transaction area in blocks created by most miners, # * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 # to be considered to fall into this category. We don't want to encourage sending # multiple transactions instead of one big transaction to avoid fees. # * If we are creating a transaction we allow transactions up to 1,000 bytes # to be considered safe and assume they can likely make it into this section. min_fee = 0 if tx_size < (mode == :block ? 1_000 : DEFAULT_BLOCK_PRIORITY_SIZE - 1_000) end # This code can be removed after enough miners have upgraded to version 0.9. # Until then, be safe when sending and require a fee if any output is less than CENT if min_fee < base_fee && mode == :block outputs.each{|output| (min_fee = base_fee; break) if output.value < Bitcoin::CENT } end min_fee = Bitcoin::network[:max_money] unless min_fee.between?(0, Bitcoin::network[:max_money]) min_fee end |
#hash_from_payload(payload) ⇒ Object Also known as: generate_hash
generate the tx hash for given payload in hex format
48 49 50 |
# File 'lib/bitcoin/protocol/tx.rb', line 48 def hash_from_payload(payload) Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse_hth end |
#is_coinbase? ⇒ Boolean
253 254 255 |
# File 'lib/bitcoin/protocol/tx.rb', line 253 def is_coinbase? inputs.size == 1 and inputs.first.coinbase? end |
#minimum_block_fee ⇒ Object
225 |
# File 'lib/bitcoin/protocol/tx.rb', line 225 def minimum_block_fee; calculate_minimum_fee(allow_free=true, :block); end |
#minimum_relay_fee ⇒ Object
224 |
# File 'lib/bitcoin/protocol/tx.rb', line 224 def minimum_relay_fee; calculate_minimum_fee(allow_free=true, :relay); end |
#parse_data_from_io(data) ⇒ Object Also known as: parse_data
parse raw binary data
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/bitcoin/protocol/tx.rb', line 60 def parse_data_from_io(data) buf = data.is_a?(String) ? StringIO.new(data) : data payload_start = buf.pos @ver = buf.read(4).unpack("V")[0] in_size = Protocol.unpack_var_int_from_io(buf) @in = [] in_size.times{ @in << TxIn.from_io(buf) } out_size = Protocol.unpack_var_int_from_io(buf) @out = [] out_size.times{ @out << TxOut.from_io(buf) } @lock_time = buf.read(4).unpack("V")[0] payload_end = buf.pos; buf.seek(payload_start) @payload = buf.read( payload_end-payload_start ) @hash = hash_from_payload(@payload) if buf.eof? true else data.is_a?(StringIO) ? buf : buf.read end end |
#signature_hash_for_input(input_idx, outpoint_tx, script_pubkey = nil, hash_type = nil, drop_sigs = nil, script = nil) ⇒ Object
generate a signature hash for input input_idx. either pass the outpoint_tx or the script_pubkey directly.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/bitcoin/protocol/tx.rb', line 105 def signature_hash_for_input(input_idx, outpoint_tx, script_pubkey=nil, hash_type=nil, drop_sigs=nil, script=nil) # https://github.com/bitcoin/bitcoin/blob/e071a3f6c06f41068ad17134189a4ac3073ef76b/script.cpp#L834 # http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Script.java#318 # https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works # https://github.com/bitcoin/bitcoin/blob/c2e8c8acd8ae0c94c70b59f55169841ad195bb99/src/script.cpp#L1058 # https://en.bitcoin.it/wiki/OP_CHECKSIG return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range hash_type ||= SIGHASH_TYPE[:all] pin = @in.map.with_index{|input,idx| if idx == input_idx script_pubkey ||= outpoint_tx.out[ input.prev_out_index ].pk_script script_pubkey = script if script # force binary aa script script_pubkey = Bitcoin::Script.drop_signatures(script_pubkey, drop_sigs) if drop_sigs # array of signature to drop (slow) #p Bitcoin::Script.new(script_pubkey).to_string input.to_payload(script_pubkey) else case (hash_type & 0x1f) when SIGHASH_TYPE[:none]; input.to_payload("", "\x00\x00\x00\x00") when SIGHASH_TYPE[:single]; input.to_payload("", "\x00\x00\x00\x00") else; input.to_payload("") end end } pout = @out.map(&:to_payload) in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size) case (hash_type & 0x1f) when SIGHASH_TYPE[:none] pout = "" out_size = Protocol.pack_var_int(0) when SIGHASH_TYPE[:single] return "\x01".ljust(32, "\x00") if input_idx >= @out.size # ERROR: SignatureHash() : input_idx=%d out of range (SIGHASH_SINGLE) pout = @out[0...(input_idx+1)].map.with_index{|out,idx| (idx==input_idx) ? out.to_payload : out.to_null_payload }.join out_size = Protocol.pack_var_int(input_idx+1) end if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0 in_size, pin = Protocol.pack_var_int(1), [ pin[input_idx] ] end buf = [ [@ver].pack("V"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join Digest::SHA256.digest( Digest::SHA256.digest( buf ) ) end |
#to_hash(options = {}) ⇒ Object
convert to ruby hash (see also #from_hash)
170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/bitcoin/protocol/tx.rb', line 170 def to_hash( = {}) @hash ||= hash_from_payload(to_payload) h = { 'hash' => @hash, 'ver' => @ver, 'vin_sz' => @in.size, 'vout_sz' => @out.size, 'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize, 'in' => @in.map{|i| i.to_hash() }, 'out' => @out.map{|o| o.to_hash() } } h end |
#to_json(options = {:space => ''}, *a) ⇒ Object
generates rawblock json as seen in the block explorer.
183 184 185 |
# File 'lib/bitcoin/protocol/tx.rb', line 183 def to_json( = {:space => ''}, *a) JSON.pretty_generate( to_hash(), ) end |
#to_json_file(path) ⇒ Object
write json representation to a file (see also #to_json)
189 190 191 |
# File 'lib/bitcoin/protocol/tx.rb', line 189 def to_json_file(path) File.open(path, 'wb'){|f| f.print to_json; } end |
#to_payload ⇒ Object
output transaction in raw binary format
91 92 93 94 95 96 97 98 |
# File 'lib/bitcoin/protocol/tx.rb', line 91 def to_payload pin = "" @in.each{|input| pin << input.to_payload } pout = "" @out.each{|output| pout << output.to_payload } [@ver].pack("V") << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V") end |
#validator(store, block = nil) ⇒ Object
218 219 220 |
# File 'lib/bitcoin/protocol/tx.rb', line 218 def validator(store, block = nil) @validator ||= Bitcoin::Validation::Tx.new(self, store, block) end |
#verify_input_signature(in_idx, outpoint_tx, block_timestamp = Time.now.to_i) ⇒ Object
verify input signature in_idx against the corresponding output in outpoint_tx
155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/bitcoin/protocol/tx.rb', line 155 def verify_input_signature(in_idx, outpoint_tx, =Time.now.to_i) outpoint_idx = @in[in_idx].prev_out_index script_sig = @in[in_idx].script_sig script_pubkey = outpoint_tx.out[outpoint_idx].pk_script script = script_sig + script_pubkey Bitcoin::Script.new(script).run() do |pubkey,sig,hash_type,drop_sigs,script| # this IS the checksig callback, must return true/false hash = signature_hash_for_input(in_idx, outpoint_tx, nil, hash_type, drop_sigs, script) #hash = signature_hash_for_input(in_idx, nil, script_pubkey, hash_type, drop_sigs, script) Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] ) end end |