File Coverage

blib/lib/Bitcoin/Crypto/Script.pm
Criterion Covered Total %
statement 84 88 95.4
branch 11 22 50.0
condition 5 9 55.5
subroutine 24 24 100.0
pod 8 9 88.8
total 132 152 86.8


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Script;
2             $Bitcoin::Crypto::Script::VERSION = '1.008_01'; # TRIAL
3             $Bitcoin::Crypto::Script::VERSION = '1.00801';
4 11     11   73769 use v5.10;
  11         51  
5 11     11   63 use strict;
  11         31  
  11         245  
6 11     11   64 use warnings;
  11         25  
  11         381  
7 11     11   696 use Moo;
  11         7743  
  11         58  
8 11     11   7308 use Crypt::Digest::SHA256 qw(sha256);
  11         6263  
  11         803  
9 11     11   1559 use Mooish::AttributeBuilder -standard;
  11         5905  
  11         83  
10              
11 11     11   5100 use Bitcoin::Crypto::Base58 qw(encode_base58check);
  11         32  
  11         672  
12 11     11   5319 use Bitcoin::Crypto::Bech32 qw(encode_segwit);
  11         34  
  11         705  
13 11     11   86 use Bitcoin::Crypto::Config;
  11         28  
  11         310  
14 11     11   64 use Bitcoin::Crypto::Helpers qw(hash160 hash256 verify_bytestring);
  11         31  
  11         587  
15 11     11   74 use Bitcoin::Crypto::Exception;
  11         28  
  11         280  
16 11     11   73 use Bitcoin::Crypto::Types qw(ArrayRef Str);
  11         38  
  11         72  
17              
18 11     11   27931 use namespace::clean;
  11         28  
  11         120  
