:scrollbar:
:data-uri:
:toc2:
:linkattrs:

= P2MR End-to-End Tutorial

:numbered:

This tutorial is inspired by 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 P2MR UTXO to a P2WPKH address.

In addition, this tutorial allows for the (un)locking mechanism of the script to optionally use _Post Quantum Cryptography_ (PQC).

The purpose of this tutorial is to demonstrate construction and spending of a link:https://github.com/cryptoquick/bips/blob/p2qrh/bip-0360.mediawiki[bip-360] `p2mr` UTXO (optionally using _Post-Quantum Cryptography_).

The steps outlined in this tutorial are executed using a custom Bitcoin Core instance running either in `regtest` or `signet`.

== Pre-reqs

=== Bitcoin Core

If participating in a workshop, your instructor will provide a bitcoin environment.
Related: your instructor should also provide you with a wallet.

Otherwise, if running this tutorial on your own, follow the instructions in the appendix of this doc:  <<build_p2mr>>.


=== Shell Environment

. *docker / podman*
+
NOTE:  If you have built the custom `p2mr` enabled Bitcoin Core, you do not need docker (nor podman) installed.  Skip this section.
+
This tutorial makes use of a `p2mr` enabled _bitcoin-cli_ utility.
This utility is made available as a docker (or podman) container.
Ensure your host machine has either docker or podman installed.

. *bitcoin-cli* command line utility:
+
NOTE: If you have built the custom `p2mr` enabled Bitcoin Core, you can simply use the `bitcoin-cli` utility found in the `build/bin/` directory.  No need to use the _dockerized_ utility described below.

.. You will need a `bitcoin-cli` binary that is `p2mr` enabled.
For this purpose, a docker container with this `bitcoin-cli` utility is provided:
+
-----
docker pull quay.io/jbride2000/bitcoin-cli:p2mr-pqc-0.0.1
-----

.. Configure an alias to the `bitcoin-cli` command that connects to your customized bitcoin-core node.
+
-----
alias b-cli='docker run --rm  --network host bitcoin-cli:p2mr-pqc-0.0.1 -rpcconnect=192.168.122.1 -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest'
-----

. *jq*: ensure json parsing utility is link:https://jqlang.org/download/[installed] and available via your $PATH.
. *awk* : standard utility for all Linux distros (often packaged as `gawk`).
. *Rust* development environment with _cargo_ utility.  Use link:https://rustup[Rustup] to install.

== Create & Fund P2MR UTXO

The purpose of this workshop is to demonstrate construction and spending of a link:https://github.com/cryptoquick/bips/blob/p2qrh/bip-0360.mediawiki[bip-360] _P2MR_ address (optionally using _Post-Quantum Cryptography_).

In this section of the workshop, you create and fund a P2MR address.

The following depicts the construction of a P2MR _TapTree_ and computation its _scriptPubKey_.

image::images/p2mr_construction.png[]

A P2MR address is created by adding locking scripts to leaves of a _TapTree_.
The locking scripts can use either _Schnorr_ (as per BIP-360) or _SLH-DSA_ (defined in a future BIP) cryptography.

. Set an environment variable specific to your Bitcoin network environment (regtest, signet, etc)
+
[source,bash]
-----
export BITCOIN_NETWORK=regtest
-----
+
Doing so influences the P2MR address that you'll create later in this tutorial.


. Define number of total leaves in tap tree :
+
[source,bash]
-----
export TOTAL_LEAF_COUNT=5
-----

. OPTIONAL: Indicate what type of cryptography to use in the locking scripts of your TapTree leaves.
Valid options are: `MIXED`, `SCHNORR_ONLY`, `SLH_DSA_ONLY`, and `CONCATENATED_SCHNORR_AND_SLH_DSA`.
Default is `MIXED`.
+
[source,bash]
-----
export TAP_TREE_LOCK_TYPE=MIXED
-----

.. If you set _TAP_TREE_LOCK_TYPE=SCHNORR_ONLY_, then the locking script of your TapTree leaves will utilize _Schnorr_ cryptography.
+
Schnorr is not quantum-resistant.  However, its signature size is relatively small: 64 bytes.
A _SCHNORR_ONLY_ tap tree with 5 leaves (aka: TOTAL_LEAF_COUNT) could be represented as follows:
+
image::images/tap_tree_schnorr_only.png[s,300]

