File Coverage

blib/lib/Bitcoin/Crypto/Transaction/Digest.pm
Criterion Covered Total %
statement 117 117 100.0
branch 28 30 93.3
condition 10 11 90.9
subroutine 13 13 100.0
pod 0 1 0.0
total 168 172 97.6


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Transaction::Digest;
2             $Bitcoin::Crypto::Transaction::Digest::VERSION = '2.000_01'; # TRIAL
3             $Bitcoin::Crypto::Transaction::Digest::VERSION = '2.00001';
4 9     9   132 use v5.10;
  9         62  
5 9     9   86 use strict;
  9         35  
  9         220  
6 9     9   49 use warnings;
  9         31  
  9         265  
7              
8 9     9   111 use Moo;
  9         34  
  9         81  
9 9     9   4676 use Mooish::AttributeBuilder -standard;
  9         25  
  9         73  
10              
11 9     9   1264 use Bitcoin::Crypto::Helpers qw(pack_varint);
  9         44  
  9         569  
12 9     9   77 use Bitcoin::Crypto::Util qw(hash256);
  9         24  
  9         480  
13 9     9   64 use Bitcoin::Crypto::Exception;
  9         42  
  9         256  
14 9     9   56 use Bitcoin::Crypto::Constants;
  9         26  
  9         403  
15 9     9   82 use Bitcoin::Crypto::Types qw(InstanceOf ByteStr PositiveOrZeroInt PositiveOrZeroInt);
  9         50  
  9         91  