19              
20             has field 'operations' => (
21             isa => ArrayRef [Str],
22             default => sub { [] },
23             );
24              
25             with qw(Bitcoin::Crypto::Role::Network);
26              
27             # list of significant opcodes from DATA section
28             our %op_codes = do {
29             my @list;
30             while (my $line = <DATA>) {
31             chomp $line;
32             last if $line eq '__END__';
33              
34             my @parts = split /\s+/, $line;
35             next if @parts == 0;
36             die 'too many DATA parts for script opcode'
37             if @parts > 2;
38              
39             # add key
40             push @list, shift @parts;
41              
42             # rest of @parts are values
43             my ($code) = @parts;
44             push @list, {
45             code => pack('C', hex $code),
46             };
47             }
48              
49             close DATA;
50             @list;
51             };
52              
53             sub _get_op_code
54             {
55 42     42   88 my ($context, $op_code) = @_;
56 42 100 33     287 if ($op_code =~ /^OP_(.+)/) {
    50 33        
57 24         62 $op_code = $1;
58 24         106 return $op_codes{$op_code}{code};
59             }
60             elsif ($op_code =~ /^[0-9]+$/ && $op_code >= 1 && $op_code <= 75) {
61              
62             # standard data push - 0x01 up to 0x4b
63 18         95 return pack('C', 0x00 + $op_code);
64             }
65             else {
66 0 0       0 Bitcoin::Crypto::Exception::ScriptOpcode->raise(
67             defined $op_code ? "unknown opcode $op_code" : 'undefined opcode variable'
68             );
69             }
70             }
71              
72             sub add_raw
73             {
74 62     62 1 118 my ($self, $bytes) = @_;
75 62         199 verify_bytestring($bytes);
76              
77 62         111 push @{$self->operations}, $bytes;
  62         180  
78 62         108 return $self;
79             }
80              
81             sub add_operation
82             {
83 42     42 1 162 my ($self, $op_code) = @_;
84 42         113 my $val = $self->_get_op_code($op_code);
85 42         129 $self->add_raw($val);
86 42         143 return $self;
87             }
88              
89             sub push_bytes
90             {
91 20     20 1 311 my ($self, $bytes) = @_;
92 20         72 verify_bytestring($bytes);
93              
94 20         63 my $len = length $bytes;
95 20 50       61 Bitcoin::Crypto::Exception::ScriptPush->raise(
96             'empty data variable'
97             ) unless $len;
98              
99 20 100 100     135 if ($bytes =~ /[\x00-\x10]/ && $len == 1) {
100 1         4 my $num = unpack 'C', $bytes;
101 1         6 $self->add_operation("OP_$num");
102             }
103             else {
104 11     11   18241 use bigint;
  11         39526  
  11         68  
105 19 100       241 if ($len <= 75) {
    50          
    0          
    0          
106 18         2239 $self->add_operation($len);
107             }
108             elsif ($len < (2 << 7)) {
109 1         238 $self->add_operation('OP_PUSHDATA1')
110             ->add_raw(pack 'C', $len);
111             }
112             elsif ($len < (2 << 15)) {
113 0         0 $self->add_operation('OP_PUSHDATA2')
114             ->add_raw(pack 'S', $len);
115             }
116             elsif ($len < (2 << 31)) {
117 0         0 $self->add_operation('OP_PUSHDATA4')
118             ->add_raw(pack 'L', $len);
119             }
120             else {
121 0         0 Bitcoin::Crypto::Exception::ScriptPush->raise(
122             'too much data to push onto stack in one operation'
123             );
124             }
125 19         144 $self->add_raw($bytes);
126             }
127 20         53 return $self;
128             }
129              
130             sub get_script
131             {
132 31     31 1 95 my ($self) = @_;
133 31         57 return join '', @{$self->operations};
  31         179  
134             }
135              
136             sub get_script_hash
137             {
138 19     19 1 55 my ($self) = @_;
139 19         58 return hash160($self->get_script);
140             }
141              
142             sub witness_program
143             {
144 4     4 0 8 my ($self) = @_;
145              
146 4         12 return pack('C', Bitcoin::Crypto::Config::witness_version) . sha256($self->get_script);
147             }
148              
149             sub get_legacy_address
150             {
151 19     19 1 56 my ($self) = @_;
152 19         107 return encode_base58check($self->network->p2sh_byte . $self->get_script_hash);
153             }
154              
155             sub get_compat_address
156             {
157 4     4 1 2695 my ($self) = @_;
158              
159             # network field is not required, lazy check for completeness
160 4 50       23 Bitcoin::Crypto::Exception::NetworkConfig->raise(
161             'this network does not support segregated witness'
162             ) unless $self->network->supports_segwit;
163              
164 4         104 my $program = Bitcoin::Crypto::Script->new(network => $self->network);
165 4         86 $program->add_operation('OP_' . Bitcoin::Crypto::Config::witness_version)
166             ->push_bytes(sha256($self->get_script));
167 4         12 return $program->get_legacy_address;
168             }
169              
170             sub get_segwit_address
171             {
172 4     4 1 2733 my ($self) = @_;
173              
174             # network field is not required, lazy check for completeness
175 4 50       21 Bitcoin::Crypto::Exception::NetworkConfig->raise(
176             'this network does not support segregated witness'
177             ) unless $self->network->supports_segwit;
178              
179 4         17 return encode_segwit($self->network->segwit_hrp, $self->witness_program);
180             }
181              
182             1;
183              
184             __DATA__
185              
186             0 00
187             FALSE 00
188             PUSHDATA1 4c
189             PUSHDATA2 4d
190             PUSHDATA4 4e
191             1NEGATE 4f
192             RESERVED 50
193             TRUE 51
194             1 51
195             2 52
196             3 53
197             4 54
198             5 55
199             6 56
200             7 57
201             8 58
202             9 59
203             10 5a
204             11 5b
205             12 5c
206             13 5d
207             14 5e
208             15 5f
209             16 60
210             NOP 61
211             VER 62
212             IF 63
213             NOTIF 64
214             VERIF 65
215             VERNOTIF 66
216             ELSE 67
217             ENDIF 68
218             VERIFY 69
219             RETURN 6a
220             TOALTSTACK 6b
221             FROMALTSTACK 6c
222             2DROP 6d
223             2DUP 6e
224             3DUP 6f
225             2OVER 70
226             2ROT 71
227             2SWAP 72
228             IFDUP 73
229             DEPTH 74
230             DROP 75
231             DUP 76
232             NIP 77
233             OVER 78
234             PICK 79
235             ROLL 7a
236             ROT 7b
237             SWAP 7c
238             TUCK 7d
239             SIZE 82
240             EQUAL 87
241             EQUALVERIFY 88
242             RESERVED1 89
243             RESERVED2 8a
244             1ADD 8b
245             1SUB 8c
246             NEGATE 8f
247             ABS 90
248             NOT 91
249             ONOTEQUAL 92
250             ADD 93
251             SUB 94
252             BOOLAND 9a
253             BOOLOR 9b
254             NUMEQUAL 9c
255             NUMEQUALVERIFY 9d
256             NUMNOTEQUAL 9e
257             LESSTHAN 9f
258             GREATERTHAN a0
259             LESSTHANOREQUAL a1
260             GREATERTHANOREQUAL a2
261             MIN a3
262             MAX a4
263             WITHIN a5
264             RIPEMD160 a6
265             SHA1 a7
266             SHA256 a8
267             HASH160 a9
268             HASH256 aa
269             CODESEPARATOR ab
270             CHECKSIG ac
271             CHECKSIGVERIFY ad
272             CHECKMULTISIG ae
273             CHECKMULTISIGVERIFY af
274             CHECKLOCKTIMEVERFIY b1
275             CHECKSEQUENCEVERIFY b2
276              
277             __END__
278             =head1 NAME
279              
280             Bitcoin::Crypto::Script - Bitcoin script representations
281              
282             =head1 SYNOPSIS
283              
284             use Bitcoin::Crypto::Script;
285              
286             my $script = Bitcoin::Crypto::Script->new
287             ->add_operation('OP_1')
288             ->add_operation('OP_TRUE')
289             ->add_operation('OP_EQUAL');
290              
291             # getting serialized script
292             my $serialized = $script->get_script();
293              
294             # getting address from script (p2wsh)
295             my $address = $script->get_segwit_adress();
296              
297             =head1 DESCRIPTION
298              
299             This class allows you to create a bitcoin script representations
300              
301             You can use a script object to:
302              
303             =over 2
304              
305             =item * create a script from opcodes
306              
307             =item * serialize script into byte string
308              
309             =item * create legacy (p2sh), compat (p2sh(p2wsh)) and segwit (p2wsh) adresses
310              
311             =back
312              
313             =head1 METHODS
314              
315             =head2 new
316              
317             $script_object = $class->new()
318              
319             A constructor. Returns new script instance.
320              
321             =head2 add_operation
322              
323             $script_object = $object->add_operation($opcode)
324              
325             Adds a new opcode at the end of a script. Returns the object instance for chaining.
326              
327             Throws an exception for unknown opcodes.
328              
329             =head2 add_raw
330              
331             $script_object = $object->add_raw($bytes)
332              
333             Adds C<$bytes> at the end of a script.
334             Useful when you need a value in a script that shouldn't be pushed to the execution stack, like the first four bytes after C<PUSHDATA4>.
335              
336             Returns the object instance for chaining.
337              
338             =head2 push_bytes
339              
340             $script_object = $object->push_bytes($bytes)
341              
342             Pushes C<$bytes> to the execution stack at the end of a script, using a minimal push opcode.
343              
344             For example, running C<$script->push_bytes("\x03")> will have the same effect as C<$script->add_operation('OP_3')>.
345              
346             Throws an exception for data exceeding a 4 byte number in length.
347              
348             Returns the object instance for chaining.
349              
350             =head2 get_script
351              
352             $bytestring = $object->get_script()
353              
354             Returns a serialized script as byte string.
355              
356             =head2 get_script_hash
357              
358             $bytestring = $object->get_script_hash()
359              
360             Returns a serialized script parsed with C<HASH160> (ripemd160 of sha256).
361              
362             =head2 set_network
363              
364             $script_object = $object->set_network($val)
365              
366             Change key's network state to C<$val>. It can be either network name present in L<Bitcoin::Crypto::Network> package or an instance of this class.
367              
368             Returns current object instance.
369              
370             =head2 get_legacy_address
371              
372             $address = $object->get_legacy_address()
373              
374             Returns string containing Base58Check encoded script hash (p2sh address)
375              
376             =head2 get_compat_address
377              
378             $address = $object->get_compat_address()
379              
380             Returns string containing Base58Check encoded script hash containing a witness program for compatibility purposes (p2sh(p2wsh) address)
381              
382             =head2 get_segwit_address
383              
384             $address = $object->get_segwit_address()
385              
386             Returns string containing Bech32 encoded witness program (p2wsh address)
387              
388             =head1 EXCEPTIONS
389              
390             This module throws an instance of L<Bitcoin::Crypto::Exception> if it encounters an error. It can produce the following error types from the L<Bitcoin::Crypto::Exception> namespace:
391              
392             =over 2
393              
394             =item * ScriptOpcode - unknown opcode was specified
395              
396             =item * ScriptPush - data pushed to the execution stack is invalid
397              
398             =item * NetworkConfig - incomplete or corrupted network configuration
399              
400             =back
401              
402             =head1 SEE ALSO
403              
404             =over 2
405              
406             =item L<Bitcoin::Crypto::Key::Private>
407              
408             =item L<Bitcoin::Crypto::Network>
409              
410             =back
411              
412             =cut
413