Bitcoin-Crypto

 view release on metacpan or  search on metacpan

lib/Bitcoin/Crypto/Key/Private.pm  view on Meta::CPAN

use v5.14;
use warnings;

use Mooish::Base -standard;
use Bitcoin::BIP39 qw(bip39_mnemonic_to_entropy entropy_to_bip39_mnemonic);
use Types::Common -sigs;
use List::Util qw(none);

use Bitcoin::Crypto::Key::Public;
use Bitcoin::Crypto::Base58 qw(encode_base58check decode_base58check);
use Bitcoin::Crypto::Constants qw(:key);
use Bitcoin::Crypto::Network;
use Bitcoin::Crypto::Util::Internal qw(validate_wif);
use Bitcoin::Crypto::Helpers qw(ensure_length);
use Bitcoin::Crypto::Exception;
use Bitcoin::Crypto::Types -types;

extends qw(Bitcoin::Crypto::Key::Base);

sub _is_private { 1 }

sub to_wif
{
	my ($self) = @_;
	my $bytes = $self->to_serialized();

	# wif network - 1B
	my $wifdata = $self->network->wif_byte;

	# key entropy - 32B
	$wifdata .= ensure_length $bytes, KEY_MAX_LENGTH;

	# additional byte for compressed key - 1B
	$wifdata .= WIF_COMPRESSED_BYTE if $self->compressed;

	return encode_base58check($wifdata);
}

signature_for from_serialized => (
	method => !!1,
	positional => [BitcoinSecret],
);

signature_for from_wif => (
	method => !!1,
	positional => [BitcoinSecret, Maybe [Str], {default => undef}],
);

sub from_wif
{
	my ($class, $secret, $network) = @_;

	return $secret->unmask_to(
		sub {
			my ($wif) = @_;

			Bitcoin::Crypto::Exception::KeyCreate->raise(
				'base58 string is not valid WIF'
			) unless validate_wif($wif);

			my $decoded = decode_base58check($wif);
			my $private = substr $decoded, 1;

			my $compressed = 0;
			if (length($private) > KEY_MAX_LENGTH) {
				chop $private;
				$compressed = 1;
			}

			my $wif_network_byte = substr $decoded, 0, 1;
			my @found_networks =
				Bitcoin::Crypto::Network->find(sub { shift->wif_byte eq $wif_network_byte });
			@found_networks = grep { $_ eq $network } @found_networks
				if defined $network;

			if (@found_networks > 1) {
				my $default_network = Bitcoin::Crypto::Network->get->id;

				Bitcoin::Crypto::Exception::KeyCreate->raise(
					'found multiple networks possible for given WIF: ' . join ', ', @found_networks
				) if none { $_ eq $default_network } @found_networks;

				@found_networks = ($default_network);
			}

			Bitcoin::Crypto::Exception::KeyCreate->raise(
				"network name $network cannot be used for given WIF"
			) if @found_networks == 0 && defined $network;

			Bitcoin::Crypto::Exception::NetworkConfig->raise(
				"couldn't find network for WIF byte $wif_network_byte"
			) if @found_networks == 0;

			my $instance = $class->from_serialized($private);
			$instance->set_compressed($compressed);
			$instance->set_network(@found_networks);
			return $instance;
		}
	);
}

sub get_public_key
{
	my ($self) = @_;

	my $public = Bitcoin::Crypto::Key::Public->new(
		_key_instance => $self->raw_key('public'),
		compressed => $self->compressed,
		network => $self->network,
		purpose => $self->purpose,
		taproot_output => $self->taproot_output,
	);

	return $public;
}

1;

__END__
=head1 NAME

Bitcoin::Crypto::Key::Private - Bitcoin private keys

=head1 SYNOPSIS

	use Bitcoin::Crypto::Key::Private;

	# get Bitcoin::Crypto::Key::Public instance from private key

	my $pub = $priv->get_public_key();



( run in 1.017 second using v1.01-cache-2.11-cpan-39bf76dae61 )