package Bitcoin::Crypto::Key::NUMS;
$Bitcoin::Crypto::Key::NUMS::VERSION = '4.004';
use v5.14;
use warnings;

use Mooish::Base -standard;
use Types::Common -sigs;
use Crypt::PRNG qw(random_bytes);
use Crypt::Digest::SHA256 qw(sha256);

use Bitcoin::Crypto qw(btc_pub);
use Bitcoin::Crypto::Types -types;
use Bitcoin::Crypto::Util::Internal qw(lift_x);
use Bitcoin::Crypto::Helpers qw(ecc);
use Bitcoin::Crypto::Constants qw(:curve);

# 32 random bytes can overflow ecc, but the changes of that are extremely low
has param 'tweak' => (
	coerce => ByteStrLen [32],
	lazy => sub { random_bytes(32) },
);

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

	state $nums_base = lift_x sha256 CURVE_GENERATOR;
	return btc_pub->from_serialized(ecc->add_public_key($nums_base, $self->tweak));
}

1;

__END__

=head1 NAME

Bitcoin::Crypto::Key::NUMS - "Nothing up my sleeve" key generator

=head1 SYNOPSIS

	use Bitcoin::Crypto::Key::NUMS;

	$pub = Bitcoin::Crypto::Key::NUMS->new->get_public_key;

	# create an address with disabled key spend path
	my $addr = $pub->get_taproot_address($script_tree);

=head1 DESCRIPTION

This class implements NUMS public keys defined in BIP341. These public keys
have no known discrete logarithm (private key) and they can be proven to be
unspendable.

When using these public keys, a valid taproot script path spending must be
provided by adding C<$script_tree> argument to
L<Bitcoin::Crypto::Key::Public/get_taproot_address> method - otherwise B<coins
will be unspendable>.

=head2 Usage

This class is designed to have two modes of operation:

=over

=item * generating NUMS keys

In this mode, you let C<tweak> be picked at random. You can at retrieve it
after generating the public key to prove that this public key cannot be spent:

	my $nums = Bitcoin::Crypto::Key::NUMS->new;
	my $pubkey = $nums->get_public_key;
	my $r = $nums->tweak;

=item * verifying NUMS keys

Suppose you want to prove the unspendability of a public key. You create an
instance of NUMS class, providing C<tweak>. You can compare the generated
public key to the one you had beforehand:

	my $got_pubkey = 'key generated by third party';
	my $got_tweak = 'tweak value they provided';

	my $nums = Bitcoin::Crypto::Key::NUMS->new(tweak => $got_tweak);
	my $pubkey = $nums->get_public_key;
	die 'bad key' unless $pubkey->to_serialized eq $got_pubkey;

=back

=head1 INTERFACE

=head2 Attributes

=head3 tweak

Available in the constructor. This is known as C<r> value in BIP341. It should
be a bytestring of length 32. If it is not provided, it will be picked at
random with CSPRNG.

Note that generating the tweak at random may fail by throwing an exception, but
the chances of that happening are extremely slim.

=head1 Methods

=head2 new

	$nums = $class->new(%args)

This is a standard Moo constructor, which can be used to create the object. It
takes arguments specified in L</Attributes>.

=head2 get_public_key

	$pubkey = $obj->get_public_key()

This method generates a new NUMS public key and returns it as an instance of
L<Bitcoin::Crypto::Key::Public>.

Note that calling this method repeatedly without modifying L</tweak>
will keep yielding the same public key.

=head1 SEE ALSO

=over

=item L<Bitcoin::Crypto::Script::Tree>

=back

=cut

