1
0
mirror of https://github.com/bitcoin/bips.git synced 2026-03-09 15:53:54 +00:00
Files
bips/bip-0360/ref-impl/rust/docs/p2tr-end-to-end.adoc
Hunter Beast eae7d9fc57 BIP360: Pay to Merkle Root (P2MR) (#1670)
Review comments and assistance by:
  Armin Sabouri <armins88@gmail.com>
  D++ <82842780+dplusplus1024@users.noreply.github.com>
  Jameson Lopp <jameson.lopp@gmail.com>
  jbride <jbride2001@yahoo.com>
  Joey Yandle <xoloki@gmail.com>
  Jon Atack <jon@atack.com>
  Jonas Nick <jonasd.nick@gmail.com>
  Kyle Crews <kylecrews@Kyles-Mac-Studio.local>
  Mark "Murch" Erhardt <murch@murch.one>
  notmike-5 <notmike-5@users.noreply.github.com>
  Vojtěch Strnad <43024885+vostrnad@users.noreply.github.com>

Co-authored-by: Ethan Heilman <ethan.r.heilman@gmail.com>
Co-authored-by: Isabel Foxen Duke <110147802+Isabelfoxenduke@users.noreply.github.com>
2026-02-11 13:01:47 -08:00

237 lines
6.7 KiB
Plaintext

: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