Class: Bitcoin::Protocol::Tx

Inherits:
Object
  • Object
show all
Defined in:
lib/bitcoin/protocol/tx.rb

Constant Summary collapse

MARKER =
0
FLAG =
1
SIGHASH_TYPE =
{ all: 1, none: 2, single: 3, anyonecanpay: 128 }
DEFAULT_BLOCK_PRIORITY_SIZE =
27000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = nil) ⇒ Tx

create tx from raw binary data



51
52
53
54
55
# File 'lib/bitcoin/protocol/tx.rb', line 51

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

Instance Attribute Details

#flagObject

Returns the value of attribute flag.



35
36
37
# File 'lib/bitcoin/protocol/tx.rb', line 35

def flag
  @flag
end

#hashObject (readonly)

transaction hash



14
15
16
# File 'lib/bitcoin/protocol/tx.rb', line 14

def hash
  @hash
end

#inObject (readonly) Also known as: inputs

inputs (Array of TxIn)



17
18
19
# File 'lib/bitcoin/protocol/tx.rb', line 17

def in
  @in
end

#lock_timeObject

lock time



29
30
31
# File 'lib/bitcoin/protocol/tx.rb', line 29

def lock_time
  @lock_time
end

#markerObject

Returns the value of attribute marker.



34
35
36
# File 'lib/bitcoin/protocol/tx.rb', line 34

def marker
  @marker
end

#outObject (readonly) Also known as: outputs

outputs (Array of TxOut)



20
21
22
# File 'lib/bitcoin/protocol/tx.rb', line 20

def out
  @out
end

#payloadObject (readonly)

raw protocol payload



23
24
25
# File 'lib/bitcoin/protocol/tx.rb', line 23

def payload
  @payload
end

#scriptsObject (readonly)

parsed / evaluated input scripts cached for later use



32
33
34
# File 'lib/bitcoin/protocol/tx.rb', line 32

def scripts
  @scripts
end

#verObject

version (usually 1)



26
27
28
# File 'lib/bitcoin/protocol/tx.rb', line 26

def ver
  @ver
end

Class Method Details

.binary_from_hash(h) ⇒ Object

convert ruby hash to raw binary



416
417
418
419
# File 'lib/bitcoin/protocol/tx.rb', line 416

def self.binary_from_hash(h)
  tx = from_hash(h)
  tx.to_payload
end

.binary_from_json(json_string) ⇒ Object

convert json representation to raw binary



425
# File 'lib/bitcoin/protocol/tx.rb', line 425

def self.binary_from_json(json_string); from_json(json_string).to_payload; end

.from_file(path) ⇒ Object

read binary block from a file



428
# File 'lib/bitcoin/protocol/tx.rb', line 428

def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end

.from_hash(h, do_raise = true) ⇒ Object

parse ruby hash (see also #to_hash)



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/bitcoin/protocol/tx.rb', line 399

def self.from_hash(h, do_raise=true)
  tx = new(nil)
  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_old_payload) }
  if h['hash'] && (h['hash'] != tx.hash)
    raise "Tx hash mismatch! Claimed: #{h['hash']}, Actual: #{tx.hash}" if do_raise
  end
  tx
end

.from_json(json_string) ⇒ Object

parse json representation



422
# File 'lib/bitcoin/protocol/tx.rb', line 422

def self.from_json(json_string); from_hash( JSON.load(json_string) ); end

.from_json_file(path) ⇒ Object

read json block from a file



431
# File 'lib/bitcoin/protocol/tx.rb', line 431

def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end

Instance Method Details

#==(other) ⇒ Object

compare to another tx



41
42
43
# File 'lib/bitcoin/protocol/tx.rb', line 41

def ==(other)
  @hash == other.hash
end

#add_in(input) ⇒ Object

add an input



64
# File 'lib/bitcoin/protocol/tx.rb', line 64

def add_in(input); (@in ||= []) << input; end

#add_out(output) ⇒ Object

add an output



67
# File 'lib/bitcoin/protocol/tx.rb', line 67

def add_out(output); (@out ||= []) << output; end

#binary_hashObject

return the tx hash in binary format



46
47
48
# File 'lib/bitcoin/protocol/tx.rb', line 46

