File Coverage

blib/lib/Bitcoin/Crypto/BIP44.pm
Criterion Covered Total %
statement 40 40 100.0
branch 4 4 100.0
condition n/a
subroutine 13 13 100.0
pod 1 1 100.0
total 58 58 100.0


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