File Coverage

blib/lib/Bitcoin/Crypto/Script/Recognition.pm
Criterion Covered Total %
statement 76 77 98.7
branch 29 34 85.2
condition 4 6 66.6
subroutine 14 14 100.0
pod 0 3 0.0
total 123 134 91.7


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Script::Recognition;
2             $Bitcoin::Crypto::Script::Recognition::VERSION = '2.000_01'; # TRIAL
3             $Bitcoin::Crypto::Script::Recognition::VERSION = '2.00001';
4 29     29   491 use v5.10;
  29         117  
5 29     29   179 use strict;
  29         77  
  29         638  
6 29     29   156 use warnings;
  29         66  
  29         877  
7              
8 29     29   197 use Moo;
  29         80  
  29         207  
9 29     29   12944 use Mooish::AttributeBuilder -standard;
  29         81  
  29         271  
10 29     29   3743 use Bitcoin::Crypto::Types qw(InstanceOf);
  29         95  
  29         509  
11 29     29   75122 use Try::Tiny;
  29         97  
  29         1894  
12              
13 29     29   212 use Bitcoin::Crypto::Script::Opcode;
  29         83  
  29         27250  
14              
15             has param 'script' => (
16             isa => InstanceOf ['Bitcoin::Crypto::Script'],
17             );
18              
19             has field '_script_serialized' => (
20             lazy => sub { $_[0]->script->to_serialized },
21             );
22              
23             has field 'type' => (
24             predicate => 1,
25             writer => 1,
26             );
27              
28             has field 'address' => (
29             predicate => 1,
30             writer => 1,
31             clearer => 1,
32             );
33              
34             has field '_blueprints' => (
35             builder => 1,
36             );
37              
38             sub _build_blueprints
39             {
40             # blueprints for standard transaction types
41             return [
42             [
43 183     183   25038 P2PK => [
44             ['data', 33, 65],
45             'OP_CHECKSIG',
46             ]
47             ],
48              
49             [
50             P2PKH => [
51             'OP_DUP',
52             'OP_HASH160',
53             ['address', 20],
54             'OP_EQUALVERIFY',
55             'OP_CHECKSIG',
56             ]
57             ],
58              
59             [
60             P2SH => [
61             'OP_HASH160',
62             ['address', 20],
63             'OP_EQUAL',
64             ]
65             ],
66              
67             [
68             P2MS => [
69             ['op_n', 1 .. 15],
70             ['data_repeated', 33, 65],
71             ['op_n', 1 .. 15],
72             'OP_CHECKMULTISIG',
73             ]
74             ],
75              
76             [
77             P2WPKH => [
78             'OP_0',
79             ['address', 20],
80             ],
81             ],
82              
83             [
84             P2WSH => [
85             'OP_0',
86             ['address', 32],
87             ]
88             ],
89              
90             [
91             NULLDATA => [
92             'OP_RETURN',
93             ['address', 1 .. 75],
94             ]
95             ],
96              
97             [
98             NULLDATA => [
99             'OP_RETURN',
100             'OP_PUSHDATA1',
101             ['address', 76 .. 80],
102             ]
103             ],
104             ];
105             }
106              
107             sub _check_blueprint
108             {
109 1424     1424   3071 my ($self, $pos, $part, @more_parts) = @_;
110 1424         21777 my $this_script = $self->_script_serialized;
111              
112 1424 100       10287 return $pos == length $this_script
113             unless defined $part;
114 1253 50       2503 return !!0 unless $pos < length $this_script;
115              
116 1253 100       2600 if (!ref $part) {
117 691         1831 my $opcode = Bitcoin::Crypto::Script::Opcode->get_opcode_by_name($part);
118 691 100       3173 return !!0 unless $opcode->code eq substr $this_script, $pos, 1;
119 314         1030 return $self->_check_blueprint($pos + 1, @more_parts);
120             }
121             else {
122 562         1448 my ($kind, @vars) = @$part;
123              
124 562 100 100     2333 if ($kind eq 'address' || $kind eq 'data') {
    100          
    50          
125 401         822 my $len = ord substr $this_script, $pos, 1;
126              
127 401 100       721 return !!0 unless grep { $_ == $len } @vars;
  1484         3476  
128 161 100       658 if ($self->_check_blueprint($pos + $len + 1, @more_parts)) {
129 158 100       909 $self->set_address(substr $this_script, $pos + 1, $len)
130             if $kind eq 'address';
131 158         752 return !!1;
132             }
133             }
134             elsif ($kind eq 'data_repeated') {
135 10         26 my $count = 0;
136 10         21 while (1) {
137 43         76 my $len = ord substr $this_script, $pos, 1;
138 43 100       66 last unless grep { $_ == $len } @vars;
  86         181  
139              
140 33         263 $pos += $len + 1;
141 33         49 $count += 1;
142             }
143              
144 10 50 33     277 return !!0 if $count == 0 || $count > 16;
145 10         59 my $opcode = Bitcoin::Crypto::Script::Opcode->get_opcode_by_name("OP_$count");
146 10 50       64 return !!0 unless $opcode->code eq substr $this_script, $pos, 1;
147 10         41 return $self->_check_blueprint($pos, @more_parts);
148             }
149             elsif ($kind eq 'op_n') {
150 151         266 my $opcode;
151             try {
152 151     32   7270 $opcode = Bitcoin::Crypto::Script::Opcode->get_opcode_by_code(substr $this_script, $pos, 1);
153 151         1051 };
154              
155 151 50       2174 return !!0 unless $opcode;
156 151 100       985 return !!0 unless $opcode->name =~ /\AOP_(\d+)\z/;
157 124 100       279 return !!0 unless grep { $_ == $1 } @vars;
  1860         3933  
158 20         107 return $self->_check_blueprint($pos + 1, @more_parts);
159             }
160             else {
161 0         0 die "invalid blueprint kind: $kind";
162             }
163             }
164             }
165              
166             sub check
167             {
168 183     183 0 340 my ($self) = @_;
169 183         284 foreach my $variant (@{$self->_blueprints}) {
  183         626  
170 919         1481 my ($type, $blueprint) = @{$variant};
  919         1721  
171              
172             # clear address if it was set by previous check
173 919         15652 $self->clear_address;
174 919 100       4338 if ($self->_check_blueprint(0, @{$blueprint})) {
  919         2006  
175 168         477 $self->set_type($type);
176 168         691 last;
177             }
178             }
179              
180 183         371 return;
181             }
182              
183             sub get_type
184             {
185 173     173 0 410 my ($self) = @_;
186              
187 173         525 $self->check;
188 173         4608 return $self->type;
189             }
190              
191             sub get_address
192             {
193 10     10 0 20 my ($self) = @_;
194              
195 10         21 $self->check;
196 10         188 return $self->address;
197             }
198              
199             1;
200