.. If you set _TAP_TREE_LOCK_TYPE=SLH_DSA_ONLY_, then the locking script of your TapTree leaves will utilize _SLH-DSA_ cryptography.
A _SLH_DSA_ONLY_ tap tree with 5 leaves (aka: TOTAL_LEAF_COUNT) could be represented as follows:
+
image::images/tap_tree_slh_dsa_only.png[l,300]
+
SLH_DSA is quantum-resistant.  However, the trade-off is the much larger signature size 7,856 bytes when spending.
+
image::images/crypto_key_characteristics.png[]
+
NOTE: PQC cryptography is made available to this BIP-360 reference implementation via the link:https://crates.io/crates/bitcoinpqc[libbitcoinpqc Rust bindings].

.. If you set _MIXED_, then each leaf of the taptree will consist of *either* a Schnorr based locking script or a SLH-DSA based locking script.
A _MIXED_ tap tree with 5 leaves (aka: TOTAL_LEAF_COUNT) could be represented as follows:
+
image::images/tap_tree_mixed.png[t,300]
+
NOTE:  The benefit of constructing a taptree with a mixed set of cryptography used as locking scripts in the leaves is articulated nicely in link:https://www.bitmex.com/blog/Taproot%20Quantum%20Spend%20Paths[this article from BitMex].

.. If you set _TAP_TREE_LOCK_TYPE=CONCATENATED_SCHNORR_AND_SLH_DSA_, then the locking script of your TapTree leaves will be secured using both SCHNORR and SLH-DSA cryptography in a concatenated / serial manner.
Private keys for both SCHNORR and SLH-DSA will be needed when unlocking.
A _CONCATENATED_SCHNORR_AND_SLH_DSA_ tap tree with 5 leaves (aka: TOTAL_LEAF_COUNT) could be represented as follows:
+
image::images/tap_tree_concatenated.png[p,300]

. Set the tap leaf index to later use as the unlocking script (when spending) For example, to later spend from the 5th leaf of the tap tree:
+
[source,bash]
-----
export LEAF_TO_SPEND_FROM=4
-----

. Generate a P2MR scripPubKey with multi-leaf taptree:
+
[source,bash]
-----
export BITCOIN_ADDRESS_INFO=$( cargo run --example p2mr_construction ) \
    && echo $BITCOIN_ADDRESS_INFO | jq -r .
-----
+
NOTE:  In `regtest`, you can expect a P2MR address that starts with: `bcrt1z` .
+
[subs=+quotes]
++++
<details>
<summary><b>What just happened?</b></summary>
The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction with a `p2mr` UTXO as follows:

<ul>
  <li>A configurable number of leaves are generated each with their own locking script.</li>
  <li>Each of these leaves are added to a Huffman tree that sorts the leaves by weight.</li>
  <li>The merkle root of the tree is calculated and subsequently used to generate the p2mr witness program and BIP0350 address.</li>
</ul>
</p>
The source code for the above logic is found in this project:  src/lib.rs

</details>
++++

.  Only if you previously set `TAP_TREE_LOCK_TYPE=MIXED`, set the environment variable `SPENDING_LEAF_TYPE`.
Valid values are `SHNORR_ONLY` or `SLH_DSA_ONLY` based on the type of locking script that was used in the leaf you will spend from later in this lab.  The logs from the previous step tell you the appropriate value to set.  For instance:
+
-----
NOTE: TAP_TREE_LOCK_TYPE=MIXED requires setting SPENDING_LEAF_TYPE when spending (based on leaf_script_type in output above) as follows:
    export SPENDING_LEAF_TYPE=SCHNORR_ONLY
-----

. Set some env vars (for use in later steps in this tutorial) based on previous result:
+
[source,bash]
-----
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 P2MR_ADDR=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.bech32m_address' )
-----

. View tapscript used in target leaf of taptree:
+
[source,bash]
-----
b-cli decodescript $LEAF_SCRIPT_HEX | jq -r '.asm'
-----
+
NOTE:  If not using PQC, notice that this script commits to a Schnorr 32-byte x-only public key.
If using PQC, this script commits to a Schnorr 32-byte SLH-DSA pub key and a OP_SUCCESS127 (represented as `OP_SUBSTR`) opcode.

. Fund this P2MR address with the coinbase reward of a newly generated block:
+
Choose from one of the following networks:

