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
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/bitcoin/script/tx_checker.rb', line 68 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.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/bitcoin/script/tx_checker.rb', line 43 def check_schnorr_sig(sig, pubkey, sig_version, opts = {}) return false unless [:taproot, :tapscript].include?(sig_version) return false if prevouts.size < input_index 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
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 |
# File 'lib/bitcoin/script/tx_checker.rb', line 90 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 Exception false end end |
#has_error? ⇒ Boolean
117 118 119 |
# File 'lib/bitcoin/script/tx_checker.rb', line 117 def has_error? !@error_code.nil? end |