File Coverage

blib/lib/Bitcoin/Crypto/Script/Runner.pm
Criterion Covered Total %
statement 74 74 100.0
branch 20 20 100.0
condition 3 5 60.0
subroutine 18 18 100.0
pod 4 4 100.0
total 119 121 98.3


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Script::Runner;
2             $Bitcoin::Crypto::Script::Runner::VERSION = '2.000_01'; # TRIAL
3             $Bitcoin::Crypto::Script::Runner::VERSION = '2.00001';
4 29     29   447382 use v5.10;
  29         188  
5 29     29   192 use strict;
  29         93  
  29         633  
6 29     29   142 use warnings;
  29         201  
  29         875  
7 29     29   4653 use Moo;
  29         57580  
  29         206  
8 29     29   25840 use Mooish::AttributeBuilder -standard;
  29         15269  
  29         238  
9 29     29   9225 use Type::Params -sigs;
  29         1027323  
  29         383  
10              
11 29     29   16650 use Scalar::Util qw(blessed);
  29         78  
  29         1954  
12              
13 29     29   3828 use Bitcoin::Crypto::Types qw(ArrayRef Str ByteStr Object InstanceOf PositiveOrZeroInt);
  29         110  
  29         248  
14 29     29   121271 use Bitcoin::Crypto::Exception;
  29         84  
  29         851  
15 29     29   193 use Bitcoin::Crypto::Helpers qw(pad_hex);
  29         101  
  29         1541  
16 29     29   12308 use Bitcoin::Crypto::Script::Transaction;
  29         276  
  29         1030  
17              
18 29     29   265 use namespace::clean;
  29         70  
  29         212  