.. Regtest
+
If on `regtest` network, execute the following:
+
[source,bash]
-----
export COINBASE_REWARD_TX_ID=$( b-cli -named generatetoaddress nblocks=1 address="$P2MR_ADDR" maxtries=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.

.. Signet
+
If on `signet` network, then execute the following:
+
[source,bash]
-----
$BITCOIN_SOURCE_DIR/contrib/signet/miner --cli "bitcoin-cli -conf=$HOME/anduro-360/configs/bitcoin.conf.signet" generate \
    --address $P2MR_ADDR \
    --grind-cmd "$BITCOIN_SOURCE_DIR/build/bin/bitcoin-util grind" \
    --min-nbits --set-block-time $(date +%s) \
    --poolid "MARA Pool"
-----

. view summary of all txs that have funded P2MR address
+
[source,bash]
-----
export P2MR_DESC=$( b-cli getdescriptorinfo "addr($P2MR_ADDR)" | jq -r '.descriptor' ) \
    && echo $P2MR_DESC \
    && b-cli scantxoutset start '[{"desc": "'''$P2MR_DESC'''"}]'
-----

. grab txid of first tx with unspent funds:
+
[source,bash]
-----
export FUNDING_TX_ID=$( b-cli scantxoutset start '[{"desc": "'''$P2MR_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)
+
[source,bash]
-----
export FUNDING_UTXO_INDEX=0
-----