16              
17             has param 'transaction' => (
18             isa => InstanceOf ['Bitcoin::Crypto::Transaction'],
19             );
20              
21             has param 'signing_index' => (
22             isa => PositiveOrZeroInt,
23             );
24              
25             has option 'signing_subscript' => (
26             coerce => ByteStr,
27             );
28              
29             has param 'sighash' => (
30             isa => PositiveOrZeroInt,
31             default => Bitcoin::Crypto::Constants::sighash_all,
32             );
33              
34             sub get_digest
35             {
36 81     81 0 213 my ($self) = @_;
37 81         284 my $sign_no = $self->signing_index;
38 81         287 my $input = $self->transaction->inputs->[$sign_no];
39              
40 81 50       302 Bitcoin::Crypto::Exception::Transaction->raise(
41             "can't find input with index $sign_no"
42             ) if !$input;
43              
44 81         175 my $procedure = '_get_digest_default';
45 81 100       304 $procedure = '_get_digest_segwit'
46             if $input->is_segwit;
47              
48 81   50     705 my $sighash_type = $self->sighash & 31 || Bitcoin::Crypto::Constants::sighash_all;
49 81         202 my $anyonecanpay = $self->sighash & Bitcoin::Crypto::Constants::sighash_anyonecanpay;
50              
51 81         357 return $self->$procedure($sighash_type, $anyonecanpay);
52             }
53              
54             sub _get_digest_default
55             {
56 35     35   108 my ($self, $sighash_type, $anyonecanpay) = @_;
57 35         89 my $transaction = $self->transaction;
58 35         146 my $tx_copy = $transaction->clone;
59              
60 35         93 @{$tx_copy->inputs} = ();
  35         113  
61 35         84 foreach my $input (@{$transaction->inputs}) {
  35         100  
62 53         165 my $input_copy = $input->clone;
63              
64 53         1222 $input_copy->set_signature_script('');
65 53         1664 $tx_copy->add_input($input_copy);
66             }
67              
68 35         144 my $this_input = $tx_copy->inputs->[$self->signing_index];
69 35 100       138 if ($self->signing_subscript) {
70 27         576 $this_input->set_signature_script($self->signing_subscript);
71             }
72             else {
73 8 50       54 Bitcoin::Crypto::Exception::Transaction->raise(
74             "can't guess the subscript from a non-standard transaction"
75             ) unless $this_input->utxo->output->is_standard;
76              
77 8         129 $this_input->set_signature_script($this_input->script_base->to_serialized);
78             }
79              
80             # Handle sighashes
81 35 100       1088 if ($sighash_type == Bitcoin::Crypto::Constants::sighash_none) {
    100          
82 3         14 @{$tx_copy->outputs} = ();
  3         14  
83 3         8 foreach my $input (@{$tx_copy->inputs}) {
  3         18  
84 5 100       93 $input->set_sequence_no(0)
85             unless $input == $this_input;
86             }
87             }
88             elsif ($sighash_type == Bitcoin::Crypto::Constants::sighash_single) {
89 3 100       26 if ($self->signing_index >= @{$transaction->outputs}) {
  3         30  
90              
91             # TODO: this should verify with digest 0000..0001 (without hashed)
92 1         21 Bitcoin::Crypto::Exception::Transaction->raise(
93             'illegal input ' . $self->signing_index . ' in SIGHASH_SINGLE'
94             );
95             }
96              
97 2         11 @{$tx_copy->outputs} = ();
  2         11  
98 2         22 my @wanted_outputs = @{$transaction->outputs}[0 .. $self->signing_index - 1];
  2         9  
99 2         7 foreach my $output (@wanted_outputs) {
100 1         4 my $output_copy = $output->clone;
101 1         66 $output_copy->set_locking_script('');
102 1         61 $output_copy->set_max_value;
103 1         9 $tx_copy->add_output($output_copy);
104             }
105              
106 2         27 $tx_copy->add_output($transaction->outputs->[$self->signing_index]);
107              
108 2         4 foreach my $input (@{$tx_copy->inputs}) {
  2         10  
109 5 100       116 $input->set_sequence_no(0)
110             unless $input == $this_input;
111             }
112             }
113              
114 34 100       205 if ($anyonecanpay) {
115 1         4 @{$tx_copy->inputs} = ($this_input);
  1         16  
116             }
117              
118 34         159 my $serialized = $tx_copy->to_serialized(witness => 0);
119 34         137 $serialized .= pack 'V', $self->sighash;
120              
121 34         406 return $serialized;
122             }
123              
124             sub _get_digest_segwit
125             {
126 46     46   137 my ($self, $sighash_type, $anyonecanpay) = @_;
127 46         225 my $transaction = $self->transaction->clone;
128 46         237 my $this_input = $transaction->inputs->[$self->signing_index]->clone;
129 46         193 $transaction->inputs->[$self->signing_index] = $this_input;
130              
131 46         118 my $empty_hash = "\x00" x 32;
132 46         118 my $single = $sighash_type == Bitcoin::Crypto::Constants::sighash_single;
133 46         102 my $none = $sighash_type == Bitcoin::Crypto::Constants::sighash_none;
134              
135 46 100       165 if ($self->signing_subscript) {
136              
137             # NOTE: sets witness for proper behavior of _script_code in
138             # Bitcoin::Crypto::Transaction::Input for P2WSH
139 42         987 $this_input->set_witness([$self->signing_subscript]);
140             }
141              
142             # According to https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
143             # Double SHA256 of the serialization of:
144             # 1. nVersion of the transaction (4-byte little endian)
145             # 2. hashPrevouts (32-byte hash)
146             # 3. hashSequence (32-byte hash)
147             # 4. outpoint (32-byte hash + 4-byte little endian)
148             # 5. scriptCode of the input (serialized as scripts inside CTxOuts)
149             # 6. value of the output spent by this input (8-byte little endian)
150             # 7. nSequence of the input (4-byte little endian)
151             # 8. hashOutputs (32-byte hash)
152             # 9. nLocktime of the transaction (4-byte little endian)
153             # 10. sighash type of the signature (4-byte little endian)
154              
155 46         11907 my $serialized = '';
156 46         238 $serialized .= pack 'V', $transaction->version;
157              
158 46         103 my @prevouts;
159             my @sequences;
160 46         92 foreach my $input (@{$transaction->inputs}) {
  46         132  
161 58         251 push @prevouts, $input->prevout;
162 58         224 push @sequences, pack 'V', $input->sequence_no;
163             }
164              
165 46         95 my @outputs;
166 46         91 foreach my $output (@{$transaction->outputs}) {
  46         145  
167 79         318 my $tmp = $output->locking_script->to_serialized;
168 79         272 push @outputs, $output->value_serialized . pack_varint(length $tmp) . $tmp;
169             }
170              
171             # handle prevouts
172 46 100       293 $serialized .= $anyonecanpay
173             ? $empty_hash
174             : hash256(join '', @prevouts)
175             ;
176              
177             # handle sequences
178 46 100 100     880 $serialized .= $anyonecanpay || $single || $none
179             ? $empty_hash
180             : hash256(join '', @sequences)
181             ;
182              
183 46         503 $serialized .= $this_input->prevout;
184              
185 46         272 my $script_base = $this_input->script_base->to_serialized;
186 46         264 $serialized .= pack_varint(length $script_base);
187 46         182 $serialized .= $script_base;
188              
189 46         258 $serialized .= $this_input->utxo->output->value_serialized;
190 46         221 $serialized .= pack 'V', $this_input->sequence_no;
191              
192             # handle outputs
193 46 100 100     298 if (!$single && !$none) {
    100 100        
194 38         192 $serialized .= hash256(join '', @outputs);
195             }
196             elsif ($single && $self->signing_index < @outputs) {
197 4         16 $serialized .= hash256($outputs[$self->signing_index]);
198             }
199             else {
200 4         10 $serialized .= $empty_hash;
201             }
202              
203 46         670 $serialized .= pack 'V', $transaction->locktime;
204 46         207 $serialized .= pack 'V', $self->sighash;
205              
206 46         546 return $serialized;
207             }
208              
209             1;
210