19              
20             has option 'transaction' => (
21             coerce => (InstanceOf ['Bitcoin::Crypto::Script::Transaction'])
22             ->plus_coercions(
23             InstanceOf ['Bitcoin::Crypto::Transaction'],
24             q{Bitcoin::Crypto::Script::Transaction->new(transaction => $_)}
25             ),
26             writer => 1,
27             clearer => 1,
28             );
29              
30             has field 'stack' => (
31             isa => ArrayRef [Str],
32             writer => -hidden,
33             );
34              
35             has field 'alt_stack' => (
36             isa => ArrayRef [Str],
37             writer => -hidden,
38             );
39              
40             has field 'pos' => (
41             isa => PositiveOrZeroInt,
42             writer => -hidden,
43             );
44              
45             has field 'operations' => (
46             isa => ArrayRef [ArrayRef],
47             writer => -hidden,
48             );
49              
50             has field '_codeseparator' => (
51             isa => PositiveOrZeroInt,
52             writer => 1,
53             );
54              
55             sub to_int
56             {
57 150     150 1 754 my ($self, $bytes) = @_;
58              
59 150 100       367 return 0 if !length $bytes;
60              
61 133         208 my $negative = !!0;
62 133         233 my $last = substr $bytes, -1, 1;
63 133         228 my $ord = ord $last;
64 133 100       274 if ($ord >= 0x80) {
65 13         19 $negative = !!1;
66 13         30 substr $bytes, -1, 1, chr($ord - 0x80);
67             }
68              
69 133         484 my $value = Math::BigInt->from_bytes(scalar reverse $bytes);
70 133 100       11701 $value->bneg if $negative;
71              
72 133         798 return $value;
73             }
74              
75             sub from_int
76             {
77 161     161 1 2681 my ($self, $value) = @_;
78              
79 161 100       490 if (!blessed $value) {
80 144         494 $value = Math::BigInt->new($value);
81             }
82              
83 161         9367 my $negative = $value < 0;
84 161 100       26861 $value->babs if $negative;
85              
86 161         737 my $bytes = reverse pack 'H*', pad_hex($value->to_hex);
87              
88 161         389 my $last = substr $bytes, -1, 1;
89 161         290 my $ord = ord $last;
90 161 100       498 if ($ord >= 0x80) {
    100          
91 7 100       18 if ($negative) {
92 1         3 $bytes .= "\x80";
93             }
94             else {
95 6         12 $bytes .= "\x00";
96             }
97             }
98             elsif ($negative) {
99 3         9 substr $bytes, -1, 1, chr($ord + 0x80);
100             }
101              
102 161         1174 return $bytes;
103             }
104              
105             sub to_bool
106             {
107 122     122 1 291 my ($self, $bytes) = @_;
108              
109 122         233 my $len = length $bytes;
110 122 100       320 return !!0 if $len == 0;
111              
112 115         324 my $substr = "\x00" x ($len - 1);
113 115   66     817 return $bytes ne $substr . "\x00"
114             && $bytes ne $substr . "\x80";
115             }
116              
117             sub from_bool
118             {
119 140     140 1 3245 my ($self, $value) = @_;
120              
121 140 100       988 return !!$value ? "\x01" : "\x00";
122             }
123              
124             sub _advance
125             {
126 798     798   1522 my ($self, $count) = @_;
127 798   50     3396 $count //= 1;
128              
129 798         17430 $self->_set_pos($self->pos + $count);
130 798         18646 return;
131             }
132              
133             sub _register_codeseparator
134             {
135 281     281   575 my ($self) = @_;
136              
137 281         4782 $self->_set_codeseparator($self->pos);
138 281         6634 return;
139             }
140              
141             signature_for stack_serialized => (
142             method => Object,
143             positional => [],
144             );
145              
146             sub stack_serialized
147             {
148             my ($self) = @_;
149              
150             return join '',
151             map { length $_ == 0 ? "\x00" : $_ }
152             @{$self->stack};
153             }
154              
155             signature_for execute => (
156             method => Object,
157             positional => [InstanceOf ['Bitcoin::Crypto::Script'], ArrayRef [ByteStr], {default => []}],
158             );
159              
160             sub execute
161             {
162             my ($self, $script, $initial_stack) = @_;
163              
164             $self->start($script, $initial_stack);
165             1 while $self->step;
166              
167             return $self;
168             }
169              
170             signature_for start => (
171             method => Object,
172             positional => [InstanceOf ['Bitcoin::Crypto::Script'], ArrayRef [ByteStr], {default => []}],
173             );
174              
175             sub start
176             {
177             my ($self, $script, $initial_stack) = @_;
178              
179             $self->_set_stack($initial_stack);
180             $self->_set_alt_stack([]);
181             $self->_set_pos(0);
182             $self->_register_codeseparator;
183             $self->_set_operations($script->operations);
184              
185             return $self;
186             }
187              
188             signature_for step => (
189             method => Object,
190             positional => [],
191             );
192              
193             sub step
194             {
195             my ($self) = @_;
196              
197             my $pos = $self->pos;
198              
199             return !!0
200             unless defined $pos;
201              
202             return !!0
203             unless $pos < @{$self->operations};
204              
205             my ($op, $raw_op, @args) = @{$self->operations->[$pos]};
206              
207             Bitcoin::Crypto::Exception::Transaction->raise(
208             'no transaction is set for the script runner'
209             ) if $op->needs_transaction && !$self->has_transaction;
210              
211             Bitcoin::Crypto::Exception::ScriptRuntime->trap_into(
212             sub {
213             $op->execute($self, @args);
214             },
215             "error at pos $pos (" . $op->name . ")"
216             );
217              
218             $self->_advance;
219             return !!1;
220             }
221              
222             signature_for subscript => (
223             method => Object,
224             positional => [],
225             );
226              
227             sub subscript
228             {
229             my ($self) = @_;
230             my $start = $self->_codeseparator;
231             my @operations = @{$self->operations};
232              
233             my $result = '';
234             foreach my $operation (@operations[$start .. $#operations]) {
235             my ($op, $raw_op) = @$operation;
236             next if $op->name eq 'OP_CODESEPARATOR';
237             $result .= $raw_op;
238             }
239              
240             # NOTE: signature is not removed from the subscript, since runner doesn't know what it is
241              
242             return $result;
243             }
244              
245             signature_for success => (
246             method => Object,
247             positional => [],
248             );
249              
250             sub success
251             {
252             my ($self) = @_;
253              
254             my $stack = $self->stack;
255              
256             return !!0 if !$stack;
257             return !!0 if !$stack->[-1];
258             return !!0 if !$self->to_bool($stack->[-1]);
259             return !!1;
260             }
261              
262             1;
263              
264             __END__
265              
266             =head1 NAME
267              
268             Bitcoin::Crypto::Script::Runner - Bitcoin script runner
269              
270             =head1 SYNOPSIS
271              
272             use Bitcoin::Crypto::Script::Runner;
273             use Data::Dumper;
274              
275             my $runner = Bitcoin::Crypto::Script::Runner->new;
276              
277             # provide an instance of Bitcoin::Crypto::Script
278             # runs the script all at once
279             $runner->execute($script);
280              
281             # ... or: runs the script step by step
282             $runner->start($script);
283             while ($runner->step) {
284             print 'runner step, stack: ';
285             print Dumper($runner->stack);
286             }
287              
288             print 'FAILURE' unless $runner->success;
289             print 'resulting stack: ';
290             print Dumper($runner->stack);
291              
292             =head1 DESCRIPTION
293              
294             This class instances can be used to execute Bitcoin scripts defined as
295             instances of L<Bitcoin::Crypto::Script>. Scripts can be executed in one go or
296             step by step, and the execution stack is available through an accessor.
297              
298             One runner can be used to execute scripts multiple times. Each time you call
299             C<execute> or C<start>, the runner state is reset. Initial stack state can be
300             provided to either one of those methods. This provides better control over
301             execution than L<Bitcoin::Crypto::Script/run>, which simply executes the script
302             and returns its stack.
303              
304             =head1 INTERFACE
305              
306             =head2 Attributes
307              
308             =head3 transaction
309              
310             Instance of L<Bitcoin::Crypto::Transaction>. It is optional, but some opcodes
311             will refuse to function without it.
312              
313             I<predicate:> C<has_transaction>
314              
315             I<writer:> C<set_transaction>
316              
317             =head3 stack
318              
319             B<Not assignable in the constructor>
320              
321             Array reference - the stack which is used during script execution. Last item in
322             this array is the stack top. Use C<< $runner->stack->[-1] >> to examine the stack top.
323              
324             Each item on the stack is a byte string. Use L</to_int> and L</to_bool> to
325             transform it into an integer or boolean value in the same fashion bitcoin
326             script interpreter does it.
327              
328             =head3 alt_stack
329              
330             B<Not assignable in the constructor>
331              
332             Array reference - alt stack, used by C<OP_TOALTSTACK> and C<OP_FROMALTSTACK>.
333              
334             =head3 operations
335              
336             B<Not assignable in the constructor>
337              
338             Array reference - An array of operations to be executed. Same as
339             L<Bitcoin::Crypto::Script/operations> and automatically obtained by calling it.
340              
341             =head3 pos
342              
343             B<Not assignable in the constructor>
344              
345             Positive integer - the position of the operation to be run in the next step
346             (from L</operations>).
347              
348             =head2 Methods
349              
350             =head3 new
351              
352             $object = $class->new(%data)
353              
354             This is a standard Moo constructor, which can be used to create the object. It
355             takes arguments specified in L</Attributes>.
356              
357             Returns class instance.
358              
359             =head3 execute
360              
361             $object = $object->execute($script, \@initial_stack = [])
362              
363             Executes the script in one go. Returns runner instance (for chaining).
364              
365             C<$script> must be an instance of L<Bitcoin::Crypto::Script>. If you only have
366             a serialized script in a string, call
367             L<Bitcoin::Crypto::Script/from_serialized> first to get a proper script
368             instance. C<$initial_stack> will be used to pre-populate the stack before
369             running the script.
370              
371             After the method returns call L</stack> to get execution stack. This can be
372             done in a single line:
373              
374             my $stack = $runner->execute($script)->stack;
375              
376             If errors occur, they will be thrown as exceptions. See L</EXCEPTIONS>.
377              
378             =head3 start
379              
380             $object = $object->start($script, \@initial_stack = [])
381              
382             Same as L</execute>, but only sets initial runner state and does not actually
383             execute any script opcodes. L</step> must be called to continue the execution.
384              
385             =head3 step
386              
387             while ($runner->step) {
388             # do something after each step
389             }
390              
391             Executes the next script opcode. Returns a false value if the script finished
392             the execution, and a true value otherwise.
393              
394             L</start> must be called before this method is called.
395              
396             Note that not every opcode will take a step to execute. This means that this script:
397              
398             OP_1 OP_IF OP_PUSHDATA1 1 0x1f OP_ENDIF
399              
400             will take four steps to execute (C<OP_1> -> C<OP_IF> -> C<0x1f> -> C<OP_ENDIF>).
401              
402             This one however:
403              
404             OP_1 OP_IF OP_PUSHDATA1 1 0x1f OP_ELSE OP_PUSHDATA1 2 0xe15e OP_ENDIF
405              
406             will also take four steps (C<OP_1> -> C<OP_IF> -> C<0x1f> -> C<OP_ELSE>).
407             This happens because C<OP_ELSE> performs a jump past C<OP_ENDIF>.
408             If the initial op was C<OP_0>, the execution would be C<OP_0> -> C<OP_IF> ->
409             C<0xe15e> -> C<OP_ENDIF>. No C<OP_ELSE> since it was jumped over and reaching
410             C<OP_ENDIF>.
411              
412             These details should not matter usually, but may be confusing if you would
413             want to for example print your stack step by step. When in doubt, check C<<
414             $runner->pos >>, which contains the position of the B<next> opcode to execute.
415              
416             =head3 subscript
417              
418             $subscript = $object->subscript()
419              
420             Returns current subscript - part of the running script from after the last
421             codeseparator, with all other codeseparators removed.
422              
423             =head3 success
424              
425             $boolean = $object->success()
426              
427             Returns a boolean indicating whether the script execution was successful.
428              
429             =head2 Helper methods
430              
431             =head3 to_int, from_int
432              
433             my $int = $runner->to_int($byte_vector);
434             my $byte_vector = $runner->from_int($int);
435              
436             These methods encode and decode numbers in format which is used on L</stack>.
437              
438             BigInts are used. C<to_int> will return an instance of L<Math::BigInt>, while
439             C<from_int> can accept it (but it should also handle regular numbers just
440             fine).
441              
442             =head3 to_bool, from_bool
443              
444             These methods encode and decode booleans in format which is used on L</stack>.
445              
446             =head3 stack_serialized
447              
448             Returns the serialized stack. Any null vectors will be transformed to C<0x00>.
449              
450             =head1 CAVEATS
451              
452             There is curretly no limit on the size of byte vector which is going to be
453             transformed to an integer for ops like OP_ADD. BigInts are used for all integers.
454              
455             =head1 EXCEPTIONS
456              
457             This module throws an instance of L<Bitcoin::Crypto::Exception> if it
458             encounters an error. It can produce the following error types from the
459             L<Bitcoin::Crypto::Exception> namespace:
460              
461             =over
462              
463             =item * ScriptRuntime - script has encountered a runtime exception - the transaction is invalid
464              
465             =item * ScriptSyntax - script syntax is invalid
466              
467             =back
468              
469             =head1 SEE ALSO
470              
471             L<Bitcoin::Crypto::Script>
472