File Coverage

blib/lib/Bitcoin/Crypto/BIP44.pm
Criterion Covered Total %
statement 37 37 100.0
branch 4 4 100.0
condition n/a
subroutine 12 12 100.0
pod 1 1 100.0
total 54 54 100.0


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::BIP44;
2             $Bitcoin::Crypto::BIP44::VERSION = '1.008_01'; # TRIAL
3             $Bitcoin::Crypto::BIP44::VERSION = '1.00801';
4 7     7   850 use v5.10;
  7         43  
5 7     7   42 use strict;
  7         16  
  7         165  
6 7     7   34 use warnings;
  7         25  
  7         179  
7 7     7   49 use Moo;
  7         16  
  7         53  
8 7     7   3112 use Scalar::Util qw(blessed);
  7         27  
  7         503  
9 7     7   941 use Mooish::AttributeBuilder -standard;
  7         3704  
  7         76  
10              
11 7     7   1894 use Bitcoin::Crypto::Types qw(BIP44Purpose PositiveOrZeroInt Bool Enum);
  7         26  
  7         82  
12 7     7   24794 use Bitcoin::Crypto::Network;
  7         21  
  7         247  
13 7     7   50 use Bitcoin::Crypto::Exception;
  7         15  
  7         682  
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   62 use namespace::clean;
  7         15  
  7         56  
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             use overload
81 7         60 q{""} => 'as_string',
82 7     7   4703 fallback => 1;
  7         30  
83              
84             sub as_string
85             {
86 28     28 1 408 my ($self) = @_;
87              
88             # https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
89             # m / purpose' / coin_type' / account' / change / address_index
90              
91 28         246 my $path = sprintf "m/%u'/%u'/%u'",
92             $self->purpose, $self->coin_type, $self->account;
93              
94 28 100       202 return $path
95             if $self->get_account;
96              
97 19 100       72 $path = 'm'
98             if $self->get_from_account;
99              
100 19         256 return sprintf "%s/%u/%u",
101             $path, $self->change, $self->index;
102             }
103              
104             1;
105              
106             __END__
107             =head1 NAME
108              
109             Bitcoin::Crypto::BIP44 - BIP44 implementation in Perl
110              
111             =head1 SYNOPSIS
112              
113             use Bitcoin::Crypto::BIP44;
114              
115             my $path = Bitcoin::Crypto::BIP44->new(
116             coin_type => Bitcoin::Crypto::Network->get('bitcoin_testnet'), # can also be a number or a key instance
117             index => 43,
118             # account => 0,
119             # change => 0,
120             );
121              
122             # stringifies automatically
123             say "$path";
124              
125             # can be used in key derivation
126             $ext_private_key->derive_key($path);
127              
128             =head1 DESCRIPTION
129              
130             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.
131              
132             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.
133              
134             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.
135              
136             =head1 PROPERTIES
137              
138             Refer to L<https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki> for details of those properties.
139              
140             All of these properties can be fetched using a method with the same name.
141              
142             =head2 purpose
143              
144             Purpose contains the BIP document number that you wish to use. Can be either C<44>, C<49> or C<84>.
145              
146             By default, number C<44> will be used.
147              
148             =head2 coin_type
149              
150             Needs to be a non-negative integer number. It should be less than C<2^31> (but will not check for that).
151              
152             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.
153              
154             This value should be in line with the table of BIP44 constants mentioned above.
155              
156             By default, the value defined in the current default network will be used: see L<Bitcoin::Crypto::Network>.
157              
158             =head2 account
159              
160             Needs to be a non-negative integer number. It should be less than C<2^31> (but will not check for that).
161              
162             By default, the value C<0> is used.
163              
164             =head2 change
165              
166             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).
167              
168             By default, the value C<0> is used.
169              
170             =head2 index
171              
172             Needs to be a non-negative integer number. It should be less than C<2^31> (but will not check for that).
173              
174             By default, the value C<0> is used.
175              
176             =head2 get_account
177              
178             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.
179              
180             By default, you will get the full derivation path.
181              
182             =head2 get_from_account
183              
184             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.
185              
186             By default, you will get the full derivation path.
187              
188             =head1 METHODS
189              
190             =head2 new
191              
192             $key_object = $class->new(%data)
193              
194             This is a regular Moo constructor, which can be used to create the object. It takes arguments specified in L</PROPERTIES>.
195              
196             Returns class instance.
197              
198             =head2 as_string
199              
200             $path = $object->as_string()
201              
202             Stringifies the object as BIP44-compilant key derivation path. Can be used indirectly by just stringifying the object.
203              
204             =head1 EXCEPTIONS
205              
206             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:
207              
208             =over 2
209              
210             =item * NetworkConfig - incomplete or corrupted network configuration
211              
212             =back
213              
214             =head1 SEE ALSO
215              
216             =over 2
217              
218             =item L<Bitcoin::Crypto::Key::ExtPrivate>
219              
220             =item L<Bitcoin::Crypto::Network>
221              
222             =back
223              
224             =cut
225