File Coverage

blib/lib/Bitcoin/Crypto/Transaction/UTXO.pm
Criterion Covered Total %
statement 32 32 100.0
branch n/a
condition n/a
subroutine 11 11 100.0
pod n/a
total 43 43 100.0


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Transaction::UTXO;
2             $Bitcoin::Crypto::Transaction::UTXO::VERSION = '2.000_01'; # TRIAL
3             $Bitcoin::Crypto::Transaction::UTXO::VERSION = '2.00001';
4 9     9   136 use v5.10;
  9         41  
5 9     9   88 use strict;
  9         30  
  9         218  
6 9     9   63 use warnings;
  9         28  
  9         250  
7              
8 9     9   79 use Moo;
  9         21  
  9         75  
9 9     9   4519 use Mooish::AttributeBuilder -standard;
  9         30  
  9         103  
10 9     9   1296 use Type::Params -sigs;
  9         46  
  9         99  
11              
12 9     9   9222 use Bitcoin::Crypto::Transaction;
  9         50  
  9         390  
13 9     9   70 use Bitcoin::Crypto::Transaction::Output;
  9         26  
  9         313  
14 9     9   58 use Bitcoin::Crypto::Types qw(IntMaxBits Int PositiveOrZeroInt ByteStr InstanceOf HashRef Str Object Maybe CodeRef);
  9         41  
  9         95  
15 9     9   37580 use Bitcoin::Crypto::Util qw(to_format);
  9         44  
  9         436  
16 9     9   79 use Bitcoin::Crypto::Exception;
  9         39  
  9         6253  
