Class: BSV::Script::Interpreter
- Inherits:
-
Object
- Object
- BSV::Script::Interpreter
- Includes:
- Operations::Arithmetic, Operations::Bitwise, Operations::Crypto, Operations::DataPush, Operations::FlowControl, Operations::Splice, Operations::StackOps
- Defined in:
- lib/bsv/script/interpreter/interpreter.rb,
lib/bsv/script/interpreter/operations/crypto.rb,
lib/bsv/script/interpreter/operations/splice.rb,
lib/bsv/script/interpreter/operations/bitwise.rb,
lib/bsv/script/interpreter/operations/data_push.rb,
lib/bsv/script/interpreter/operations/stack_ops.rb,
lib/bsv/script/interpreter/operations/arithmetic.rb,
lib/bsv/script/interpreter/operations/flow_control.rb
Overview
Bitcoin script interpreter implementing the post-Genesis BSV script engine.
Evaluates unlock + lock script pairs, supporting the full BSV opcode set including restored opcodes (OP_MUL, OP_LSHIFT, OP_CAT, etc.) and post-Genesis rules (OP_RETURN as early success, no script size limits).
Defined Under Namespace
Modules: Operations
Constant Summary collapse
- CONDITIONAL_OPCODES =
Conditional opcodes must be processed even in non-executing branches to maintain correct nesting depth. OP_VERIF and OP_VERNOTIF are included here because they open conditional blocks (like OP_IF/OP_NOTIF) and must be dispatched to track nesting depth even when the branch is not executing. This matches the Go SDK's IsConditional() function.
[ Opcodes::OP_IF, Opcodes::OP_NOTIF, Opcodes::OP_ELSE, Opcodes::OP_ENDIF, Opcodes::OP_VERIF, Opcodes::OP_VERNOTIF ].freeze
- MAX_CONDITIONAL_DEPTH =
Maximum nesting depth for OP_IF / OP_NOTIF blocks. Prevents interpreter stack overflow from deeply nested conditionals.
256- CHRONICLE_ONLY_OPCODES =
Opcodes that require Chronicle to execute. With explicit flags but without UTXO_AFTER_CHRONICLE, executing any of these raises DISABLED_OPCODE. OP_VER / OP_VERIF / OP_VERNOTIF are included because pre-Chronicle they're either reserved (OP_VER) or behave as a conditional-only NOP in non-executing branches — execution itself is disabled. Mirrors TS Spend.ts (lines ~694-709) and Go IsDisabled.
[ Opcodes::OP_2MUL, Opcodes::OP_2DIV, Opcodes::OP_VER, Opcodes::OP_VERIF, Opcodes::OP_VERNOTIF ].freeze
- VER_CONDITIONAL_OPCODES =
The two version-conditional opcodes — extracted to module-level constants so the interpreter hot path (every conditional dispatch / every executed chunk) doesn't allocate a fresh Array per call.
[Opcodes::OP_VERIF, Opcodes::OP_VERNOTIF].freeze
- KNOWN_FLAGS =
Recognised verification flag names. Catches typos (a misspelled
SIGPUSHONLLYwould otherwise silently disable enforcement) and forces any new flag to be declared here before use. The set is the union of flags appearing in the canonical conformance corpus and the Bitcoin Corescript_tests.jsonfixture, plus the witness/taproot family that downstream callers filter out before reaching the interpreter. Set[ 'CHECKLOCKTIMEVERIFY', 'CHECKSEQUENCEVERIFY', 'CLEANSTACK', 'DERSIG', 'DISCOURAGE_UPGRADABLE_NOPS', 'DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM', 'GENESIS', 'LOW_S', 'MINIMALDATA', 'MINIMALIF', 'NULLDUMMY', 'NULLFAIL', 'P2SH', 'SIGHASH_FORKID', 'SIGPUSHONLY', 'STRICTENC', 'TAPROOT', 'UTXO_AFTER_CHRONICLE', 'UTXO_AFTER_GENESIS', 'WITNESS' ].freeze
Instance Attribute Summary collapse
-
#astack ⇒ Object
readonly
Returns the value of attribute astack.
-
#dstack ⇒ Object
readonly
Returns the value of attribute dstack.
Class Method Summary collapse
-
.evaluate(unlock_script, lock_script, flags: nil, tx_version: nil) ⇒ Boolean
Evaluate unlock + lock scripts without transaction context.
-
.verify(tx:, input_index:, unlock_script:, lock_script:, satoshis:, flags: nil) ⇒ Boolean
Verify a transaction input by evaluating its scripts.
Instance Method Summary collapse
Instance Attribute Details
#astack ⇒ Object (readonly)
Returns the value of attribute astack.
40 41 42 |
# File 'lib/bsv/script/interpreter/interpreter.rb', line 40 def astack @astack end |
#dstack ⇒ Object (readonly)
Returns the value of attribute dstack.
40 41 42 |
# File 'lib/bsv/script/interpreter/interpreter.rb', line 40 def dstack @dstack end |
Class Method Details
.evaluate(unlock_script, lock_script, flags: nil, tx_version: nil) ⇒ Boolean
Evaluate unlock + lock scripts without transaction context.
Signature operations will always fail (no sighash available).
118 119 120 121 122 123 124 125 |
# File 'lib/bsv/script/interpreter/interpreter.rb', line 118 def self.evaluate(unlock_script, lock_script, flags: nil, tx_version: nil) new( unlock_script: unlock_script, lock_script: lock_script, flags: flags, tx_version: tx_version ).execute end |
.verify(tx:, input_index:, unlock_script:, lock_script:, satoshis:, flags: nil) ⇒ Boolean
Verify a transaction input by evaluating its scripts.
142 143 144 145 146 147 148 149 150 151 |
# File 'lib/bsv/script/interpreter/interpreter.rb', line 142 def self.verify(tx:, input_index:, unlock_script:, lock_script:, satoshis:, flags: nil) new( unlock_script: unlock_script, lock_script: lock_script, tx: tx, input_index: input_index, satoshis: satoshis, flags: flags ).execute end |
Instance Method Details
#execute ⇒ Object
153 154 155 156 157 158 159 160 161 162 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 |
# File 'lib/bsv/script/interpreter/interpreter.rb', line 153 def execute enforce_sig_pushonly scripts = [@unlock_script, @lock_script] script_names = %w[unlock_script lock_script] scripts.each_with_index do |script, script_idx| @current_script = script chunks = script.chunks BSV.logger&.debug { "[Interpreter] === #{script_names[script_idx]} (#{chunks.length} chunks) ===" } chunks.each_with_index do |chunk, chunk_idx| @current_chunk_idx = chunk_idx execute_opcode(chunk) break if @early_return end break if @early_return && script_idx == 1 # Between scripts: verify conditionals balanced raise ScriptError.new(ScriptErrorCode::UNBALANCED_CONDITIONAL, 'unbalanced conditional') unless @cond_stack.empty? # Clear alt stack between scripts @astack.clear # Reset state for next script @last_code_sep = 0 @early_return = false @after_op_return = false end check_final_stack BSV.logger&.debug { "[Interpreter] final stack: #{@dstack.length} items -> success" } true end |