File Coverage

blib/lib/Bitcoin/Crypto/Network.pm
Criterion Covered Total %
statement 47 47 100.0
branch 7 12 58.3
condition 2 3 66.6
subroutine 14 14 100.0
pod 5 5 100.0
total 75 81 92.5


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Network;
2             $Bitcoin::Crypto::Network::VERSION = '1.008';
3 12     12   140632 use v5.10;
  12         69  
4 12     12   66 use strict;
  12         38  
  12         287  
5 12     12   61 use warnings;
  12         21  
  12         357  
6 12     12   1640 use Moo;
  12         21743  
  12         76  
7 12     12   8756 use Scalar::Util qw(blessed);
  12         27  
  12         651  
8 12     12   1859 use Types::Standard qw(Str Int);
  12         344615  
  12         92  
9 12     12   31609 use Types::Common::String qw(StrLength);
  12         749836  
  12         116  
10              
11 12     12   15122 use Bitcoin::Crypto::Exception;
  12         42  
  12         367  
12              
13 12     12   75 use namespace::clean;
  12         32  
  12         109  
14              
15             my %networks;
16             my $default_network;
17              
18             has "id" => (
19             is => "ro",
20             isa => Str,
21             required => 1,
22             );
23              
24             has "name" => (
25             is => "ro",
26             isa => Str,
27             required => 1,
28             );
29              
30             has "p2pkh_byte" => (
31             is => "ro",
32             isa => StrLength [1, 1],
33             required => 1,
34             );
35              
36             has "wif_byte" => (
37             is => "ro",
38             isa => StrLength [1, 1],
39             required => 1,
40             );
41              
42             has "p2sh_byte" => (
43             is => "ro",
44             isa => StrLength [1, 1],
45             required => 0,
46             );
47              
48             has "segwit_hrp" => (
49             is => "ro",
50             isa => Str,
51             required => 0,
52             );
53              
54             has "extprv_version" => (
55             is => "ro",
56             isa => Int,
57             required => 0,
58             );
59              
60             has "extpub_version" => (
61             is => "ro",
62             isa => Int,
63             required => 0,
64             );
65              
66             has "extprv_compat_version" => (
67             is => "ro",
68             isa => Int,
69             required => 0,
70             );
71              
72             has "extpub_compat_version" => (
73             is => "ro",
74             isa => Int,
75             required => 0,
76             );
77              
78             has "extprv_segwit_version" => (
79             is => "ro",
80             isa => Int,
81             required => 0,
82             );
83              
84             has "extpub_segwit_version" => (
85             is => "ro",
86             isa => Int,
87             required => 0,
88             );
89              
90             has "bip44_coin" => (
91             is => "ro",
92             isa => Int,
93             required => 0,
94             );
95              
96             sub register
97             {
98 50     50 1 376 my ($self, %config) = @_;
99 50 50       231 if (!blessed $self) {
100 50         953 $self = $self->new(%config);
101             }
102 49         99993 $networks{$self->id} = $self;
103 49         211 return $self;
104             }
105              
106             sub set_default
107             {
108 13     13 1 42 my ($self) = @_;
109              
110 13 50       97 Bitcoin::Crypto::Exception::NetworkConfig->raise(
111             "network must be an instance of Bitcoin::Crypto::Network"
112             ) unless blessed($self);
113              
114             Bitcoin::Crypto::Exception::NetworkConfig->raise(
115             "the network needs to be registered before becoming the default one"
116 13 50       79 ) unless defined $networks{$self->id};
117              
118 13         45 $default_network = $self->id;
119 13         27 return $self;
120             }
121              
122             sub supports_segwit
123             {
124 34     34 1 75 my ($self) = @_;
125              
126 34         146 return defined $self->segwit_hrp;
127             }
128              
129             sub find
130             {
131 57     57 1 2317 my ($class, $sub) = @_;
132              
133 57 100       155 return keys %networks
134             unless defined $sub;
135              
136 54 50       193 Bitcoin::Crypto::Exception::NetworkConfig->raise(
137             "argument passed to Bitcoin::Crypto::Network->find must be a coderef returning boolean"
138             ) unless ref $sub eq "CODE";
139              
140 54         208 return grep { $sub->($networks{$_}) } keys %networks;
  218         571  
141             }
142              
143             sub get
144             {
145 599     599 1 5773 my ($class, $id) = @_;
146              
147 599   66     2514 my $network = $networks{$id // $default_network};
148 599 50       1438 Bitcoin::Crypto::Exception::NetworkConfig->raise(
149             "network $id is not registered"
150             ) unless defined $network;
151              
152 599         10902 return $network;
153             }
154              
155             ### PREDEFINED NETWORKS SECTION
156             # When adding a network, make sure to:
157             # - code in valid constants of the network below
158             # - provide resources that will confirm these constant values (in the merge request)
159             # - add your network to the POD documentation below
160             # - add your network to test file 17-predefined-networks.t
161              
162             ### BITCOIN
163              
164             __PACKAGE__->register(
165             id => "bitcoin",
166             name => "Bitcoin Mainnet",
167             p2pkh_byte => "\x00",
168             p2sh_byte => "\x05",
169             wif_byte => "\x80",
170             segwit_hrp => "bc",
171              
172             extprv_version => 0x0488ade4,
173             extpub_version => 0x0488b21e,
174              
175             extprv_compat_version => 0x049d7878,
176             extpub_compat_version => 0x049d7cb2,
177              
178             extprv_segwit_version => 0x04b2430c,
179             extpub_segwit_version => 0x04b24746,
180              
181             bip44_coin => 0,
182             )->set_default;
183              
184             __PACKAGE__->register(
185             id => "bitcoin_testnet",
186             name => "Bitcoin Testnet",
187             p2pkh_byte => "\x6f",
188             p2sh_byte => "\xc4",
189             wif_byte => "\xef",
190             segwit_hrp => "tb",
191              
192             extprv_version => 0x04358394,
193             extpub_version => 0x043587cf,
194              
195             extprv_compat_version => 0x044a4e28,
196             extpub_compat_version => 0x044a5262,
197              
198             extprv_segwit_version => 0x045f18bc,
199             extpub_segwit_version => 0x045f1cf6,
200              
201             bip44_coin => 1,
202             );
203              
204             ### DOGECOIN
205              
206             __PACKAGE__->register(
207             id => "dogecoin",
208             name => "Dogecoin Mainnet",
209             p2pkh_byte => "\x1e",
210             p2sh_byte => "\x16",
211             wif_byte => "\x9e",
212              
213             extprv_version => 0x02fac398,
214             extpub_version => 0x02facafd,
215              
216             bip44_coin => 3,
217             );
218              
219             __PACKAGE__->register(
220             id => "dogecoin_testnet",
221             name => "Dogecoin Testnet",
222             p2pkh_byte => "\x71",
223             p2sh_byte => "\xc4",
224             wif_byte => "\xf1",
225              
226             extprv_version => 0x04358394,
227             extpub_version => 0x043587cf,
228              
229             bip44_coin => 1,
230             );
231              
232             1;
233              
234             __END__
235             =head1 NAME
236              
237             Bitcoin::Crypto::Network - Management tool for cryptocurrency networks
238              
239             =head1 SYNOPSIS
240              
241             use Bitcoin::Crypto::Network;
242              
243             # by default network is set to bitcoin
244             # get() without arguments returns default network
245              
246             Bitcoin::Crypto::Network->get->name; # Bitcoin Mainnet
247              
248             # by default there are two networks specified
249             # find() without arguments returns a list of all network ids
250              
251             Bitcoin::Crypto::Network->find; # (mainnet, testnet)
252              
253             # you can get full network configuration with get() using network id
254              
255             Bitcoin::Crypto::Network->get("bitcoin_testnet")->name; # Bitcoin Testnet
256              
257             # search for network and get array of keys in return
258             # there will be multiple results if your search is matched
259             # by multiple networks
260              
261             Bitcoin::Crypto::Network->find(sub { shift->name eq "Bitcoin Mainnet" }); # (mainnet)
262             Bitcoin::Crypto::Network->find(sub { shift->p2pkh_byte eq "\x6f" }); # (testnet)
263              
264             # if you're working with cryptocurrency other than Bitcoin you need to add a new network
265              
266             # network configuration is important for importing WIF private keys (network
267             # recognition), generating addresses and serializing extended keys.
268             # It may also hold other data specific to a network
269              
270             # register() can be used to create a network
271              
272             my $litecoin = Bitcoin::Crypto::Network->register(
273             id => "litecoin",
274             name => "Litecoin Mainnet",
275             p2pkh_byte => "\x30",
276             wif_byte => "\xb0",
277             );
278              
279             # after you've added your network you can set it as default. This means that
280             # all extended keys generated by other means than importing serialized key and
281             # all private keys generated by other means than importing WIF / extended keys
282             # will use that configuration.
283              
284             $litecoin->set_default;
285              
286              
287             =head1 DESCRIPTION
288              
289             This package allows you to manage non-bitcoin cryptocurrencies.
290             Before you start producing keys and addresses for your favorite crypto
291             you have to configure its network first.
292              
293             =head1 PREDEFINED NETWORKS
294              
295             Here is a list of networks that are already defined and can be used without defining them.
296              
297             If you want to see more predefined networks added and you're willing to make
298             some research to find out the correct values for the configuration fields,
299             consider opening a pull request on Github.
300              
301             =head2 Bitcoin Mainnet
302              
303             defined with id: C<bitcoin>
304              
305             =head2 Bitcoin Testnet
306              
307             defined with id: C<bitcoin_testnet>
308              
309             =head2 Dogecoin Mainnet
310              
311             defined with id: C<dogecoin>
312              
313             =head2 Dogecoin Testnet
314              
315             defined with id: C<dogecoin_testnet>
316              
317             =head1 CONFIGURATION
318              
319             Right now networks only require four keys, which are marked with C<(*)>
320              
321             my %config = (
322             id => "(*) identifier for the network",
323             name => "(*) human-readable network name",
324             p2pkh_byte => "(*) p2pkh address prefix byte, eg. 0x00",
325             wif_byte => "(*) WIF private key prefix byte, eg. 0x80",
326             p2sh_byte => "p2sh address prefix byte, eg. 0x05",
327             segwit_hrp => "segwit native address human readable part, eg. 'bc'",
328              
329             extprv_version => "version prefix of serialized extended private keys, eg. 0x0488ade4",
330             extpub_version => "version prefix of serialized extended public keys, eg. 0x0488b21e",
331             extprv_compat_version => "same as extprv_version, but for BIP49",
332             extpub_compat_version => "same as extpub_version, but for BIP49",
333             extprv_segwit_version => "same as extprv_version, but for BIP84",
334             extpub_segwit_version => "same as extpub_version, but for BIP84",
335              
336             bip44_coin => "bip44 coin number, eg. 0",
337             );
338              
339             After you register a network with this hashref your program will be able to import keys for that
340             network but all keys created from other sources will be treated as Bitcoin.
341             You need to set_default to make all new keys use it. If you use many
342             networks it might be better to set a network with key's set_network method:
343              
344             $priv->set_network("network_id");
345              
346             Remember that if you don't specify network field for some feature you won't be able to
347             use it. For example the module will complain if you try to generate segwit address
348             with custom network without segwit_hrp field set.
349              
350             =head1 METHODS
351              
352             =head2 register
353              
354             $network_object = $class->register(%config)
355             $network_object = $object->register()
356              
357             Adds the network instance to a list of known networks.
358              
359             Calls L</new> with keys present in C<%config> hash when called in static context.
360              
361             Returns the network instance.
362              
363             =head2 set_default
364              
365             $network_object = $object->set_default()
366              
367             Sets the network as default one. All newly created private and public keys will be bound to this network.
368              
369             Returns the network instance.
370              
371             =head2 supports_segwit
372              
373             $bool = $object->supports_segwit()
374              
375             Returns a boolean which can be used to determine whether a given network has segwit configured.
376              
377             =head2 new
378              
379             $network_object = $class->new(%config)
380              
381             Creates a new network instance. See L</CONFIGURATION> for a list of possible C<%config> keys.
382              
383             =head2 get
384              
385             $network_object = $class->get($id = undef)
386              
387             Without arguments, returns the default network configuration as the Bitcoin::Crypto::Network instance.
388              
389             With the C<$id> argument (string), returns the instance of a configuration matching the id.
390              
391             Throws an exception if network doesn't exist.
392              
393             =head2 find
394              
395             @network_objects = $class->find($sub = undef)
396              
397             Without arguments, returns a list of all registered network ids.
398              
399             With the C<$sub> argument (coderef), searches for all networks that pass the criteria and returns their ids.
400              
401             Returns list of network instances.
402              
403             The C<$sub> will be passed all the instances of registered networks, one at a time.
404              
405             If must perform required checks and return a boolean value. All the networks that pass this
406             test will be returned. Example:
407              
408             sub {
409             my $instance = shift;
410             return $instance->name eq "Some name";
411             }
412              
413             =head1 SEE ALSO
414              
415             =over 2
416              
417             =item L<Bitcoin::Crypto::Key::ExtPrivate>
418              
419             =item L<Bitcoin::Crypto::Key::Private>
420              
421             =back
422              
423             =cut
424