. view details of funding UTXO to the P2MR address:
+
[source,bash]
-----
export FUNDING_UTXO=$( b-cli 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 P2MR UTXO

In the previous section, you created and funded a P2MR UTXO.
That UTXO includes a leaf script locked with a key-pair (optionally based on PQC) known to you.

In this section, you spend from that P2MR UTXO.
Specifically, you will generate an appropriate _SigHash_ and sign it (to create a signature) using the known private key that unlocks the known leaf script of the P2MR UTXO.

For the purpose of this tutorial, you will spend funds to a new P2WPKH utxo.  (there is nothing novel about this P2WPKH utxo).


. Determine value (in sats) of the funding P2MR utxo:
+
[source,bash]
-----
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.
+
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_

.. regtest
+
[source,bash]
-----
b-cli -generate 110
-----

.. signet
+
This will involve having the signet miner generate about 110 blocks .... which can take about 10 minutes.
+
The `common/utils` directory of this project provides a script called: link:../../common/utils/signet_miner_loop.sh[signet_miner_loop.sh].


. Referencing the funding tx (via $FUNDING_TX_ID and $FUNDING_UTXO_INDEX), create the spending tx:
+
[source,bash]
-----
export SPEND_DETAILS=$( cargo run --example p2mr_spend )
-----
+
[subs=+quotes]
++++
<details>
<summary><b>What just happened?</b></summary>
The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction that spends from the `p2mr` UTXO as follows:

<ul>
  <li>Create a transaction template (aka: SigHash) that serves as the message to be signed.</li>
  <li>Using the known private key and the SigHash, create a signature that is capable of unlocking one of the leaf scripts of the P2MR tree.</li>
  <li>Add this signature to the witness section of the transaction.</li>
</ul>
</p>
The source code for the above logic is found in this project:  src/lib.rs

</details>
++++

. Set environment variables passed to _bitcoin-cli_ when spending:
+
[source,bash]
-----
export RAW_P2MR_SPEND_TX=$( echo $SPEND_DETAILS | jq -r '.tx_hex' ) \
        && echo "RAW_P2MR_SPEND_TX = $RAW_P2MR_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:
+
[source,bash]
-----
b-cli decoderawtransaction $RAW_P2MR_SPEND_TX
-----
+
Pay particular attention to the `vin.txinwitness` field.
Do the three elements (script input, script and control block) of the witness stack for this script path spend make sense ?
What do you observe as the first byte of the `control block` element ?

. Test standardness of the spending tx by sending to local mempool of p2mr enabled Bitcoin Core:
+
[source,bash]
-----
b-cli testmempoolaccept '["'''$RAW_P2MR_SPEND_TX'''"]'
-----

. Submit tx:
+
[source,bash]
-----
export P2MR_SPENDING_TX_ID=$( b-cli sendrawtransaction $RAW_P2MR_SPEND_TX ) \
    && echo $P2MR_SPENDING_TX_ID
-----
+
NOTE:  Should return same tx id as was included in $RAW_P2MR_SPEND_TX

== Mine P2MR Spend TX

. View tx in mempool:
+
[source,bash]
-----
b-cli getrawtransaction $P2MR_SPENDING_TX_ID 1
-----
+
NOTE:  There will not yet be a field `blockhash` in the response.

. Mine 1 block:

.. regtest:
+
[source,bash]
-----
b-cli -generate 1
-----

.. signet:
+
If on `signet` network, then execute the following:
+
[source,bash]
-----
$BITCOIN_SOURCE_DIR/contrib/signet/miner --cli "bitcoin-cli -conf=$HOME/anduro-360/configs/bitcoin.conf.signet" generate \
    --address $P2MR_ADDR \
    --grind-cmd "$BITCOIN_SOURCE_DIR/build/bin/bitcoin-util grind" \
    --min-nbits --set-block-time $(date +%s) \
    --poolid "MARA Pool"
-----

. Obtain `blockhash` field of mined tx:
+
[source,bash]
-----
export BLOCK_HASH=$( b-cli getrawtransaction $P2MR_SPENDING_TX_ID 1 | jq -r '.blockhash' ) \
    && echo $BLOCK_HASH
-----

. View tx in block:
+
[source,bash]
-----
b-cli getblock $BLOCK_HASH | jq -r .tx
-----

== Appendix

[[build_p2mr]]
=== Build P2MR / PQC Enabled Bitcoin Core

The link:https://github.com/jbride/bitcoin/tree/p2mr[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:

. Set BITCOIN_SOURCE_DIR
+
-----
export BITCOIN_SOURCE_DIR=/path/to/root/dir/of/cloned/bitcoin/source
-----

. build
+
-----
cmake -B build \
        -DWITH_ZMQ=ON \
        -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
        -DBUILD_BENCH=ON \
        -DBUILD_DAEMON=ON \
        -DSANITIZERS=address,undefined

cmake --build build -j$(nproc)
-----

. run in either `regtest` or `signet` mode:

.. regtest:
+
-----
./build/bin/bitcoind -daemon=0 -regtest=1 -txindex -prune=0
-----

.. signet:
+
-----
./build/bin/bitcoind -daemon=0 -signet=1 -txindex -prune=0
-----
+
NOTE: If running in `signet`, your bitcoin core will need to be configured with the `signetchallenge` property.
link:https://edil.com.br/blog/creating-a-custom-bitcoin-signet[This tutorial] provides a nice overview of the topic.

=== libbitcoinpqc build

The `p2mr-pqc` branch of this project includes a dependency on the link:https://crates.io/crates/libbitcoinpqc[libbitcoinpqc crate].
libbitcoinpqc contains native code (C/C++/ASM) and is made available to Rust projects via Rust bindings.
This C/C++/ASM code is provided in the libbitcoinpqc crate as source code (not prebuilt binaries).

Subsequently, the `Cargo` utility needs to build this libbitcoinpqc C native code on your local machine.
You will need to have C development related libraries installed on your local machine.

Every developer or CI machine building `p2mr-ref` must have cmake and a C toolchain installed locally.

==== Linux

. Debian / Ubuntu
+
-----
sudo apt update
sudo apt install cmake build-essential clang libclang-dev
-----

. Fedora / RHEL
+
-----
sudo dnf5 update
sudo dnf5 install cmake make gcc gcc-c++ clang clang-libs llvm-devel
-----

==== OSX

[[bitcoin_core_wallet]]
=== Bitcoin Core Wallet

This tutorial assumes that a bitcoin core wallet is available.

. For example, the following would be sufficient:
+
-----

export W_NAME=anduro

b-cli -named createwallet \
    wallet_name=$W_NAME \
    descriptors=true \
    load_on_startup=true
-----

=== Schnorr + SLH-DSA

-----
<SCHNORR Pub Key> OP_CHECKSIG <SLH-DSA pub key> OP_SUBSTR OP_BOOLAND OP_VERIFY
-----


The logic flow is:

. <SCHNORR Pub Key> OP_CHECKSIG: Verify Schnorr signature against Schnorr pubkey
. <SLH-DSA pub key> OP_SUBSTR: Verify SLH-DSA signature against SLH-DSA pubkey (using OP_SUBSTR for the SLH-DSA verification)
. OP_BOOLAND: Ensure both signature verifications succeeded
. OP_VERIFY: Final verification that the script execution succeeded
. This creates a "both signatures required" locking condition, which is exactly what you want for SCHNORR_AND_SLH_DSA scripts.


===== Sighash bytes

Sighash bytes are appended to each signature, instead of being separate witness elements:

. SlhDsaOnly: SLH-DSA signature + sighash byte appended
. SchnorrOnly: Schnorr signature + sighash byte appended
. SchnorrAndSlhDsa: Schnorr signature (no sighash) + SLH-DSA signature + sighash byte appended to the last signature
