File Coverage

blib/lib/Bitcoin/Crypto/Key/Public.pm
Criterion Covered Total %
statement 36 36 100.0
branch n/a
condition n/a
subroutine 13 13 100.0
pod n/a
total 49 49 100.0


line stmt bran cond sub pod time code
1             package Bitcoin::Crypto::Key::Public;
2             $Bitcoin::Crypto::Key::Public::VERSION = '2.000_01'; # TRIAL
3             $Bitcoin::Crypto::Key::Public::VERSION = '2.00001';
4 16     16   224 use v5.10;
  16         86  
5 16     16   112 use strict;
  16         61  
  16         421  
6 16     16   96 use warnings;
  16         62  
  16         531  
7 16     16   122 use Moo;
  16         38  
  16         134  
8 16     16   7678 use Type::Params -sigs;
  16         42  
  16         157  
9              
10 16     16   14792 use Bitcoin::Crypto::Script;
  16         320  
  16         926  
11 16     16   148 use Bitcoin::Crypto::Base58 qw(encode_base58check);
  16         42  
  16         1041  
12 16     16   117 use Bitcoin::Crypto::Bech32 qw(encode_segwit);
  16         159  
  16         941  
13 16     16   123 use Bitcoin::Crypto::Types qw(Object);
  16         42  
  16         139  
14 16     16   44541 use Bitcoin::Crypto::Constants;
  16         37  
  16         501  
15 16     16   118 use Bitcoin::Crypto::Util qw(hash160 get_public_key_compressed);
  16         38  
  16         1018  
16              
17 16     16   116 use namespace::clean;
  16         50  
  16         158  