17              
18             my %utxos;
19             my $loader;
20              
21             has param 'txid' => (
22             coerce => ByteStr->create_child_type(
23             constraint => q{ length $_ == 32 },
24             coercion => 1
25             ),
26             );
27              
28             # NOTE: ideally, utxo should point to a transaction, and transaction should
29             # point to a block
30             has option 'block' => (
31             isa => InstanceOf ['Bitcoin::Crypto::Block'],
32             );
33              
34             has param 'output_index' => (
35             isa => IntMaxBits [32],
36             );
37              
38             has param 'output' => (
39             coerce => (InstanceOf ['Bitcoin::Crypto::Transaction::Output'])
40             ->plus_coercions(HashRef q{ Bitcoin::Crypto::Transaction::Output->new($_) }),
41             );
42              
43             signature_for register => (
44             method => Object,
45             positional => [],
46             );
47              
48             sub register
49             {
50             my ($self) = @_;
51              
52             # Do not store NULLDATA UTXOs
53             return $self
54             if $self->output->is_standard && $self->output->locking_script->type eq 'NULLDATA';
55              
56             $utxos{$self->txid}[$self->output_index] = $self;
57             return $self;
58             }
59              
60             signature_for unregister => (
61             method => Object,
62             positional => [],
63             );
64              
65             sub unregister
66             {
67             my ($self) = @_;
68              
69             delete $utxos{$self->txid}[$self->output_index];
70             return $self;
71             }
72              
73             signature_for get => (
74             method => Str,
75             positional => [ByteStr, PositiveOrZeroInt],
76             );
77              
78             sub get
79             {
80             my ($class, $txid, $outid) = @_;
81              
82             my $utxo = $utxos{$txid}[$outid];
83              
84             # NOTE: loader should unregister the utxo in its own store
85             if (!$utxo && defined $loader) {
86             $utxo = $loader->($txid, $outid);
87             $utxo->register if $utxo;
88             }
89              
90             Bitcoin::Crypto::Exception::UTXO->raise(
91             "no UTXO registered for transaction id @{[to_format [hex => $txid]]} and output index $outid"
92             ) unless $utxo;
93              
94             return $utxo;
95             }
96              
97             signature_for set_loader => (
98             method => Str,
99             positional => [Maybe[CodeRef]],
100             );
101              
102             sub set_loader
103             {
104             my ($class, $new_loader) = @_;
105              
106             $loader = $new_loader;
107             return;
108             }
109              
110             signature_for extract => (
111             method => Str,
112             positional => [ByteStr],
113             );
114              
115             sub extract
116             {
117             my ($class, $serialized_tx) = @_;
118              
119             # hijack the utxo loader
120             my $old_loader = $loader;
121             $loader = sub {
122             if ($old_loader) {
123             my $loaded = $old_loader->(@_);
124             return $loaded if $loaded;
125             }
126              
127             return $class->new(
128             txid => shift,
129             output_index => shift,
130             output => {
131             locking_script => [NULLDATA => 'stub utxo'],
132             value => 0,
133             },
134             );
135             };
136              
137             my $tx = Bitcoin::Crypto::Transaction->from_serialized($serialized_tx);
138             $loader = $old_loader;
139              
140             $tx->update_utxos;
141             return;
142             }
143              
144             1;
145              
146             __END__
147             =head1 NAME
148              
149             Bitcoin::Crypto::Transaction::UTXO - Unspent transaction output instance
150              
151             =head1 SYNOPSIS
152              
153             use Bitcoin::Crypto qw(btc_utxo);
154              
155             # register the utxos automatically from the serialized transaction
156             btc_utxo->extract($serialized_tx);
157              
158             # create the utxo manually
159             my $utxo = btc_utxo->new(
160             txid => [hex => '94e519b9c0f43228e3dc841d838fc7372de95345206ef936ac6020889abe0457'],
161             output_index => 1,
162             output => {
163             locking_script => [P2PKH => '1HrfeGdVP4d1uAdbSknzeaFpDFQVJyVpLu'],
164             value => 1_02119131,
165             }
166             );
167              
168             # register
169             $utxo->register;
170              
171             # find the utxo
172             btc_utxo->get([hex => '94e519b9c0f43228e3dc841d838fc7372de95345206ef936ac6020889abe0457'], 1);
173              
174             # unregister
175             $utxo->unregister;
176              
177             =head1 DESCRIPTION
178              
179             UTXO is a transaction output which hasn't been spent yet. All transaction
180             inputs must be UTXOs. Bitcoin::Crypto requires you to register UTXOs before you
181             can create a transaction.
182              
183             =head1 INTERFACE
184              
185             =head2 Attributes
186              
187             =head3 txid
188              
189             A bytestring - id of the source transaction.
190              
191             I<Available in the constructor>.
192              
193             =head3 output_index
194              
195             A positive or zero integer which is the index of the output in the source
196             transaction.
197              
198             I<Available in the constructor>.
199              
200             =head3 block
201              
202             Optional instance of L<Bitcoin::Crypto::Block>.
203              
204             I<Available in the constructor>.
205              
206             =head3 output
207              
208             Instance of L<Bitcoin::Crypto::Transaction::Output>. A hash reference will be
209             coerced into an object by passing it to the constructor.
210              
211             I<Available in the constructor>.
212              
213             =head2 Methods
214              
215             =head3 new
216              
217             $tx = $class->new(%args)
218              
219             This is a standard Moo constructor, which can be used to create the object. It
220             takes arguments specified in L</Attributes>.
221              
222             Returns class instance.
223              
224             =head3 register
225              
226             $object = $object->register()
227              
228             Registers the given UTXO. It will be held in memory and will be available to
229             fetch using L</get>.
230              
231             =head3 unregister
232              
233             $object = $object->unregister()
234              
235             Does the opposite of L</register>.
236              
237             =head3 get
238              
239             $utxo = $object->get($txid, $output_index);
240              
241             Returns the UTXO registered with given txid and output index. Throws an
242             exception if it cannot be found or loaded.
243              
244             =head3 set_loader
245              
246             $class->set_loader(sub { ... })
247             $class->set_loader(undef)
248              
249             Replaces an UTXO loader.
250              
251             The subroutine should accept the same parameters as L</get> and return a
252             constructed UTXO object. If possible, the loader should not return the same
253             UTXO twice in a single runtime of the script.
254              
255             Returns nothing. Passing undef disables the loader.
256              
257             =head3 extract
258              
259             $class->extract($serialized_tx)
260              
261             Extracts all outputs from the C<$serialized_tx> (a bytestring).
262              
263             Returns nothing.
264              
265             =head1 EXCEPTIONS
266              
267             This module throws an instance of L<Bitcoin::Crypto::Exception> if it
268             encounters an error. It can produce the following error types from the
269             L<Bitcoin::Crypto::Exception> namespace:
270              
271             =over
272              
273             =item * Bitcoin::Crypto::Exception::UTXO - UTXO was not found
274              
275             =back
276              
277             =head1 SEE ALSO
278              
279             =over
280              
281             =item L<Bitcoin::Crypto::Transaction>
282              
283             =back
284              
285             =cut
286