def binary_hash
  @binary_hash ||= [@hash].pack("H*").reverse
end

#bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp = Time.now.to_i, opts = {}) ⇒ Object



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/bitcoin/protocol/tx.rb', line 350

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

#calculate_minimum_fee(allow_free = true, mode = :block) ⇒ Object



472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/bitcoin/protocol/tx.rb', line 472

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 ? Bitcoin.network[:free_tx_bytes] : 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 do |output|
      if output.value < Bitcoin.network[:dust]
        # If per dust fee, then we add min fee for each output less than dust.
        # Otherwise, we set to min fee if there is any output less than dust.
        if Bitcoin.network[:per_dust_fee]
          min_fee += base_fee
        else
          min_fee = base_fee
          break
        end
      end
    end
  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



58
59
60
# File 'lib/bitcoin/protocol/tx.rb', line 58

def hash_from_payload(payload)
  Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse_hth
end

#is_coinbase?Boolean

Returns:

  • (Boolean)


509
510
511
# File 'lib/bitcoin/protocol/tx.rb', line 509

def is_coinbase?
  inputs.size == 1 and inputs.first.coinbase?
end

#is_final?(block_height, block_time) ⇒ Boolean

Checks if transaction is final taking into account height and time of a block in which it is located (or about to be included if it’s unconfirmed tx).

Returns:

  • (Boolean)


439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/bitcoin/protocol/tx.rb', line 439

def is_final?(block_height, block_time)
  # No time lock - tx is final.
  return true if lock_time == 0

  # Time based nLockTime implemented in 0.1.6
  # If lock_time is below the magic threshold treat it as a block height.
  # If lock_time is above the threshold, it's a unix timestamp.
  return true if lock_time < (lock_time < Bitcoin::LOCKTIME_THRESHOLD ? block_height : block_time)

  inputs.each{|input| return false if !input.is_final? }

  return true
end

#legacy_sigops_countObject



453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/bitcoin/protocol/tx.rb', line 453

def legacy_sigops_count
  # Note: input scripts normally never have any opcodes since every input script 
  # can be statically reduced to a pushdata-only script.
  # However, anyone is allowed to create a non-standard transaction with any opcodes in the inputs.
  count = 0
  self.in.each do |txin|
    count += Bitcoin::Script.new(txin.script_sig).sigops_count_accurate(false)
  end
  self.out.each do |txout|
    count += Bitcoin::Script.new(txout.pk_script).sigops_count_accurate(false)
  end
  count
end

#lexicographical_sort!Object

sort transaction inputs and outputs under BIP 69 github.com/bitcoin/bips/blob/master/bip-0069.mediawiki



525
526
527
528
# File 'lib/bitcoin/protocol/tx.rb', line 525

def lexicographical_sort!
  inputs.sort_by!{|i| [i.previous_output, i.prev_out_index]}
  outputs.sort_by!{|o| [o.amount, o.pk_script.bth]}
end

#minimum_block_feeObject



470
# File 'lib/bitcoin/protocol/tx.rb', line 470

def minimum_block_fee; calculate_minimum_fee(allow_free=true, :block); end

#minimum_relay_feeObject



469
# File 'lib/bitcoin/protocol/tx.rb', line 469

def minimum_relay_fee; calculate_minimum_fee(allow_free=true, :relay); end

#normalized_hashObject Also known as: nhash



513
514
515
# File 'lib/bitcoin/protocol/tx.rb', line 513

def normalized_hash
  signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).reverse.hth
end

#parse_data_from_io(data) ⇒ Object Also known as: parse_data

parse raw binary data



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/bitcoin/protocol/tx.rb', line 70

def parse_data_from_io(data)
  buf = data.is_a?(String) ? StringIO.new(data) : data

  @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
    in_size = Protocol.unpack_var_int_from_io(buf)
    witness = true
  end

  @in = []
  in_size.times{
    break if buf.eof?
    @in << TxIn.from_io(buf)
  }

  return false if buf.eof?

  out_size = Protocol.unpack_var_int_from_io(buf)
  @out = []
  out_size.times{
    break if buf.eof?
    @out << TxOut.from_io(buf)
  }

  return false if buf.eof?

  if witness
    in_size.times do |i|
      witness_count = Protocol.unpack_var_int_from_io(buf)
      witness_count.times do
        size = Protocol.unpack_var_int_from_io(buf)
        @in[i].script_witness.stack << buf.read(size)
      end
    end
  end

  @lock_time = buf.read(4).unpack("V")[0]

  @hash = hash_from_payload(to_old_payload)

  if buf.eof?
    true
  else
    data.is_a?(StringIO) ? buf : buf.read
  end
