Class: BSV::Script::Interpreter

Inherits:
Object
  • Object
show all
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).

Examples:

Evaluate scripts without transaction context

BSV::Script::Interpreter.evaluate(unlock_script, lock_script)

Verify a transaction input

BSV::Script::Interpreter.verify(
  tx: transaction, input_index: 0,
  unlock_script: input.script, lock_script: prev_output.script,
  satoshis: prev_output.satoshis
)

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#astackObject (readonly)

Returns the value of attribute astack.



40
41
42
# File 'lib/bsv/script/interpreter/interpreter.rb', line 40

def astack
  @astack
end

#dstackObject (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) ⇒ Boolean

Evaluate unlock + lock scripts without transaction context.

Signature operations will always fail (no sighash available).

Parameters:

  • unlock_script (Script)

    the unlocking script

  • lock_script (Script)

    the locking script

Returns:

  • (Boolean)

    true if execution succeeds

Raises:



64
65
66
67
68
69
# File 'lib/bsv/script/interpreter/interpreter.rb', line 64

def self.evaluate(unlock_script, lock_script)
  new(
    unlock_script: unlock_script,
    lock_script: lock_script
  ).execute
end

.verify(tx:, input_index:, unlock_script:, lock_script:, satoshis:) ⇒ Boolean

Verify a transaction input by evaluating its scripts.

Parameters:

  • tx (Transaction::Transaction)

    the transaction being verified

  • input_index (Integer)

    the input index within the transaction

  • unlock_script (Script)

    the input’s unlocking script

  • lock_script (Script)

    the previous output’s locking script

  • satoshis (Integer)

    the value of the previous output in satoshis

Returns:

  • (Boolean)

    true if verification succeeds

Raises:



80
81
82
83
84
85
86
87
88
# File 'lib/bsv/script/interpreter/interpreter.rb', line 80

def self.verify(tx:, input_index:, unlock_script:, lock_script:, satoshis:)
  new(
    unlock_script: unlock_script,
    lock_script: lock_script,
    tx: tx,
    input_index: input_index,
    satoshis: satoshis
  ).execute
end

Instance Method Details

#executeObject



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
# File 'lib/bsv/script/interpreter/interpreter.rb', line 90

def execute
  scripts = [@unlock_script, @lock_script]

  scripts.each_with_index do |script, script_idx|
    @current_script = script
    chunks = script.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
  true
end