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 }
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(block_size = 1, 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(block_size = 1, allow_free = true, mode = :block) ⇒ Object
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/bitcoin/protocol/tx.rb', line 225 def calculate_minimum_fee(block_size=1, allow_free=true, mode=:block) base_fee = (mode == :relay) ? Bitcoin.network[:min_relay_tx_fee] : Bitcoin.network[:min_tx_fee] tx_size = to_payload.bytesize new_block_size = block_size + tx_size min_fee = (1 + tx_size / 1_000) * base_fee if allow_free if block_size == 1 min_fee = 0 if tx_size < 10_000 else min_fee = 0 if new_block_size < 27_000 end end if min_fee < base_fee outputs.each{|output| (min_fee = base_fee; break) if output.value < Bitcoin::CENT } end if block_size != 1 && new_block_size >= (Bitcoin::MAX_BLOCK_SIZE_GEN/2) #return Bitcoin::network[:max_money] if new_block_size >= Bitcoin::MAX_BLOCK_SIZE_GEN min_fee *= Bitcoin::MAX_BLOCK_SIZE_GEN / (Bitcoin::MAX_BLOCK_SIZE_GEN - new_block_size) 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
252 253 254 |
# File 'lib/bitcoin/protocol/tx.rb', line 252 def is_coinbase? inputs.size == 1 and inputs.first.coinbase? end |
#minimum_block_fee ⇒ Object
223 |
# File 'lib/bitcoin/protocol/tx.rb', line 223 def minimum_block_fee; calculate_minimum_fee(1_000, true, :block); end |
#minimum_relay_fee ⇒ Object
222 |
# File 'lib/bitcoin/protocol/tx.rb', line 222 def minimum_relay_fee; calculate_minimum_fee(1_000, 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 |