18              
19             with qw(Bitcoin::Crypto::Role::BasicKey);
20              
21 281     281   1863 sub _is_private { 0 }
22              
23             signature_for key_hash => (
24             method => Object,
25             positional => [],
26             );
27              
28             sub key_hash
29             {
30             my ($self) = @_;
31              
32             return hash160($self->to_serialized);
33             }
34              
35             around from_serialized => sub {
36             my ($orig, $class, $key) = @_;
37              
38             my $self = $class->$orig($key);
39             $self->set_compressed(get_public_key_compressed($key));
40              
41             return $self;
42             };
43              
44             signature_for witness_program => (
45             method => Object,
46             positional => [],
47             );
48              
49             sub witness_program
50             {
51             my ($self) = @_;
52              
53             my $program = Bitcoin::Crypto::Script->new(network => $self->network);
54             $program
55             ->add_operation('OP_' . Bitcoin::Crypto::Constants::segwit_witness_version)
56             ->push_bytes($self->key_hash);
57              
58             return $program;
59             }
60              
61             signature_for get_legacy_address => (
62             method => Object,
63             positional => [],
64             );
65              
66             sub get_legacy_address
67             {
68             my ($self) = @_;
69              
70             Bitcoin::Crypto::Exception::AddressGenerate->raise(
71             'legacy addresses can only be created with BIP44 in legacy (BIP44) mode'
72             ) unless $self->has_purpose(Bitcoin::Crypto::Constants::bip44_purpose);
73              
74             my $pkh = $self->network->p2pkh_byte . $self->key_hash;
75             return encode_base58check($pkh);
76             }
77              
78             signature_for get_compat_address => (
79             method => Object,
80             positional => [],
81             );
82              
83             sub get_compat_address
84             {
85             my ($self) = @_;
86              
87             # network field is not required, lazy check for completeness
88             Bitcoin::Crypto::Exception::NetworkConfig->raise(
89             'this network does not support segregated witness'
90             ) unless $self->network->supports_segwit;
91              
92             Bitcoin::Crypto::Exception::AddressGenerate->raise(
93             'compat addresses can only be created with BIP44 in compat (BIP49) mode'
94             ) unless $self->has_purpose(Bitcoin::Crypto::Constants::bip44_compat_purpose);
95              
96             return $self->witness_program->get_legacy_address;
97             }
98              
99             signature_for get_segwit_address => (
100             method => Object,
101             positional => [],
102             );
103              
104             sub get_segwit_address
105             {
106             my ($self) = @_;
107              
108             # network field is not required, lazy check for completeness
109             Bitcoin::Crypto::Exception::NetworkConfig->raise(
110             'this network does not support segregated witness'
111             ) unless $self->network->supports_segwit;
112              
113             Bitcoin::Crypto::Exception::AddressGenerate->raise(
114             'segwit addresses can only be created with BIP44 in segwit (BIP84) mode'
115             ) unless $self->has_purpose(Bitcoin::Crypto::Constants::bip44_segwit_purpose);
116              
117             return encode_segwit($self->network->segwit_hrp, $self->witness_program->run->stack_serialized);
118             }
119              
120             signature_for get_address => (
121             method => Object,
122             positional => [],
123             );
124              
125             sub get_address
126             {
127             my ($self) = @_;
128              
129             return $self->get_segwit_address
130             if $self->has_purpose(Bitcoin::Crypto::Constants::bip44_segwit_purpose);
131              
132             return $self->get_compat_address
133             if $self->has_purpose(Bitcoin::Crypto::Constants::bip44_compat_purpose);
134              
135             return $self->get_legacy_address
136             if $self->has_purpose(Bitcoin::Crypto::Constants::bip44_purpose);
137              
138             return $self->get_segwit_address
139             if $self->network->supports_segwit;
140              
141             return $self->get_legacy_address;
142             }
143              
144             1;
145              
146             __END__
147              
148             =head1 NAME
149              
150             Bitcoin::Crypto::Key::Public - Bitcoin public keys
151              
152             =head1 SYNOPSIS
153              
154             use Bitcoin::Crypto::Key::Public;
155              
156             $pub = Bitcoin::Crypto::Key::Public->from_serialized([hex => $asn_hex]);
157              
158             # verify signature of custom message
159             # (it has to be byte string, see perlpacktut)
160              
161             $pub->verify_message('Hello world', $sig);
162              
163             # getting address from public key (p2wpkh)
164              
165             my $address = $pub->get_segwit_address();
166              
167             =head1 DESCRIPTION
168              
169             This class allows you to create a public key instance.
170              
171             You can use a public key to:
172              
173             =over
174              
175             =item * verify messages
176              
177             =item * create addresses: legacy (p2pkh), compatibility (p2sh(p2wpkh)) and segwit (p2wpkh).
178              
179             =back
180              
181             =head1 METHODS
182              
183             =head2 new
184              
185             Constructor is reserved for internal and advanced use only. Use L</from_serialized>
186             instead.
187              
188             =head2 from_serialized
189              
190             $key_object = $class->from_serialized($serialized)
191              
192             This creates a new key from string data. Argument C<$serialized> is a
193             formatable bytestring which must represent a public key in ASN X9.62 format.
194              
195             Returns a new key object instance.
196              
197             =head2 to_serialized
198              
199             $serialized = $key_object->to_serialized()
200              
201             This returns a public key in ASN X9.62 format. The result is a bytestring which
202             can be further formated with C<to_format> utility.
203              
204             The result will vary depending on compression state: see L</set_compressed>
205              
206             =head2 from_bytes
207              
208             Deprecated. Use C<< $class->from_serialized($data) >> instead.
209              
210             =head2 to_bytes
211              
212             Deprecated. Use C<< $key->to_serialized($data) >> instead.
213              
214             =head2 from_hex
215              
216             Deprecated. Use C<< $class->from_serialized([hex => $data]) >> instead.
217              
218             =head2 to_hex
219              
220             Deprecated. Use C<< to_format [hex => $key->to_serialized($data)] >> instead.
221              
222             =head2 set_compressed
223              
224             $key_object = $object->set_compressed($val)
225              
226             Change key's compression state to C<$val> (boolean). This will change the
227             address. If C<$val> is omitted it is set to C<1>.
228              
229             Returns current key instance.
230              
231             =head2 set_network
232              
233             $key_object = $object->set_network($val)
234              
235             Change key's network state to C<$val>. It can be either network name present in
236             L<Bitcoin::Crypto::Network> package or an instance of this class.
237              
238             Returns current key instance.
239              
240             =head2 verify_message
241              
242             $signature_valid = $object->verify_message($message, $signature)
243              
244             Verifies C<$signature> against digest of C<$message> (digesting it with double
245             sha256) using public key.
246              
247             Returns boolean.
248              
249             Character encoding note: C<$message> should be encoded in the proper encoding
250             before passing it to this method. Passing Unicode string will cause the
251             function to fail. You can encode like this (for UTF-8):
252              
253             use Encode qw(encode);
254             $message = encode('UTF-8', $message);
255              
256             =head2 get_legacy_address
257              
258             $address_string = $object->get_legacy_address()
259              
260             Returns string containing Base58Check encoded public key hash (C<p2pkh> address).
261              
262             If the public key was obtained through BIP44 derivation scheme, this method
263             will check whether the purpose was C<44> and raise an exception otherwise. If
264             you wish to generate this address anyway, call L</clear_purpose>.
265              
266             =head2 get_compat_address
267              
268             $address_string = $object->get_compat_address()
269              
270             Returns string containing Base58Check encoded script hash containing a witness
271             program for compatibility purposes (C<p2sh(p2wpkh)> address)
272              
273             If the public key was obtained through BIP44 derivation scheme, this method
274             will check whether the purpose was C<49> and raise an exception otherwise. If
275             you wish to generate this address anyway, call L</clear_purpose>.
276              
277             =head2 get_segwit_address
278              
279             $address_string = $object->get_segwit_address()
280              
281             Returns string containing Bech32 encoded witness program (C<p2wpkh> address)
282              
283             If the public key was obtained through BIP44 derivation scheme, this method
284             will check whether the purpose was C<84> and raise an exception otherwise. If
285             you wish to generate this address anyway, call L</clear_purpose>.
286              
287             =head2 get_address
288              
289             $address_string = $object->get_address()
290              
291             Returns a string containing the address. Tries to guess which address type is
292             most fitting:
293              
294             =over
295              
296             =item * If the key has a BIP44 purpose set, generates type of address which
297             matches the purpose
298              
299             =item * If the key doesn't have a purpose but the network supports segwit,
300             returns a segwit address (same as C<get_segwit_address>)
301              
302             =item * If the network doesn't support segwit, returns legacy address
303              
304             =back
305              
306             B<NOTE>: The rules this function uses to choose the address type B<will>
307             change when more up-to-date address types are implemented (like taproot). Use
308             other address functions if this is not what you want.
309              
310             =head2 clear_purpose
311              
312             $object->clear_purpose;
313              
314             Clears the BIP44 purpose of this key instance, removing safety checks on
315             address generation.
316              
317             =head1 EXCEPTIONS
318              
319             This module throws an instance of L<Bitcoin::Crypto::Exception> if it
320             encounters an error. It can produce the following error types from the
321             L<Bitcoin::Crypto::Exception> namespace:
322              
323             =over
324              
325             =item * KeyCreate - key couldn't be created correctly
326              
327             =item * Verify - couldn't verify the message correctly
328              
329             =item * NetworkConfig - incomplete or corrupted network configuration
330              
331             =item * AddressGenerate - address could not be generated (see BIP44 constraint notes)
332              
333             =back
334              
335             =head1 SEE ALSO
336              
337             L<Bitcoin::Crypto::Key::Private>
338              
339             L<Bitcoin::Crypto::Base58>
340              
341             L<Bitcoin::Crypto::Bech32>
342