:scrollbar: :data-uri: :toc2: :linkattrs: = P2TR End-to-End Tutorial :numbered: This tutorial is inspired from the link:https://learnmeabitcoin.com/technical/upgrades/taproot/#example-3-script-path-spend-signature[script-path-spend-signature] example of the _learnmeabitcoin_ tutorial. It is customized to create, fund and spend from a P2TR UTXO to a P2WPKH address. Execute in Bitcoin Core `regtest` mode. == Pre-reqs === Bitcoin Core The link:https://github.com/jbride/bitcoin/tree/p2mr-pqc[p2mr branch] of bitcoin core is needed. Build instructions for the `p2mr` branch are the same as `master` and is documented link:https://github.com/bitcoin/bitcoin/blob/master/doc/build-unix.md[here]. As such, the following is an example series of steps (on a Fedora 42 host) to compile and run the `p2mr` branch of bitcoin core: . build + ----- $ cmake -B build \ -DWITH_ZMQ=ON \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DBUILD_BENCH=ON \ -DSANITIZERS=address,undefined $ cmake --build build -j$(nproc) ----- . run in `regtest` mode + ----- $ ./build/bin/bitcoind -daemon=0 -regtest=1 -txindex ----- === Shell Environment . *b-reg* command line alias: + Configure an alias to the `bitcoin-cli` command that connects to your customized bitcoin-core node running in `regtest` mode. . *jq*: ensure json parsing utility is installed and available via your $PATH. . *awk* : standard utility for all Linux distros (often packaged as `gawk`). === Bitcoin Core Wallet This tutorial assumes that a bitcoin core wallet is available. For example, the following would be sufficient: ----- $ export W_NAME=regtest \ && export WPASS=regtest $ b-reg -named createwallet \ wallet_name=$W_NAME \ descriptors=true \ passphrase="$WPASS" \ load_on_startup=true ----- == Fund P2TR UTXO . OPTIONAL: Define number of leaves in tap tree as well as the tap leaf to later use as the unlocking script: + ----- $ export TOTAL_LEAF_COUNT=5 \ && export LEAF_TO_SPEND_FROM=4 ----- + NOTE: Defaults are 4 leaves with the first leaf (leaf 0 ) as the script to later use as the unlocking script. . Generate a P2TR scripPubKey with multi-leaf taptree: + ----- $ export BITCOIN_NETWORK=regtest \ && export BITCOIN_ADDRESS_INFO=$( cargo run --example p2tr_construction ) \ && echo $BITCOIN_ADDRESS_INFO | jq -r . ----- + NOTE: In `regtest`, you can expect a P2TR address that starts with: `bcrt1q` . + NOTE: In the context of P2TR, the _tree_root_hex_ from the response is in reference to the _merkle_root_ used in this tutorial. . Set some env vars (for use in later steps in this tutorial) based on previous result: + ----- $ export MERKLE_ROOT=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.taptree_return.tree_root_hex' ) \ && export LEAF_SCRIPT_PRIV_KEYS_HEX=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.taptree_return.leaf_script_priv_keys_hex' ) \ && export LEAF_SCRIPT_HEX=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.taptree_return.leaf_script_hex' ) \ && export CONTROL_BLOCK_HEX=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.taptree_return.control_block_hex' ) \ && export FUNDING_SCRIPT_PUBKEY=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.script_pubkey_hex' ) \ && export P2TR_ADDR=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.bech32m_address' ) ----- . View tapscript used in target leaf of taptree: + ----- $ b-reg decodescript $LEAF_SCRIPT_HEX | jq -r '.asm' ----- + NOTE: Notice that this script commits to a Schnorr 32-byte x-only public key. . fund this P2TR address with the coinbase reward of a newly generated block: + ----- $ export COINBASE_REWARD_TX_ID=$( b-reg -named generatetoaddress 1 $P2TR_ADDR 5 | jq -r '.[]' ) \ && echo $COINBASE_REWARD_TX_ID ----- + NOTE: Sometimes Bitcoin Core may not hit a block (even on regtest). If so, just try the above command again. . view summary of all txs that have funded P2TR address + ----- $ export P2TR_DESC=$( b-reg getdescriptorinfo "addr($P2TR_ADDR)" | jq -r '.descriptor' ) \ && echo $P2TR_DESC \ && b-reg scantxoutset start '[{"desc": "'''$P2TR_DESC'''"}]' ----- . grab txid of first tx with unspent funds: + ----- $ export FUNDING_TX_ID=$( b-reg scantxoutset start '[{"desc": "'''$P2TR_DESC'''"}]' | jq -r '.unspents[0].txid' ) \ && echo $FUNDING_TX_ID ----- . Set FUNDING_UTXO_INDEX env var (used later to correctly identify funding UTXO when generating the spending tx) + ----- $ export FUNDING_UTXO_INDEX=0 ----- . view details of funding UTXO to the P2TR address: + ----- $ export FUNDING_UTXO=$( b-reg getrawtransaction $FUNDING_TX_ID 1 | jq -r '.vout['''$FUNDING_UTXO_INDEX''']' ) \ && echo $FUNDING_UTXO | jq -r . ----- + NOTE: the above only works when Bitcoin Core is started with the following arg: -txindex == Spend P2TR UTXO . Determine value (in sats) of funding utxo: + ----- $ export FUNDING_UTXO_AMOUNT_SATS=$(echo $FUNDING_UTXO | jq -r '.value' | awk '{printf "%.0f", $1 * 100000000}') \ && echo $FUNDING_UTXO_AMOUNT_SATS ----- . Generate additional blocks. + This is necessary if you have only previously generated less than 100 blocks. + ----- $ b-reg -generate 110 ----- + Otherwise, you may see an error from bitcoin core such as the following when attempting to spend: + _bad-txns-premature-spend-of-coinbase, tried to spend coinbase at depth 1_ . Referencing the funding tx (via $FUNDING_TX_ID and $FUNDING_UTXO_INDEX), create the spending tx: + ----- $ export SPEND_DETAILS=$( cargo run --example p2tr_spend ) $ export RAW_P2TR_SPEND_TX=$( echo $SPEND_DETAILS | jq -r '.tx_hex' ) \ && echo "RAW_P2TR_SPEND_TX = $RAW_P2TR_SPEND_TX" \ && export SIG_HASH=$( echo $SPEND_DETAILS | jq -r '.sighash' ) \ && echo "SIG_HASH = $SIG_HASH" \ && export SIG_BYTES=$( echo $SPEND_DETAILS | jq -r '.sig_bytes' ) \ && echo "SIG_BYTES = $SIG_BYTES" ----- . Inspect the spending tx: + ----- $ b-reg decoderawtransaction $RAW_P2TR_SPEND_TX ----- . Test standardness of the spending tx by sending to local mempool of p2tr enabled Bitcoin Core: ----- $ b-reg testmempoolaccept '["'''$RAW_P2TR_SPEND_TX'''"]' ----- . Submit tx: + ----- $ export P2TR_SPENDING_TX_ID=$( b-reg sendrawtransaction $RAW_P2TR_SPEND_TX ) \ && echo $P2TR_SPENDING_TX_ID ----- + NOTE: Should return same tx id as was included in $RAW_P2TR_SPEND_TX == Mine P2TR Spend TX . View tx in mempool: + ----- $ b-reg getrawtransaction $P2TR_SPENDING_TX_ID 1 ----- + NOTE: There will not yet be a field `blockhash` in the response. . Mine 1 block: + ----- $ b-reg -generate 1 ----- . Obtain `blockhash` field of mined tx: + ----- $ export BLOCK_HASH=$( b-reg getrawtransaction $P2TR_SPENDING_TX_ID 1 | jq -r '.blockhash' ) \ && echo $BLOCK_HASH ----- . View tx in block: + ----- $ b-reg getblock $BLOCK_HASH | jq -r .tx ----- == TO-DO