NAME
Bitcoin::Crypto::Script::Runner - Bitcoin Script runner
SYNOPSIS
use Bitcoin::Crypto::Script::Runner;
use Data::Dumper;
my $runner = Bitcoin::Crypto::Script::Runner->new;
# provide an instance of Bitcoin::Crypto::Script
# runs the script all at once
$runner->execute($script);
# ... or: runs the script step by step
$runner->start($script);
while ($runner->step) {
print 'runner step, stack: ';
print Dumper($runner->stack);
}
print 'FAILURE' unless $runner->success;
print 'resulting stack: ';
print Dumper($runner->stack);
DESCRIPTION
This class instances can be used to execute Bitcoin scripts defined as instances of Bitcoin::Crypto::Script. Scripts can be executed in one go or step by step, and the execution stack is available through an accessor.
One runner can be used to execute scripts multiple times. Each time you call execute or start, the runner state is reset. Initial stack state can be provided to either one of those methods. This provides better control over execution than "run" in Bitcoin::Crypto::Script, which simply executes the script and returns its stack.
INTERFACE
Attributes
transaction
Instance of Bitcoin::Crypto::Transaction wrapped inside Bitcoin::Crypto::Script::Transaction for some extra data. It is optional, but some opcodes will refuse to function without it.
predicate: has_transaction
writer: set_transaction
flags
An instance of Bitcoin::Crypto::Transaction::Flags. If not passed, full set of consensus flags will be assumed (same as calling "new" in Bitcoin::Crypto::Transaction::Flags with no arguments).
writer: set_flags
script
The current script being executed. Will be set automatically in "start". set_script can be used to set it manually.
Not assignable in the constructor
stack
Not assignable in the constructor
Array reference - the stack which is used during script execution. Last item in this array is the stack top. Use $runner->stack->[-1] to examine the stack top.
Each item on the stack is a byte string. Use "to_int" and "to_bool" to transform it into an integer or boolean value in the same fashion bitcoin script interpreter does it.
alt_stack
Not assignable in the constructor
Array reference - alt stack, used by OP_TOALTSTACK and OP_FROMALTSTACK.
operations
Not assignable in the constructor
A proxy to "operations" in Bitcoin::Crypto::Script of the selected "script".
pos
Not assignable in the constructor
Positive integer - the position of the operation to be run in the next step (from "operations").
codeseparator
Not assignable in the constructor
Positive integer - "pos" of the last encountered codeseparator, or undef if none was encountered.
Methods
new
$object = $class->new(%data)
This is a standard Moo constructor, which can be used to create the object. It takes arguments specified in "Attributes".
Returns class instance.
execute
$object = $object->execute($script, \@initial_stack = [])
Executes the script in one go. Returns runner instance (for chaining).
$script must be an instance of Bitcoin::Crypto::Script. If you only have a serialized script in a string, call "from_serialized" in Bitcoin::Crypto::Script first to get a proper script instance. $initial_stack will be used to pre-populate the stack before running the script.
After the method returns call "stack" to get execution stack. This can be done in a single line:
my $stack = $runner->execute($script)->stack;
If errors occur, an exception will be thrown.
start
$object = $object->start($script, \@initial_stack = [])
Same as "execute", but only sets initial runner state and does not actually execute any script opcodes. "step" must be called to continue the execution.
step
while ($runner->step) {
# do something after each step
}
Executes the next script opcode. Returns a false value if the script finished the execution, and a true value otherwise.
"start" must be called before this method is called.
Note that not every opcode will take a step to execute. This means that this script:
OP_1 OP_IF OP_PUSHDATA1 1 0x1f OP_ENDIF
will take four steps to execute (OP_1 -> OP_IF -> 0x1f -> OP_ENDIF).
This one however:
OP_1 OP_IF OP_PUSHDATA1 1 0x1f OP_ELSE OP_PUSHDATA1 2 0xe15e OP_ENDIF
will also take four steps (OP_1 -> OP_IF -> 0x1f -> OP_ELSE). This happens because OP_ELSE performs a jump past OP_ENDIF. If the initial op was OP_0, the execution would be OP_0 -> OP_IF -> 0xe15e -> OP_ENDIF. No OP_ELSE since it was jumped over and reaching OP_ENDIF.
These details should not matter usually, but may be confusing if you would want to for example print your stack step by step. When in doubt, check $runner->pos, which contains the position of the next opcode to execute.
subscript
$subscript = $object->subscript()
Returns current subscript - part of the running script from after the last codeseparator, also known as scriptCode.
Depending on the input spending segwit, the subscript will behave differently:
pre-segwit
Removes all codeseparators after the last executed codeseparator. Currently does not remove the signature (known as FindAndDelete).
segwit
Only removes the part up to the last executed
OP_CODESEPARATOR.
success
$boolean = $object->success()
Returns a boolean indicating whether the script execution was successful.
is_tapscript
$boolean = $object->is_tapscript()
Returns true if currently executed script is a tapscript.
Helper methods
to_int
from_int
my $int = $runner->to_int($byte_vector, $max_bytes = 4);
my $byte_vector = $runner->from_int($int);
These methods encode and decode numbers in format which is used on "stack".
to_int limits the size of an integer to $max_bytes.
On 32-bit machines, BigInts are used. to_int will return an instance of Math::BigInt, while from_int can accept it (but it should also handle regular numbers just fine). On 64-bit machines, perl numbers will be used.
Most of the time, the internal representation of integers on the stack should not matter. If it does matter though, BITCOIN_CRYPTO_USE_BIGINTS environmental variable can be set to 1 to force use of BigInts on 64 bit machines.
to_bool
to_minimal_bool
from_bool
These methods encode and decode booleans in format which is used on "stack". to_minimal_bool variant is used to enforce "minimal_if" in Bitcoin::Crypto::Transaction::Flags.
stack_serialized
Returns the serialized stack. Any null vectors will be transformed to 0x00.