end

#signature_hash_for_input(input_idx, subscript, hash_type = nil) ⇒ Object

generate a signature hash for input input_idx. either pass the outpoint_tx or the script_pubkey directly.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bitcoin/protocol/tx.rb', line 163

def signature_hash_for_input(input_idx, subscript, hash_type=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

  # Note: BitcoinQT checks if input_idx >= @in.size and returns 1 with an error message.
  # But this check is never actually useful because BitcoinQT would crash 
  # right before VerifyScript if input index is out of bounds (inside CScriptCheck::operator()()).
  # That's why we don't need to do such a check here.
  #
  # However, if you look at the case SIGHASH_TYPE[:single] below, we must 
  # return 1 because it's possible to have more inputs than outputs and BitcoinQT returns 1 as well.
  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
      subscript = subscript.out[ input.prev_out_index ].script if subscript.respond_to?(:out) # legacy api (outpoint_tx)
      input.to_payload(subscript)
    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

#signature_hash_for_witness_input(input_idx, witness_program, prev_out_value, witness_script = nil, hash_type = nil, skip_separator_index = 0) ⇒ Object

generate a witness signature hash for input input_idx. github.com/bitcoin/bips/blob/master/bip-0143.mediawiki



217
218
219
220
221
222
223
224
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
251
252
253
254
255
256
257
# File 'lib/bitcoin/protocol/tx.rb', line 217

def signature_hash_for_witness_input(input_idx, witness_program, prev_out_value, witness_script = nil, hash_type=nil, skip_separator_index = 0)
  return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range

  hash_type ||= SIGHASH_TYPE[:all]

  script = Bitcoin::Script.new(witness_program)
  raise "ScriptPubkey does not contain witness program." unless script.is_witness?

  hash_prevouts = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i| [i.prev_out_hash, i.prev_out_index].pack("a32V")}.join))
  hash_sequence = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i|i.sequence}.join))
  outpoint = [@in[input_idx].prev_out_hash, @in[input_idx].prev_out_index].pack("a32V")
  amount = [prev_out_value].pack("Q")
  nsequence = @in[input_idx].sequence

  if script.is_witness_v0_keyhash?
    script_code = [["1976a914", script.get_hash160, "88ac"].join].pack("H*")
  elsif script.is_witness_v0_scripthash?
    raise "witness script does not match script pubkey" unless Bitcoin::Script.to_witness_p2sh_script(Digest::SHA256.digest(witness_script).bth) == witness_program
    script = skip_separator_index > 0 ? Bitcoin::Script.new(witness_script).subscript_codeseparator(skip_separator_index) : witness_script
    script_code = Bitcoin::Protocol.pack_var_string(script)
  end

  hash_outputs = Digest::SHA256.digest(Digest::SHA256.digest(@out.map{|o|o.to_payload}.join))

  case (hash_type & 0x1f)
    when SIGHASH_TYPE[:single]
      hash_outputs = input_idx >= @out.size ? "\x00".ljust(32, "\x00") : Digest::SHA256.digest(Digest::SHA256.digest(@out[input_idx].to_payload))
      hash_sequence = "\x00".ljust(32, "\x00")
    when SIGHASH_TYPE[:none]
      hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
  end

  if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
    hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
  end

  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

#sizeObject



433
434
435
# File 'lib/bitcoin/protocol/tx.rb', line 433

def size
  payload.bytesize
end

#to_hash(options = {}) ⇒ Object

