File Coverage

blib/lib/Bitcoin/Crypto/BIP44.pm
Criterion Covered Total %
statement 37 37 100.0
branch 6 6 100.0
condition 3 3 100.0
subroutine 12 12 100.0
pod 1 1 100.0
total 59 59 100.0


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::BIP44;
2             $Bitcoin::Crypto::BIP44::VERSION = '2.000_01'; # TRIAL
3             $Bitcoin::Crypto::BIP44::VERSION = '2.00001';
4 9     9   990 use v5.10;
  9         50  
5 9     9   73 use strict;
  9         43  
  9         241  
6 9     9   53 use warnings;
  9         71  
  9         233  
7 9     9   49 use Moo;
  9         19  
  9         112  
8 9     9   4279 use Scalar::Util qw(blessed);
  9         56  
  9         520  
9 9     9   1113 use Mooish::AttributeBuilder -standard;
  9         3787  
  9         102  
10              
11 9     9   2317 use Bitcoin::Crypto::Types qw(BIP44Purpose PositiveOrZeroInt Bool Enum);
  9         41  
  9         97  
12 9     9   38747 use Bitcoin::Crypto::Network;
  9         35  
  9         335  
13 9     9   107 use Bitcoin::Crypto::Exception;
  9         21  
  9         801  
14              
15             sub _get_network_constant
16             {
17             my ($network) = @_;
18              
19             my $coin_type = $network->bip44_coin;
20              
21             Bitcoin::Crypto::Exception::NetworkConfig->raise(
22             'no bip44_coin constant found in network configuration'
23             ) unless defined $coin_type;
24              
25             return $coin_type;
26             }
27              
28 9     9   62 use namespace::clean;
  9         24  
  9         112  
29              
30             has param 'purpose' => (
31             isa => BIP44Purpose,
32             default => 44,
33             );
34              
35             has param 'coin_type' => (
36             isa => PositiveOrZeroInt,
37             coerce => sub {
38             my ($coin_type) = @_;
39              
40             if (blessed $coin_type) {
41             $coin_type = $coin_type->network
42             if $coin_type->DOES('Bitcoin::Crypto::Role::Network');
43              
44             $coin_type = _get_network_constant($coin_type)
45             if $coin_type->isa('Bitcoin::Crypto::Network');
46             }
47              
48             return $coin_type;
49             },
50             default => sub {
51             _get_network_constant(Bitcoin::Crypto::Network->get);
52             },
53             );
54              
55             has param 'account' => (
56             isa => PositiveOrZeroInt,
57             default => 0,
58             );
59              
60             has param 'change' => (
61             isa => Enum [1, 0],
62             default => 0,
63             );
64              
65             has param 'index' => (
66             isa => PositiveOrZeroInt,
67             default => 0,
68             );
69              
70             has param 'get_account' => (
71             isa => Bool,
72             default => !!0,
73             );
74              
75             has param 'get_from_account' => (
76             isa => Bool,
77             default => !!0,
78             );
79              
80             has param 'public' => (
81             isa => Bool,
82             default => !!0,
83             );
84              
85             use overload
86 9         108 q{""} => 'as_string',
87 9     9   6130 fallback => 1;
  9         25  
