Class: Bitcoin::TxChecker
- Inherits:
-
Object
- Object
- Bitcoin::TxChecker
- Defined in:
- lib/bitcoin/script/tx_checker.rb
Instance Attribute Summary collapse
-
#amount ⇒ Object
readonly
Returns the value of attribute amount.
-
#error_code ⇒ Object
Returns the value of attribute error_code.
-
#input_index ⇒ Object
readonly
Returns the value of attribute input_index.
-
#prevouts ⇒ Object
readonly
Returns the value of attribute prevouts.
-
#tx ⇒ Object
readonly
Returns the value of attribute tx.
Instance Method Summary collapse
- #check_locktime(locktime) ⇒ Object
-
#check_schnorr_sig(sig, pubkey, sig_version, opts = {}) ⇒ Boolean
check schnorr signature.
- #check_sequence(sequence) ⇒ Object
-
#check_sig(sig, pubkey, script_code, sig_version, allow_hybrid: false) ⇒ Boolean
check ecdsa signature.
- #has_error? ⇒ Boolean
-
#initialize(tx: nil, amount: 0, input_index: nil, prevouts: []) ⇒ TxChecker
constructor
A new instance of TxChecker.
Constructor Details
#initialize(tx: nil, amount: 0, input_index: nil, prevouts: []) ⇒ TxChecker
Returns a new instance of TxChecker.
10 11 12 13 14 15 |
# File 'lib/bitcoin/script/tx_checker.rb', line 10 def initialize(tx: nil, amount: 0, input_index: nil, prevouts: []) @tx = tx @input_index = input_index @prevouts = prevouts @amount = input_index && prevouts[input_index] ? prevouts[input_index].value : amount end |
Instance Attribute Details
#amount ⇒ Object (readonly)
Returns the value of attribute amount.
6 7 8 |
# File 'lib/bitcoin/script/tx_checker.rb', line 6 def amount @amount end |
#error_code ⇒ Object
Returns the value of attribute error_code.
8 9 10 |
# File 'lib/bitcoin/script/tx_checker.rb', line 8 def error_code @error_code end |
#input_index ⇒ Object (readonly)
Returns the value of attribute input_index.
5 6 7 |
# File 'lib/bitcoin/script/tx_checker.rb', line 5 def input_index @input_index end |
#prevouts ⇒ Object (readonly)
Returns the value of attribute prevouts.
7 8 9 |
# File 'lib/bitcoin/script/tx_checker.rb', line 7 def prevouts @prevouts end |
#tx ⇒ Object (readonly)
Returns the value of attribute tx.
4 5 6 |
# File 'lib/bitcoin/script/tx_checker.rb', line 4 def tx @tx end |
Instance Method Details
#check_locktime(locktime) ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/bitcoin/script/tx_checker.rb', line 73 def check_locktime(locktime) # There are two kinds of nLockTime: lock-by-blockheight and lock-by-blocktime, # distinguished by whether nLockTime < LOCKTIME_THRESHOLD. # We want to compare apples to apples, so fail the script unless the type of nLockTime being tested is the same as the nLockTime in the transaction. unless ((tx.lock_time < LOCKTIME_THRESHOLD && locktime < LOCKTIME_THRESHOLD) || (tx.lock_time >= LOCKTIME_THRESHOLD && locktime >= LOCKTIME_THRESHOLD)) return false end # Now that we know we're comparing apples-to-apples, the comparison is a simple numeric one. return false if locktime > tx.lock_time # Finally the nLockTime feature can be disabled and thus CHECKLOCKTIMEVERIFY bypassed if every txin has been finalized by setting nSequence to maxint. # The transaction would be allowed into the blockchain, making the opcode ineffective. # Testing if this vin is not final is sufficient to prevent this condition. # Alternatively we could test all inputs, but testing just this input minimizes the data required to prove correct CHECKLOCKTIMEVERIFY execution. return false if TxIn::SEQUENCE_FINAL == tx.inputs[input_index].sequence true end |
#check_schnorr_sig(sig, pubkey, sig_version, opts = {}) ⇒ Boolean
check schnorr signature.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/bitcoin/script/tx_checker.rb', line 44 def check_schnorr_sig(sig, pubkey, sig_version, opts = {}) return false unless [:taproot, :tapscript].include?(sig_version) # A taproot sighash commits to the amount and scriptPubkey of every prevout, so a partial # set does not fail to verify, it verifies against a sighash consensus never computes. # Checked here rather than left to the generator, since the rescue below would turn it # into a script error. raise ArgumentError, 'prevouts must be specified for all inputs.' unless prevouts.size == tx.in.size sig = sig.htb return set_error(SCRIPT_ERR_SCHNORR_SIG_SIZE) unless [64, 65].include?(sig.bytesize) hash_type = SIGHASH_TYPE[:default] if sig.bytesize == 65 hash_type = sig[-1].unpack1('C') sig = sig[0..-2] return set_error(SCRIPT_ERR_SCHNORR_SIG_HASHTYPE) if hash_type == SIGHASH_TYPE[:default] # hash type can not specify 0x00. end return set_error(SCRIPT_ERR_SCHNORR_SIG_HASHTYPE) unless (hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83)) begin sighash = tx.sighash_for_input(input_index, opts: opts, hash_type: hash_type, sig_version: sig_version, prevouts: prevouts) key = Key.from_xonly_pubkey(pubkey) key.verify(sig, sighash, algo: :schnorr) rescue ArgumentError return set_error(SCRIPT_ERR_SCHNORR_SIG_HASHTYPE) end end |
#check_sequence(sequence) ⇒ Object
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 |
# File 'lib/bitcoin/script/tx_checker.rb', line 95 def check_sequence(sequence) tx_sequence = tx.inputs[input_index].sequence # Fail if the transaction's version number is not set high enough to trigger BIP 68 rules. return false if tx.version < 2 # Sequence numbers with their most significant bit set are not consensus constrained. # Testing that the transaction's sequence number do not have this bit set prevents using this property to get around a CHECKSEQUENCEVERIFY check. return false unless tx_sequence & TxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG == 0 # Mask off any bits that do not have consensus-enforced meaning before doing the integer comparisons locktime_mask = TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | TxIn::SEQUENCE_LOCKTIME_MASK tx_sequence_masked = tx_sequence & locktime_mask sequence_masked = sequence & locktime_mask # There are two kinds of nSequence: lock-by-blockheight and lock-by-blocktime, # distinguished by whether sequence_masked < TxIn#SEQUENCE_LOCKTIME_TYPE_FLAG. # We want to compare apples to apples, so fail the script # unless the type of nSequenceMasked being tested is the same as the nSequenceMasked in the transaction. unless ((tx_sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) || (tx_sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG)) return false end # Now that we know we're comparing apples-to-apples, the comparison is a simple numeric one. sequence_masked <= tx_sequence_masked end |
#check_sig(sig, pubkey, script_code, sig_version, allow_hybrid: false) ⇒ Boolean
check ecdsa signature
23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/bitcoin/script/tx_checker.rb', line 23 def check_sig(sig, pubkey, script_code, sig_version, allow_hybrid: false) return false if sig.empty? sig = sig.htb hash_type = sig[-1].unpack1('C') sig = sig[0..-2] sighash = tx.sighash_for_input(input_index, script_code, opts: {amount: amount}, hash_type: hash_type, sig_version: sig_version) key_type = pubkey.start_with?('02') || pubkey.start_with?('03') ? Key::TYPES[:compressed] : Key::TYPES[:uncompressed] begin key = Key.new(pubkey: pubkey, key_type: key_type, allow_hybrid: allow_hybrid) key.verify(sig, sighash) rescue StandardError false end end |
#has_error? ⇒ Boolean
122 123 124 |
# File 'lib/bitcoin/script/tx_checker.rb', line 122 def has_error? !@error_code.nil? end |