convert to ruby hash (see also #from_hash)



374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/bitcoin/protocol/tx.rb', line 374

def to_hash(options = {})
  @hash ||= hash_from_payload(to_old_payload)
  h = {
    'hash' => @hash, 'ver' => @ver, # 'nid' => normalized_hash,
    'vin_sz' => @in.size, 'vout_sz' => @out.size,
    'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
    'in'  =>  @in.map{|i|i.to_hash(options)},
    'out' => @out.map{|o| o.to_hash(options) }
  }
  h['nid'] = normalized_hash  if options[:with_nid]
  h
end

#to_json(options = {:space => ''}, *a) ⇒ Object

generates rawblock json as seen in the block explorer.



388
389
390
# File 'lib/bitcoin/protocol/tx.rb', line 388

def to_json(options = {:space => ''}, *a)
  JSON.pretty_generate( to_hash(options), options )
end

#to_json_file(path) ⇒ Object

write json representation to a file (see also #to_json)



394
395
396
# File 'lib/bitcoin/protocol/tx.rb', line 394

def to_json_file(path)
  File.open(path, 'wb'){|f| f.print to_json; }
end

#to_old_payloadObject



133
134
135
136
137
138
139
140
# File 'lib/bitcoin/protocol/tx.rb', line 133

def to_old_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

#to_payloadObject

output transaction in raw binary format



129
130
131
# File 'lib/bitcoin/protocol/tx.rb', line 129

def to_payload
  witness? ? to_witness_payload : to_old_payload
end

#to_witness_payloadObject

output transaction in raw binary format with witness



143
144
145
146
147
148
149
# File 'lib/bitcoin/protocol/tx.rb', line 143

def to_witness_payload
  buf = [@ver, MARKER, FLAG].pack('Vcc')
  buf << Protocol.pack_var_int(@in.length) << @in.map(&:to_payload).join
  buf << Protocol.pack_var_int(@out.length) << @out.map(&:to_payload).join
  buf << witness_payload << [@lock_time].pack('V')
  buf
end

#verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp = Time.now.to_i, opts = {}) ⇒ Object

verify input signature in_idx against the corresponding output in outpoint_tx outpoint

options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/bitcoin/protocol/tx.rb', line 264

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) 
    outpoint_tx_or_script.out[outpoint_idx].pk_script
  else
    # Otherwise, it's already a script.
    outpoint_tx_or_script
  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?
  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

#verify_witness_input_signature(in_idx, outpoint_tx_or_script, prev_out_amount, block_timestamp = Time.now.to_i, opts = {}) ⇒ Object

verify witness input signature in_idx against the corresponding output in outpoint_tx outpoint

options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/bitcoin/protocol/tx.rb', line 298

def verify_witness_input_signature(in_idx, outpoint_tx_or_script, 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)
  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

  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

  @in[in_idx].script_witness.stack.each{|s|script_sig << Bitcoin::Script.pack_pushdata(s)}
  code_separator_index = 0

  if script_pubkey.is_witness_v0_keyhash? # P2WPKH
    @scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_hash160_script(script_pubkey.get_hash160))
  elsif script_pubkey.is_witness_v0_scripthash? # P2WSH
    witness_hex = @in[in_idx].script_witness.stack.last.bth
    witness_script = Bitcoin::Script.new(witness_hex.htb)
    return false unless Bitcoin.sha256(witness_hex) == script_pubkey.get_hash160
    @scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_p2sh_script(Bitcoin.hash160(witness_hex)))
  else
    return false
  end

  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|
    if script_pubkey.is_witness_v0_keyhash?
      hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, nil, hash_type)
    elsif script_pubkey.is_witness_v0_scripthash?
      hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, witness_hex.htb, hash_type, code_separator_index)
      code_separator_index += 1 if witness_script.codeseparator_count > code_separator_index
    end
    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

#witness?Boolean

Returns:

  • (Boolean)


155
156
157
# File 'lib/bitcoin/protocol/tx.rb', line 155

def witness?
  !@in.find { |i| !i.script_witness.empty? }.nil?
end

#witness_hashObject

get witness hash



519
520
521
# File 'lib/bitcoin/protocol/tx.rb', line 519

def witness_hash
  hash_from_payload(to_witness_payload)
end

#witness_payloadObject



151
152
153
# File 'lib/bitcoin/protocol/tx.rb', line 151

def witness_payload
  @in.map { |i| i.script_witness.to_payload }.join
end