88              
89             sub as_string
90             {
91 34     34 1 627 my ($self) = @_;
92              
93             # https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
94             # m / purpose' / coin_type' / account' / change / address_index
95              
96 34 100       169 my $path = $self->public ? 'M' : 'm';
97              
98 34 100 100     409 $path = sprintf "%s/%u'/%u'/%u'",
99             $path, $self->purpose, $self->coin_type, $self->account
100             if !$self->public && !$self->get_from_account;
101              
102 34 100       249 return $path
103             if $self->get_account;
104              
105 23         314 return sprintf "%s/%u/%u",
106             $path, $self->change, $self->index;
107             }
108              
109             1;
110              
111             __END__
112             =head1 NAME
113              
114             Bitcoin::Crypto::BIP44 - BIP44 implementation in Perl
115              
116             =head1 SYNOPSIS
117              
118             use Bitcoin::Crypto::BIP44;
119              
120             my $path = Bitcoin::Crypto::BIP44->new(
121             coin_type => Bitcoin::Crypto::Network->get('bitcoin_testnet'), # can also be a number or a key instance
122             index => 43,
123             # account => 0,
124             # change => 0,
125             );
126              
127             # stringifies automatically
128             say "$path";
129              
130             # can be used in key derivation
131             $ext_private_key->derive_key($path);
132              
133             =head1 DESCRIPTION
134              
135             This class is a helper for constructing BIP44-compilant key derivation paths.
136             BIP44 describes the mechanism the HD (Hierarchical Deterministic) wallets use
137             to decide derivation paths for coins. BIP49 and BIP84 are constructed the same
138             way, but used for compat and segwit addresses respectively.
139              
140             Each coin has its own C<coin_type> constant, a list of which is maintained
141             here: L<https://github.com/satoshilabs/slips/blob/master/slip-0044.md>.
142             L<Bitcoin::Crypto::Network> instances hold these constants under the
143             C<bip44_coin> property.
144              
145             BIP44 objects stringify automatically and can be directly used in
146             L<Bitcoin::Crypto::Key::ExtPrivate/derive_key> method. In return, any key
147             object can be used as C<coin_type> in L</new>, which will automatically fetch
148             coin_type number from the key's current network.
149              
150             =head1 INTERFACE
151              
152             =head2 Attributes
153              
154             Refer to L<https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki> for
155             details of those attributes.
156              
157             All of these attributes can be fetched using a method with the same name.
158              
159             =head3 purpose
160              
161             Purpose contains the BIP document number that you wish to use. Can be either
162             C<44>, C<49> or C<84>.
163              
164             By default, number C<44> will be used.
165              
166             =head3 coin_type
167              
168             Needs to be a non-negative integer number. It should be less than C<2^31> (but
169             will not check for that).
170              
171             Will also accept key objects and network objects, as it is possible to fetch
172             the constant for them. In this case, it might raise an exception if the network
173             does not contain the C<bip44_coin> constant.
174              
175             This value should be in line with the table of BIP44 constants mentioned above.
176              
177             By default, the value defined in the current default network will be used: see
178             L<Bitcoin::Crypto::Network>.
179              
180             =head3 account
181              
182             Needs to be a non-negative integer number. It should be less than C<2^31> (but
183             will not check for that).
184              
185             By default, the value C<0> is used.
186              
187             =head3 change
188              
189             Needs to be a number C<1> (for addresses to be used as change outputs) or C<0>
190             (for addresses that are to be used only internally).
191              
192             By default, the value C<0> is used.
193              
194             =head3 index
195              
196             Needs to be a non-negative integer number. It should be less than C<2^31> (but
197             will not check for that).
198              
199             By default, the value C<0> is used.
200              
201             =head3 public
202              
203             Use C<public> if you want to derive a public extended key. This is a must when
204             deriving BIP44 from extended public keys.
205              
206             Since it isn't possible to derive full BIP44 path with public derivation
207             scheme, this will assume L</get_from_account>.
208              
209             =head3 get_account
210              
211             If passed C<1>, the resulting derivation path will only go as far as to the
212             account part. L</index> and L</change> values will be ignored. Use this to get
213             extended key for the account.
214              
215             By default, you will get the full derivation path.
216              
217             =head3 get_from_account
218              
219             If passed C<1>, the resulting derivation path will start after the account
220             part. L</purpose>, L</coin_type> and L</account> values will be ignored. Use
221             this to further derive key that was only derived up to the account part.
222              
223             By default, you will get the full derivation path.
224              
225             =head2 Methods
226              
227             =head3 new
228              
229             $bip_object = $class->new(%data)
230              
231             This is a standard Moo constructor, which can be used to create the object. It
232             takes arguments specified in L</Attributes>.
233              
234             Returns class instance.
235              
236             =head3 as_string
237              
238             $path = $object->as_string()
239              
240             Stringifies the object as BIP44-compilant key derivation path. Can be used
241             indirectly by just stringifying the object.
242              
243             =head1 EXCEPTIONS
244              
245             This module throws an instance of L<Bitcoin::Crypto::Exception> if it
246             encounters an error. It can produce the following error types from the
247             L<Bitcoin::Crypto::Exception> namespace:
248              
249             =over
250              
251             =item * NetworkConfig - incomplete or corrupted network configuration
252              
253             =back
254              
255             =head1 SEE ALSO
256              
257             L<Bitcoin::Crypto::Key::ExtPrivate>
258              
259             L<https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki>
260