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. It is optional, but some opcodes will refuse to function without it.
predicate: has_transaction
writer: set_transaction
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
Array reference - An array of operations to be executed. Same as "operations" in Bitcoin::Crypto::Script and automatically obtained by calling it.
pos
Not assignable in the constructor
Positive integer - the position of the operation to be run in the next step (from "operations").
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, they will be thrown as exceptions. See "EXCEPTIONS".
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, with all other codeseparators removed.
success
$boolean = $object->success()
Returns a boolean indicating whether the script execution was successful.
Helper methods
to_int, from_int
my $int = $runner->to_int($byte_vector);
my $byte_vector = $runner->from_int($int);
These methods encode and decode numbers in format which is used on "stack".
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).
to_bool, from_bool
These methods encode and decode booleans in format which is used on "stack".
stack_serialized
Returns the serialized stack. Any null vectors will be transformed to 0x00
.
CAVEATS
There is curretly no limit on the size of byte vector which is going to be transformed to an integer for ops like OP_ADD. BigInts are used for all integers.
EXCEPTIONS
This module throws an instance of Bitcoin::Crypto::Exception if it encounters an error. It can produce the following error types from the Bitcoin::Crypto::Exception namespace:
ScriptRuntime - script has encountered a runtime exception - the transaction is invalid
ScriptSyntax - script syntax is invalid