use v5.10;
use strict;
use warnings;

use Bitcoin::Crypto qw(btc_psbt btc_transaction btc_utxo btc_prv btc_script_tree btc_tapscript);
use Bitcoin::Crypto::Util qw(to_format get_taproot_ext);
use Bitcoin::Crypto::Network;

Bitcoin::Crypto::Network->get('bitcoin_testnet')->set_default;

# this is PSBT generated by taproot_script_create.pl script
my $psbt = btc_psbt->from_serialized(
	[
		base64 =>
			'cHNidP8BAF4BAAAAASrw4x+sPfBApDwbeArERu8uYmQEVAQgnrLD8fA6tpsmAAAAAAD/////AaifBwAAAAAAIlEgSOoBPq0tYMqcvx0BPYScIeRsE/xTJQ9Ut58kC3OphC8AAAAAAAABBSC66lPZr8m73ZKZvmN2kATfEefN1nYUpXX5lOJRGdz3/QEGcQHAaQAgBq4mEWYxDxsfKhKphM8+BCROC1d5lUMuFVsq8n63WCK6IOWo3YxzwW0ugT4JF2ltpsReMj56V4FfePgU33VeBkMXuiA4GJfkxEVI62Z37Clb+MztzKrWNi7iENHZw7SFjRL5V7pSnAHAAl2cAA=='
	]
);

# extract outputs from psbt transaction
my $prev_tx = $psbt->get_field('PSBT_GLOBAL_UNSIGNED_TX')->value;
$prev_tx->update_utxos;

# get tree from psbt, mark the leaf we're spending with an id
my $tree = $psbt->get_field('PSBT_OUT_TAP_TREE', 0)->value;
$tree->tree->[0]{id} = 0;

# get public key from psbt
my $public_key = $psbt->get_field('PSBT_OUT_TAP_INTERNAL_KEY', 0)->value;

my $tx = btc_transaction->new;

# die to_format [hex => $psbt->get_field('PSBT_GLOBAL_UNSIGNED_TX')->value->get_hash];
# input must point to the transaction output above - transaction ID and output number
$tx->add_input(
	utxo => [$prev_tx->get_hash, 0],
);

# send all the coins to this address.
$tx->add_output(
	locking_script => [address => 'tb1plhfdt6d2ngfsl7zn0rd59pfz87ynqj5wvmttxwy4xxn2zkcregkqdvkalv'],
	value => 0,
);

# calculate fee and set the value of first output. Unsigned tx virtual size is
# used, so the real fee rate will be approx two times smaller
my $wanted_fee_rate = 2;
$tx->outputs->[0]->set_value($tx->fee - int($tx->virtual_size * $wanted_fee_rate));

# manual signing (since the transaction is custom):
# - signing with two private keys to satisfy 2 out of 3 transaction
# - private keys must be in reverse order than the order of keys in
#   OP_CHECKSIGADD calls
# - private keys must be taproot output keys
# - leaving one empty vector for the one signature we don't use (second item)
# - signing with SIGHASH_DEFAULT, so it does not need to be added to the
#   signature manually (as 65th byte)
# - subscript is the whole script, because there are no OP_CODESEPARATORs
# - ext_flag is 1, and ext is generated as per BIP342
# - both signatures can use the same digest
# - adding serialized script and control block as the last two witness items to
#   mark script path spend
# - not adding annex, since it has no meaning yet (can lead to funds loss)

my $private_key_1 = btc_prv->from_wif('L2eKy3kX5DYnw7B1sXpEs2gd9xK5PSkiiBS1YSFaHvYyn1M9rsJJ');
my $private_key_3 = btc_prv->from_wif('L2zrD2aQRgGHzJpX7TB7qYhzibFqitH3NyvZUJUzqfHaLmxyBvm5');
my $script = $tree->tree->[0]{script};

$private_key_1->set_taproot_output(!!1);
$private_key_3->set_taproot_output(!!1);

my $digest = $tx->get_digest(
	signing_index => 0,
	signing_subscript => $script->to_serialized,
	taproot_ext_flag => 1,
	taproot_ext => get_taproot_ext(
		1,
		script_tree => $tree,
		leaf_id => 0,
		codesep_pos => undef
	),
);

$tx->inputs->[0]->set_witness(
	[
		$private_key_3->sign_message($digest),
		'',
		$private_key_1->sign_message($digest),
		$script->to_serialized,
		$tree->get_control_block(0, $public_key)->to_serialized,
	]
);

# verify the correctness of the transaction. Throws an exception on failure
$tx->verify;

say $tx->dump;
say to_format [hex => $tx->to_serialized];

__END__

=head1 P2TR script output redeem example

A transaction spending one P2TR input with script spend path and produces a
single P2TR output. Example uses "Nothing up my sleeve" point and script tree
generated previously, which is imported from a PSBT base64 string.

We choose the first script in the tree, which uses new C<OP_CHECKSIGADD> opcode
to implement a 2 out of 3 multisig script. Taproot has old C<OP_CHECKMULTISIG>
opcode disabled.

This example shows how to manually sign a custom taproot transaction, which can
be extended to most other custom transactions.

Fee rate is (inaccurately) approximated. To set exact fee rate sign the
transaction, calculate fee based on its virtual size and then sign again
- changing the value of the output invalidates previous signatures.

This code was used to produce testnet transaction:
L<https://mempool.space/testnet4/tx/d87c30f149aae524296ea39b6f2db2d6361981bdde36ae1862e946910be9ea0b>

