diff --git a/bip-0360/ref-impl/common/tests/data/p2tsh_construction.json b/bip-0360/ref-impl/common/tests/data/p2mr_construction.json
similarity index 83%
rename from bip-0360/ref-impl/common/tests/data/p2tsh_construction.json
rename to bip-0360/ref-impl/common/tests/data/p2mr_construction.json
index 7729217bcc..f6d76d07ca 100644
--- a/bip-0360/ref-impl/common/tests/data/p2tsh_construction.json
+++ b/bip-0360/ref-impl/common/tests/data/p2mr_construction.json
@@ -2,20 +2,37 @@
"version": 1,
"test_vectors": [
{
- "id": "p2tsh_missing_leaf_script_tree_error",
- "objective": "Tests P2TSH with missing leaf script tree",
+ "id": "p2tr_using_v2_witness_version_error",
+ "objective": "Tests that a P2TR v2 scriptPubKey fails with use of witness version 2",
+ "given": {
+ "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d",
+ "scriptTree": null
+ },
+ "intermediary": {
+ "merkleRoot": null,
+ "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70",
+ "tweakedPubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343"
+ },
+ "expected": {
+ "scriptPubKey": "522053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",
+ "error": "P2TR requires witness version of 1"
+ }
+ },
+ {
+ "id": "p2mr_missing_leaf_script_tree_error",
+ "objective": "Tests P2MR with missing leaf script tree",
"given": {
"script_tree": ""
},
"intermediary": {
},
"expected": {
- "error": "P2TSH requires a script tree with at least one leaf"
+ "error": "P2MR requires a script tree with at least one leaf"
}
},
{
- "id": "p2tsh_single_leaf_script_tree",
- "objective": "Tests P2TSH with single leaf script tree",
+ "id": "p2mr_single_leaf_script_tree",
+ "objective": "Tests P2MR with single leaf script tree",
"given": {
"scriptTree": {
"id": 0,
@@ -39,8 +56,8 @@
}
},
{
- "id": "p2tsh_different_version_leaves",
- "objective": "Tests P2TSH with two script leaves of different versions. TO-DO: currently ignores given leaf version and over-rides. Probably better to throw error",
+ "id": "p2mr_different_version_leaves",
+ "objective": "Tests P2MR with two script leaves of different versions. TO-DO: currently ignores given leaf version and over-rides. Probably better to throw error",
"given": {
"scriptTree": [
{
@@ -75,8 +92,8 @@
}
},
{
- "id": "p2tsh_simple_lightning_contract",
- "objective": "Tests P2TSH with two script leaves that simulate a simple lightning network contract. Reference: https://github.com/bitcoin-core/btcdeb/blob/master/doc/tapscript-example-with-tap.md",
+ "id": "p2mr_simple_lightning_contract",
+ "objective": "Tests P2MR with two script leaves that simulate a simple lightning network contract. Reference: https://github.com/bitcoin-core/btcdeb/blob/master/doc/tapscript-example-with-tap.md",
"given": {
"scriptTree": [
{
@@ -112,8 +129,8 @@
}
},
{
- "id": "p2tsh_two_leaf_same_version",
- "objective": "Tests P2TSH with two script leaves of same version",
+ "id": "p2mr_two_leaf_same_version",
+ "objective": "Tests P2MR with two script leaves of same version",
"given": {
"scriptTree": [
{
@@ -148,8 +165,8 @@
}
},
{
- "id": "p2tsh_three_leaf_complex",
- "objective": "Tests P2TSH with a complex three-leaf script tree structure, demonstrating nested script paths and multiple verification options",
+ "id": "p2mr_three_leaf_complex",
+ "objective": "Tests P2MR with a complex three-leaf script tree structure, demonstrating nested script paths and multiple verification options",
"given": {
"internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",
"scriptTree": [
@@ -194,8 +211,8 @@
}
},
{
- "id": "p2tsh_three_leaf_alternative",
- "objective": "Tests another variant of P2TSH with three leaves arranged in a different tree structure, showing alternative script path spending options",
+ "id": "p2mr_three_leaf_alternative",
+ "objective": "Tests another variant of P2MR with three leaves arranged in a different tree structure, showing alternative script path spending options",
"given": {
"internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",
"scriptTree": [
diff --git a/bip-0360/ref-impl/common/tests/data/p2tsh_pqc_construction.json b/bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json
similarity index 90%
rename from bip-0360/ref-impl/common/tests/data/p2tsh_pqc_construction.json
rename to bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json
index 54f9ae6293..86a7f2fca7 100644
--- a/bip-0360/ref-impl/common/tests/data/p2tsh_pqc_construction.json
+++ b/bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json
@@ -2,20 +2,20 @@
"version": 1,
"test_vectors": [
{
- "id": "p2tsh_missing_leaf_script_tree_error",
- "objective": "Tests P2TSH with missing leaf script tree",
+ "id": "p2mr_missing_leaf_script_tree_error",
+ "objective": "Tests P2MR with missing leaf script tree",
"given": {
"script_tree": ""
},
"intermediary": {
},
"expected": {
- "error": "P2TSH requires a script tree with at least one leaf"
+ "error": "P2MR requires a script tree with at least one leaf"
}
},
{
- "id": "p2tsh_single_leaf_script_tree",
- "objective": "Tests P2TSH with single leaf script tree",
+ "id": "p2mr_single_leaf_script_tree",
+ "objective": "Tests P2MR with single leaf script tree",
"given": {
"scriptTree": {
"id": 0,
@@ -40,8 +40,8 @@
}
},
{
- "id": "p2tsh_different_version_leaves",
- "objective": "Tests P2TSH with two script leaves of different versions. TO-DO: currently ignores given leaf version and over-rides. Probably better to throw error",
+ "id": "p2mr_different_version_leaves",
+ "objective": "Tests P2MR with two script leaves of different versions. TO-DO: currently ignores given leaf version and over-rides. Probably better to throw error",
"given": {
"scriptTree": [
{
@@ -77,8 +77,8 @@
}
},
{
- "id": "p2tsh_simple_lightning_contract",
- "objective": "Tests P2TSH with two script leaves that simulate a simple lightning network contract. Reference: https://github.com/bitcoin-core/btcdeb/blob/master/doc/tapscript-example-with-tap.md",
+ "id": "p2mr_simple_lightning_contract",
+ "objective": "Tests P2MR with two script leaves that simulate a simple lightning network contract. Reference: https://github.com/bitcoin-core/btcdeb/blob/master/doc/tapscript-example-with-tap.md",
"given": {
"scriptTree": [
{
@@ -116,8 +116,8 @@
}
},
{
- "id": "p2tsh_two_leaf_same_version",
- "objective": "Tests P2TSH with two script leaves of same version",
+ "id": "p2mr_two_leaf_same_version",
+ "objective": "Tests P2MR with two script leaves of same version",
"given": {
"scriptTree": [
{
@@ -153,8 +153,8 @@
}
},
{
- "id": "p2tsh_three_leaf_complex",
- "objective": "Tests P2TSH with a complex three-leaf script tree structure, demonstrating nested script paths and multiple verification options",
+ "id": "p2mr_three_leaf_complex",
+ "objective": "Tests P2MR with a complex three-leaf script tree structure, demonstrating nested script paths and multiple verification options",
"given": {
"scriptTree": [
{
@@ -201,8 +201,8 @@
}
},
{
- "id": "p2tsh_three_leaf_alternative",
- "objective": "Tests another variant of P2TSH with three leaves arranged in a different tree structure, showing alternative script path spending options",
+ "id": "p2mr_three_leaf_alternative",
+ "objective": "Tests another variant of P2MR with three leaves arranged in a different tree structure, showing alternative script path spending options",
"given": {
"scriptTree": [
{
diff --git a/bip-0360/ref-impl/common/utils/signet_miner_loop.sh b/bip-0360/ref-impl/common/utils/signet_miner_loop.sh
index b5882cb8e8..6ec65c4a40 100755
--- a/bip-0360/ref-impl/common/utils/signet_miner_loop.sh
+++ b/bip-0360/ref-impl/common/utils/signet_miner_loop.sh
@@ -2,8 +2,8 @@
# Invokes mining simulator a configurable number of times
-if [ -z "${P2TSH_ADDR}" ]; then
- echo "Error: Environment variable P2TSH_ADDR needs to be set"
+if [ -z "${P2MR_ADDR}" ]; then
+ echo "Error: Environment variable P2MR_ADDR needs to be set"
exit 1
fi
@@ -33,7 +33,7 @@ for ((i=1; i<=LOOP_COUNT; i++))
do
echo "Iteration $i of $LOOP_COUNT"
$BITCOIN_SOURCE_DIR/contrib/signet/miner --cli "bitcoin-cli -conf=$BITCOIN_CONF_FILE_PATH" generate \
- --address $P2TSH_ADDR \
+ --address $P2MR_ADDR \
--grind-cmd "$BITCOIN_SOURCE_DIR/build/bin/bitcoin-util grind" \
--poolid "$POOL_ID" \
--min-nbits --set-block-time $(date +%s)
diff --git a/bip-0360/ref-impl/common/utils/workshop/Dockerfile.bcli b/bip-0360/ref-impl/common/utils/workshop/Dockerfile.bcli
index 4c03bc149b..2060accaa5 100644
--- a/bip-0360/ref-impl/common/utils/workshop/Dockerfile.bcli
+++ b/bip-0360/ref-impl/common/utils/workshop/Dockerfile.bcli
@@ -1,5 +1,5 @@
-# podman build -f Dockerfile.bcli -t quay.io/jbride2000/p2tsh_bcli:0.1 .
-# podman run -it --entrypoint /bin/bash quay.io/jbride200/p2tsh_bcli:0.1
+# podman build -f Dockerfile.bcli -t quay.io/jbride2000/p2mr_bcli:0.1 .
+# podman run -it --entrypoint /bin/bash quay.io/jbride200/p2mr_bcli:0.1
FROM rust:1-slim-bookworm AS builder
@@ -28,7 +28,7 @@ WORKDIR /bitcoin
# Copy Bitcoin Core source (or clone)
# COPY . /bitcoin
-RUN git clone --branch p2tsh-pqc --single-branch https://github.com/jbride/bitcoin.git
+RUN git clone --branch p2mr-pqc --single-branch https://github.com/jbride/bitcoin.git
# Environment variables for musl
ENV CC=gcc
diff --git a/bip-0360/ref-impl/common/utils/workshop/Dockerfile.full b/bip-0360/ref-impl/common/utils/workshop/Dockerfile.full
index 2e047f85c0..8ee222e714 100644
--- a/bip-0360/ref-impl/common/utils/workshop/Dockerfile.full
+++ b/bip-0360/ref-impl/common/utils/workshop/Dockerfile.full
@@ -1,5 +1,5 @@
-# podman build -f Dockerfile.full -t quay.io/jbride2000/p2tsh_demo:0.1 .
-# podman run -it --entrypoint /bin/bash quay.io/jbride2000/p2tsh_demo:0.1
+# podman build -f Dockerfile.full -t quay.io/jbride2000/p2mr_demo:0.1 .
+# podman run -it --entrypoint /bin/bash quay.io/jbride2000/p2mr_demo:0.1
FROM rust:1-slim-bookworm AS builder
@@ -28,7 +28,7 @@ WORKDIR /bitcoin
# Copy Bitcoin Core source (or clone)
# COPY . /bitcoin
-RUN git clone --branch p2tsh-pqc --single-branch https://github.com/jbride/bitcoin.git
+RUN git clone --branch p2mr-pqc --single-branch https://github.com/jbride/bitcoin.git
# Environment variables for musl
ENV CC=gcc
@@ -96,7 +96,7 @@ USER bip360
WORKDIR /home/bip360
-RUN git clone --no-checkout --depth 1 --branch p2tsh-pqc \
+RUN git clone --no-checkout --depth 1 --branch p2mr-pqc \
--single-branch https://github.com/jbride/bips.git bips && \
cd bips && \
git sparse-checkout init --cone && \
diff --git a/bip-0360/ref-impl/js/README.adoc b/bip-0360/ref-impl/js/README.adoc
index 8d18999318..797ba5436b 100644
--- a/bip-0360/ref-impl/js/README.adoc
+++ b/bip-0360/ref-impl/js/README.adoc
@@ -13,6 +13,6 @@ $ npx tsc
# run tests
-$ node src/p2tsh-example.ts
+$ node src/p2mr-example.ts
$ node src/test-npm-pqc-package.js
-----
diff --git a/bip-0360/ref-impl/js/package-lock.json b/bip-0360/ref-impl/js/package-lock.json
index 2fd47283f6..453951f285 100644
--- a/bip-0360/ref-impl/js/package-lock.json
+++ b/bip-0360/ref-impl/js/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
- "@jbride/bitcoinjs-lib": "^7.0.0-rc.0-p2tsh-0.0",
+ "@jbride/bitcoinjs-lib": "^7.0.0-rc.0-p2mr-0.0",
"@jbride/bitcoinpqc-wasm": "^0.1.1",
"ecpair": "^3.0.0",
"tiny-secp256k1": "^2.2.4"
@@ -20,8 +20,8 @@
}
},
"node_modules/@jbride/bitcoinjs-lib": {
- "version": "7.0.0-rc.0-p2tsh-0.0",
- "resolved": "https://registry.npmjs.org/@jbride/bitcoinjs-lib/-/bitcoinjs-lib-7.0.0-rc.0-p2tsh-0.0.tgz",
+ "version": "7.0.0-rc.0-p2mr-0.0",
+ "resolved": "https://registry.npmjs.org/@jbride/bitcoinjs-lib/-/bitcoinjs-lib-7.0.0-rc.0-p2mr-0.0.tgz",
"integrity": "sha512-JRL0LTBVFBhfHhEciJi8I7C5jGoL24b07rwQthe+3UAGbv2LLOqKRYAD3dc5XD4Maanh1awV+MUELgc7OFgjYw==",
"license": "MIT",
"dependencies": {
diff --git a/bip-0360/ref-impl/js/package.json b/bip-0360/ref-impl/js/package.json
index f01277be61..4ec7be0f5b 100644
--- a/bip-0360/ref-impl/js/package.json
+++ b/bip-0360/ref-impl/js/package.json
@@ -15,7 +15,7 @@
"typescript": "^5.9.3"
},
"dependencies": {
- "@jbride/bitcoinjs-lib": "^7.0.0-rc.0-p2tsh-0.0",
+ "@jbride/bitcoinjs-lib": "^7.0.0-rc.0-p2mr-0.0",
"@jbride/bitcoinpqc-wasm": "^0.1.1",
"ecpair": "^3.0.0",
"tiny-secp256k1": "^2.2.4"
diff --git a/bip-0360/ref-impl/js/src/p2tsh-example.ts b/bip-0360/ref-impl/js/src/p2mr-example.ts
similarity index 84%
rename from bip-0360/ref-impl/js/src/p2tsh-example.ts
rename to bip-0360/ref-impl/js/src/p2mr-example.ts
index af009e9857..6eb20fb406 100644
--- a/bip-0360/ref-impl/js/src/p2tsh-example.ts
+++ b/bip-0360/ref-impl/js/src/p2mr-example.ts
@@ -1,5 +1,5 @@
-// src/p2tsh-example.ts
-// Example demonstrating P2TSH (Pay-to-Taproot-Script-Hash) address construction
+// src/p2mr-example.ts
+// Example demonstrating P2MR (Pay-to-Taproot-Script-Hash) address construction
import { payments } from '@jbride/bitcoinjs-lib';
import * as bitcoinCrypto from '@jbride/bitcoinjs-lib/src/crypto';
@@ -9,7 +9,7 @@ import ECPairFactory, { type ECPairInterface } from 'ecpair';
import * as ecc from 'tiny-secp256k1';
import { randomBytes } from 'crypto';
-const { p2tsh } = payments;
+const { p2mr } = payments;
// Initialize ECPair with the ECC library
const ECPair = ECPairFactory(ecc);
@@ -37,11 +37,11 @@ function signAndVerify(
}
/**
- * Example 1: Construct a P2TSH address from a script tree with a single leaf
+ * Example 1: Construct a P2MR address from a script tree with a single leaf
* This is the simplest case - a script tree containing one script.
*/
function example1_simpleScriptTree() {
- console.log('=== Example 1: P2TSH from simple script tree ===');
+ console.log('=== Example 1: P2MR from simple script tree ===');
// Generate a key pair
const keyPair = ECPair.makeRandom({ rng });
@@ -56,18 +56,18 @@ function example1_simpleScriptTree() {
output: script,
};
- // Construct the P2TSH payment
- const payment = p2tsh({
+ // Construct the P2MR payment
+ const payment = p2mr({
scriptTree: scriptTree,
});
console.log('Generated compressed pubkey:', pubkey.toString('hex'));
console.log('X-only pubkey:', Buffer.from(xOnlyPubkey).toString('hex'));
console.log('Script tree:', { output: bscript.toASM(script) });
- console.log('P2TSH Address:', payment.address);
+ console.log('P2MR Address:', payment.address);
console.log('Output script:', bscript.toASM(payment.output!));
console.log('Merkle root hash:', payment.hash ? Buffer.from(payment.hash).toString('hex') : undefined);
- const message = Buffer.from('P2TSH demo - example 1', 'utf8');
+ const message = Buffer.from('P2MR demo - example 1', 'utf8');
const result = signAndVerify(keyPair, xOnlyPubkey, message);
console.log('Message:', result.message.toString('utf8'));
@@ -80,11 +80,11 @@ function example1_simpleScriptTree() {
}
/**
- * Example 2: Construct a P2TSH address from a script tree with multiple leaves
+ * Example 2: Construct a P2MR address from a script tree with multiple leaves
* This demonstrates a more complex script tree structure.
*/
function example2_multiLeafScriptTree() {
- console.log('=== Example 2: P2TSH from multi-leaf script tree ===');
+ console.log('=== Example 2: P2MR from multi-leaf script tree ===');
// Generate two different key pairs for the leaves
const keyPair1 = ECPair.makeRandom({ rng });
@@ -103,8 +103,8 @@ function example2_multiLeafScriptTree() {
{ output: script2 },
];
- // Construct the P2TSH payment
- const payment = p2tsh({
+ // Construct the P2MR payment
+ const payment = p2mr({
scriptTree: scriptTree,
});
@@ -117,11 +117,11 @@ function example2_multiLeafScriptTree() {
console.log('Script tree leaves:');
console.log(' Leaf 1:', bscript.toASM(script1));
console.log(' Leaf 2:', bscript.toASM(script2));
- console.log('P2TSH Address:', payment.address);
+ console.log('P2MR Address:', payment.address);
console.log('Output script:', bscript.toASM(payment.output!));
console.log('Merkle root hash:', payment.hash ? Buffer.from(payment.hash).toString('hex') : undefined);
- const message1 = Buffer.from('P2TSH demo - example 2 leaf 1', 'utf8');
- const message2 = Buffer.from('P2TSH demo - example 2 leaf 2', 'utf8');
+ const message1 = Buffer.from('P2MR demo - example 2 leaf 1', 'utf8');
+ const message2 = Buffer.from('P2MR demo - example 2 leaf 2', 'utf8');
const result1 = signAndVerify(keyPair1, xOnlyPubkey1, message1);
const result2 = signAndVerify(keyPair2, xOnlyPubkey2, message2);
@@ -144,11 +144,11 @@ function example2_multiLeafScriptTree() {
}
/**
- * Example 4: Construct a P2TSH address from a hash and redeem script
- * This demonstrates creating a P2TSH when you have the hash directly.
+ * Example 4: Construct a P2MR address from a hash and redeem script
+ * This demonstrates creating a P2MR when you have the hash directly.
*/
function example3_fromHashAndRedeem() {
- console.log('=== Example 3: P2TSH from hash and redeem script ===');
+ console.log('=== Example 3: P2MR from hash and redeem script ===');
// Generate a key pair
const keyPair = ECPair.makeRandom({ rng });
@@ -162,8 +162,8 @@ function example3_fromHashAndRedeem() {
'hex',
);
- // Construct the P2TSH payment
- const payment = p2tsh({
+ // Construct the P2MR payment
+ const payment = p2mr({
hash: hash,
redeem: {
output: redeemScript,
@@ -174,9 +174,9 @@ function example3_fromHashAndRedeem() {
console.log('X-only pubkey:', Buffer.from(xOnlyPubkey).toString('hex'));
console.log('Redeem script:', bscript.toASM(redeemScript));
console.log('Hash:', hash.toString('hex'));
- console.log('P2TSH Address:', payment.address);
+ console.log('P2MR Address:', payment.address);
console.log('Output script:', bscript.toASM(payment.output!));
- const message = Buffer.from('P2TSH demo - example 3', 'utf8');
+ const message = Buffer.from('P2MR demo - example 3', 'utf8');
const result = signAndVerify(keyPair, xOnlyPubkey, message);
console.log('Message:', result.message.toString('utf8'));
@@ -189,7 +189,7 @@ function example3_fromHashAndRedeem() {
}
// Run all examples
-console.log('P2TSH Address Construction Examples\n');
+console.log('P2MR Address Construction Examples\n');
console.log('=====================================\n');
example1_simpleScriptTree();
diff --git a/bip-0360/ref-impl/rust/Cargo.lock b/bip-0360/ref-impl/rust/Cargo.lock
index ebdf473ddc..e635f4bd5a 100644
--- a/bip-0360/ref-impl/rust/Cargo.lock
+++ b/bip-0360/ref-impl/rust/Cargo.lock
@@ -2,18 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "ahash"
-version = "0.8.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
-dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -101,44 +89,6 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-[[package]]
-name = "bdk_chain"
-version = "0.23.2-pqc-0.1"
-source = "sparse+https://crates.denver.space/api/v1/crates/"
-checksum = "6ea956ce13678893de26ffce4ead2f28ac1748ccb8058341bf839616cd357ccf"
-dependencies = [
- "bdk_core",
- "bitcoin",
- "miniscript",
- "serde",
-]
-
-[[package]]
-name = "bdk_core"
-version = "0.6.2"
-source = "sparse+https://crates.denver.space/api/v1/crates/"
-checksum = "a31fd2c38b90b16d97da383ff281f3f5ae636a7767cb067af6c0d90364c30fb6"
-dependencies = [
- "bitcoin",
- "hashbrown",
- "serde",
-]
-
-[[package]]
-name = "bdk_wallet"
-version = "3.0.0-alpha.0-pqc-0.1"
-source = "sparse+https://crates.denver.space/api/v1/crates/"
-checksum = "e7687f76ee256115a2778e4b115bad9b439b87a8fccd52c4358c0f532482cf4c"
-dependencies = [
- "bdk_chain",
- "bitcoin",
- "bitcoinpqc",
- "miniscript",
- "rand_core 0.6.4",
- "serde",
- "serde_json",
-]
-
[[package]]
name = "bech32"
version = "0.11.0"
@@ -166,10 +116,25 @@ dependencies = [
]
[[package]]
-name = "bitcoin"
-version = "0.32.6"
+name = "bitcoin-internals"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitcoin-io"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
+
+[[package]]
+name = "bitcoin-p2mr-pqc"
+version = "0.32.6-p2mr-pqc.1"
source = "sparse+https://crates.denver.space/api/v1/crates/"
-checksum = "b1831b23596c0d9d0d3cb01b059027718add9748f4c1167455c748d632124379"
+checksum = "ce8a80e619111bf8d228f3f1b169a2487e6e28974f39c8935657640e0214f9ed"
dependencies = [
"base58ck",
"base64",
@@ -184,21 +149,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "bitcoin-internals"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "bitcoin-io"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
-
[[package]]
name = "bitcoin-units"
version = "0.1.2"
@@ -235,6 +185,21 @@ dependencies = [
"serde",
]
+[[package]]
+name = "bitcoinpqc"
+version = "0.3.0"
+source = "sparse+https://crates.denver.space/api/v1/crates/"
+checksum = "5fbdb2a3ebd6701c141909f4b7f16165b848481c5c3f37a8bd7b273304ead5a6"
+dependencies = [
+ "bindgen",
+ "bitmask-enum",
+ "cmake",
+ "hex",
+ "libc",
+ "secp256k1 0.31.1",
+ "serde",
+]
+
[[package]]
name = "bitflags"
version = "2.9.4"
@@ -366,16 +331,6 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash",
- "serde",
-]
-
[[package]]
name = "hex"
version = "0.4.3"
@@ -478,14 +433,13 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniscript"
-version = "13.0.0-pqc-0.2"
+version = "13.0.0-p2mr-pqc-1.0"
source = "sparse+https://crates.denver.space/api/v1/crates/"
-checksum = "4cf5cd2ed677044868fdcfbbc28175d535f5086524674f85938c474258bc60aa"
+checksum = "aae6f58a3f729d916bf610da66391345706d36115a346efd0041be21c4fa058f"
dependencies = [
"bech32",
- "bitcoin",
- "bitcoinpqc",
- "serde",
+ "bitcoin-p2mr-pqc",
+ "bitcoinpqc 0.2.0",
]
[[package]]
@@ -511,13 +465,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
-name = "p2tsh-ref"
+name = "p2mr-ref"
version = "0.1.0"
dependencies = [
"anyhow",
- "bdk_wallet",
- "bitcoin",
- "bitcoinpqc",
+ "bitcoin-p2mr-pqc",
+ "bitcoinpqc 0.3.0",
"env_logger",
"hex",
"log",
@@ -821,12 +774,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
diff --git a/bip-0360/ref-impl/rust/Cargo.toml b/bip-0360/ref-impl/rust/Cargo.toml
index c3ddb0c961..1c94d5f46b 100644
--- a/bip-0360/ref-impl/rust/Cargo.toml
+++ b/bip-0360/ref-impl/rust/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "p2tsh-ref"
+name = "p2mr-ref"
version = "0.1.0"
edition = "2024"
@@ -8,13 +8,12 @@ edition = "2024"
# Dev version of miniscript crate re-exports bitcoin 0.32.6
# view configuration for "kellnr-denver-space":
# cat .cargo/config.toml
-miniscript = { version="=13.0.0-pqc-0.2", registry="kellnr-denver-space" }
-bitcoin = { version="0.32.6", features = ["rand-std", "serde", "base64"], registry = "kellnr-denver-space" }
+miniscript = { version="=13.0.0-p2mr-pqc-1.0", registry="kellnr-denver-space" }
+bitcoin = { package = "bitcoin-p2mr-pqc", version="0.32.6-p2mr-pqc.1", features = ["rand-std", "serde", "base64"], registry = "kellnr-denver-space" }
+bitcoinpqc = { version="0.3.0", features = ["serde"], registry="kellnr-denver-space" }
-bitcoinpqc = { version="0.2.0", features = ["serde"] }
-
-# BDK Wallet with P2TSH support
-bdk_wallet = { version = "=3.0.0-alpha.0-pqc-0.1", registry = "kellnr-denver-space" }
+# BDK Wallet with P2MR support
+#bdk_wallet = { version = "=3.0.0-alpha.0-pqc-0.1", registry = "kellnr-denver-space" }
env_logger = "0.11.5"
log = "0.4.22"
@@ -28,7 +27,7 @@ rand = "0.9"
[patch.crates-io]
-#bitcoin = { git = "https://github.com/jbride/rust-bitcoin.git", branch = "p2tsh" }
+#bitcoin = { git = "https://github.com/jbride/rust-bitcoin.git", branch = "p2mr" }
# Verify:
# cargo update
diff --git a/bip-0360/ref-impl/rust/README.md b/bip-0360/ref-impl/rust/README.md
index 7a0de948d0..52bc7327ad 100644
--- a/bip-0360/ref-impl/rust/README.md
+++ b/bip-0360/ref-impl/rust/README.md
@@ -1,12 +1,12 @@
-# p2tsh test vectors
+# p2mr test vectors
This rust project contains the test vectors for BIP-360
## Run Test Vectors
-These test vectors are being developed in conjunction with forks of [rust-bitcoin](https://github.com/jbride/rust-bitcoin/tree/p2tsh) and [rust-miniscript](https://github.com/jbride/rust-miniscript/tree/p2tsh-pqc) customized with p2tsh functionality.
+These test vectors are being developed in conjunction with forks of [rust-bitcoin](https://github.com/jbride/rust-bitcoin/tree/p2mr) and [rust-miniscript](https://github.com/jbride/rust-miniscript/tree/p2mr-pqc) customized with p2mr functionality.
1. environment variables
@@ -19,13 +19,13 @@ These test vectors are being developed in conjunction with forks of [rust-bitcoi
1. run a specific test:
```
- $ cargo test test_p2tsh_single_leaf_script_tree -- --nocapture
+ $ cargo test test_p2mr_single_leaf_script_tree -- --nocapture
```
## Local Development
-All P2TSH/PQC enabled bitcoin crates are temporarily available in a custom crate registry at: `https://crates.denver.space`.
+All P2MR/PQC enabled bitcoin crates are temporarily available in a custom crate registry at: `https://crates.denver.space`.
These crates will be made available in `crates.io` in the near future.
Subsequently, you will need to execute the following at the root of your rust workspace:
@@ -36,7 +36,7 @@ mkdir .cargo \
index = "sparse+https://crates.denver.space/api/v1/crates/"' > .cargo/config
```
-Afterwards, for all P2TSH/PQC enabled dependencies used in your project, include a "registry" similar to the following:
+Afterwards, for all P2MR/PQC enabled dependencies used in your project, include a "registry" similar to the following:
```bash
bitcoin = { version="0.32.6", registry = "kellnr-denver-space" }
diff --git a/bip-0360/ref-impl/rust/docs/development_notes.adoc b/bip-0360/ref-impl/rust/docs/development_notes.adoc
index a1e8799969..270b05ac63 100644
--- a/bip-0360/ref-impl/rust/docs/development_notes.adoc
+++ b/bip-0360/ref-impl/rust/docs/development_notes.adoc
@@ -4,78 +4,78 @@
=== Two Different Size Limits:
* *MAX_SCRIPT_ELEMENT_SIZE* (in interpreter.cpp line 1882) - This is a consensus rule that limits individual stack elements to 520 bytes. This is what's currently blocking your SLH-DSA signature.
-* *MAX_STANDARD_P2TSH_STACK_ITEM_SIZE* (in policy.h) - This is a policy rule that limits P2TSH stack items to 80 bytes (or 8000 bytes with your change) for standardness.
+* *MAX_STANDARD_P2MR_STACK_ITEM_SIZE* (in policy.h) - This is a policy rule that limits P2MR stack items to 80 bytes (or 8000 bytes with your change) for standardness.
-== P2TSH changes to rust-bitcoin
+== P2MR changes to rust-bitcoin
-# 1. p2tsh module
+# 1. p2mr module
-The p2tsh branch of rust-bitcoin includes a new module: `p2tsh`.
+The p2mr branch of rust-bitcoin includes a new module: `p2mr`.
-Source code for this new module can be found [here](https://github.com/jbride/rust-bitcoin/blob/p2tsh/bitcoin/src/p2tsh/mod.rs).
+Source code for this new module can be found [here](https://github.com/jbride/rust-bitcoin/blob/p2mr/bitcoin/src/p2mr/mod.rs).
-Highlights of this _p2tsh_ module as follows:
+Highlights of this _p2mr_ module as follows:
-## 1.1. p2tshBuilder
+## 1.1. p2mrBuilder
This is struct inherits from the rust-bitcoin _TaprootBuilder_.
It has an important modification in that it disables keypath spend.
-Similar to its Taproot parent, p2tshBuilder provides functionality to add leaves to a TapTree.
-One its TapTree has been fully populated with all leaves, an instance of _p2tshSpendInfo_ can be retrieved from p2tshBuilder.
+Similar to its Taproot parent, p2mrBuilder provides functionality to add leaves to a TapTree.
+One its TapTree has been fully populated with all leaves, an instance of _p2mrSpendInfo_ can be retrieved from p2mrBuilder.
```
-pub struct p2tshBuilder {
+pub struct p2mrBuilder {
inner: TaprootBuilder
}
-impl p2tshBuilder {
+impl p2mrBuilder {
- /// Creates a new p2tsh builder.
+ /// Creates a new p2mr builder.
pub fn new() -> Self {
Self {
inner: TaprootBuilder::new()
}
}
- /// Adds a leaf to the p2tsh builder.
+ /// Adds a leaf to the p2mr builder.
pub fn add_leaf_with_ver(
self,
depth: u8,
script: ScriptBuf,
leaf_version: LeafVersion,
- ) -> Result {
+ ) -> Result {
match self.inner.add_leaf_with_ver(depth, script, leaf_version) {
Ok(builder) => Ok(Self { inner: builder }),
- Err(_) => Err(p2tshError::LeafAdditionError)
+ Err(_) => Err(p2mrError::LeafAdditionError)
}
}
- /// Finalizes the p2tsh builder.
- pub fn finalize(self) -> Result {
+ /// Finalizes the p2mr builder.
+ pub fn finalize(self) -> Result {
let node_info: NodeInfo = self.inner.try_into_node_info().unwrap();
- Ok(p2tshSpendInfo {
+ Ok(p2mrSpendInfo {
merkle_root: Some(node_info.node_hash()),
//script_map: self.inner.script_map().clone(),
})
}
- /// Converts the p2tsh builder into a Taproot builder.
+ /// Converts the p2mr builder into a Taproot builder.
pub fn into_inner(self) -> TaprootBuilder {
self.inner
}
}
```
-## 1.2. p2tshSpendInfo
+## 1.2. p2mrSpendInfo
-Provides merkle_root of a completed p2tsh TapTree
+Provides merkle_root of a completed p2mr TapTree
```
-/// A struct for p2tsh spend information.
+/// A struct for p2mr spend information.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct p2tshSpendInfo {
+pub struct p2mrSpendInfo {
/// The merkle root of the script path.
pub merkle_root: Option
@@ -83,27 +83,27 @@ pub struct p2tshSpendInfo {
}
```
-## 1.3. p2tshScriptBuf
+## 1.3. p2mrScriptBuf
-Allows for creation of a p2tsh scriptPubKey UTXO using only the merkle root of a script tree only.
+Allows for creation of a p2mr scriptPubKey UTXO using only the merkle root of a script tree only.
```
-/// A wrapper around ScriptBuf for p2tsh (Pay to Quantum Resistant Hash) scripts.
-pub struct p2tshScriptBuf {
+/// A wrapper around ScriptBuf for p2mr (Pay to Quantum Resistant Hash) scripts.
+pub struct p2mrScriptBuf {
inner: ScriptBuf
}
-impl p2tshScriptBuf {
- /// Creates a new p2tsh script from a ScriptBuf.
+impl p2mrScriptBuf {
+ /// Creates a new p2mr script from a ScriptBuf.
pub fn new(inner: ScriptBuf) -> Self {
Self { inner }
}
- /// Generates p2tsh scriptPubKey output
+ /// Generates p2mr scriptPubKey output
/// Only accepts the merkle_root (of type TapNodeHash)
- /// since keypath spend is disabled in p2tsh
- pub fn new_p2tsh(merkle_root: TapNodeHash) -> Self {
- // https://github.com/cryptoquick/bips/blob/p2tsh/bip-0360.mediawiki#scriptpubkey
+ /// since keypath spend is disabled in p2mr
+ pub fn new_p2mr(merkle_root: TapNodeHash) -> Self {
+ // https://github.com/cryptoquick/bips/blob/p2mr/bip-0360.mediawiki#scriptpubkey
let merkle_root_hash_bytes: [u8; 32] = merkle_root.to_byte_array();
let script = Builder::new()
.push_opcode(OP_PUSHNUM_3)
@@ -112,7 +112,7 @@ impl p2tshScriptBuf {
.push_slice(&merkle_root_hash_bytes)
.into_script();
- p2tshScriptBuf::new(script)
+ p2mrScriptBuf::new(script)
}
/// Returns the script as a reference.
@@ -122,17 +122,17 @@ impl p2tshScriptBuf {
}
```
-## 1.4. p2tsh Control Block
+## 1.4. p2mr Control Block
Closely related to P2TR control block.
Difference being that _internal public key_ is not included.
```
-/// A control block for p2tsh (Pay to Quantum Resistant Hash) script path spending.
+/// A control block for p2mr (Pay to Quantum Resistant Hash) script path spending.
/// This is a simplified version of Taproot's control block that excludes key-related fields.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct p2tshControlBlock {
+pub struct p2mrControlBlock {
/// The version of the leaf.
pub leaf_version: LeafVersion,
/// The merkle branch of the leaf.
@@ -142,33 +142,33 @@ pub struct p2tshControlBlock {
# 2. Witness Program
-New p2tsh related functions that allow for creation of a new V3 _witness program_ given a merkle_root only.
+New p2mr related functions that allow for creation of a new V3 _witness program_ given a merkle_root only.
Found in bitcoin/src/blockdata/script/witness_program.rs
```
/// Creates a [`WitnessProgram`] from a 32 byte merkle root.
-fn new_p2tsh(program: [u8; 32]) -> Self {
+fn new_p2mr(program: [u8; 32]) -> Self {
WitnessProgram { version: WitnessVersion::V3, program: ArrayVec::from_slice(&program) }
}
/// Creates a pay to quantum resistant hash address from a merkle root.
-pub fn p2tsh(merkle_root: Option) -> Self {
+pub fn p2mr(merkle_root: Option) -> Self {
let merkle_root = merkle_root.unwrap();
- WitnessProgram::new_p2tsh(merkle_root.to_byte_array())
+ WitnessProgram::new_p2mr(merkle_root.to_byte_array())
}
```
# 3. Address
-New _p2tsh_ function that allows for creation of a new _p2tsh_ Address given a merkle_root only.
+New _p2mr_ function that allows for creation of a new _p2mr_ Address given a merkle_root only.
Found in bitcoin/src/address/mod.rs
```
/// Creates a pay to quantum resistant hash address from a merkle root.
-pub fn p2tsh(merkle_root: Option, hrp: impl Into) -> Address {
- let program = WitnessProgram::p2tsh(merkle_root);
+pub fn p2mr(merkle_root: Option, hrp: impl Into) -> Address {
+ let program = WitnessProgram::p2mr(merkle_root);
Address::from_witness_program(program, hrp)
}
```
diff --git a/bip-0360/ref-impl/rust/docs/images/p2tsh_construction.png b/bip-0360/ref-impl/rust/docs/images/p2mr_construction.png
similarity index 100%
rename from bip-0360/ref-impl/rust/docs/images/p2tsh_construction.png
rename to bip-0360/ref-impl/rust/docs/images/p2mr_construction.png
diff --git a/bip-0360/ref-impl/rust/docs/images/p2tsh_witness.png b/bip-0360/ref-impl/rust/docs/images/p2mr_witness.png
similarity index 100%
rename from bip-0360/ref-impl/rust/docs/images/p2tsh_witness.png
rename to bip-0360/ref-impl/rust/docs/images/p2mr_witness.png
diff --git a/bip-0360/ref-impl/rust/docs/images/tap_tree_concatenated.png b/bip-0360/ref-impl/rust/docs/images/tap_tree_concatenated.png
new file mode 100644
index 0000000000..7a392eecc9
Binary files /dev/null and b/bip-0360/ref-impl/rust/docs/images/tap_tree_concatenated.png differ
diff --git a/bip-0360/ref-impl/rust/docs/images/tap_tree_mixed.png b/bip-0360/ref-impl/rust/docs/images/tap_tree_mixed.png
new file mode 100644
index 0000000000..d4c0a7d6b1
Binary files /dev/null and b/bip-0360/ref-impl/rust/docs/images/tap_tree_mixed.png differ
diff --git a/bip-0360/ref-impl/rust/docs/images/tap_tree_schnorr_only.png b/bip-0360/ref-impl/rust/docs/images/tap_tree_schnorr_only.png
new file mode 100644
index 0000000000..9445608596
Binary files /dev/null and b/bip-0360/ref-impl/rust/docs/images/tap_tree_schnorr_only.png differ
diff --git a/bip-0360/ref-impl/rust/docs/images/tap_tree_slh_dsa_only.png b/bip-0360/ref-impl/rust/docs/images/tap_tree_slh_dsa_only.png
new file mode 100644
index 0000000000..eb51b26fc9
Binary files /dev/null and b/bip-0360/ref-impl/rust/docs/images/tap_tree_slh_dsa_only.png differ
diff --git a/bip-0360/ref-impl/rust/docs/p2tsh-end-to-end.adoc b/bip-0360/ref-impl/rust/docs/p2mr-end-to-end.adoc
similarity index 53%
rename from bip-0360/ref-impl/rust/docs/p2tsh-end-to-end.adoc
rename to bip-0360/ref-impl/rust/docs/p2mr-end-to-end.adoc
index 42036e4613..f09c5bc117 100644
--- a/bip-0360/ref-impl/rust/docs/p2tsh-end-to-end.adoc
+++ b/bip-0360/ref-impl/rust/docs/p2mr-end-to-end.adoc
@@ -3,17 +3,17 @@
:toc2:
:linkattrs:
-= P2TSH End-to-End Tutorial
+= 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 P2TSH UTXO to a P2WPKH address.
+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] `p2tsh` UTXO (optionally using _Post-Quantum Cryptography_).
+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`.
@@ -24,91 +24,137 @@ The steps outlined in this tutorial are executed using a custom Bitcoin Core ins
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: <>.
+Otherwise, if running this tutorial on your own, follow the instructions in the appendix of this doc: <>.
=== Shell Environment
. *docker / podman*
+
-NOTE: If you have built the custom `p2tsh` enabled Bitcoin Core, you do not need docker (nor podman) installed. Skip this section.
+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 `p2tsh` enabled _bitcoin-cli_ utility.
+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 `p2tsh` 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.
+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 `p2tsh` enabled.
+.. 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:p2tsh-pqc-0.0.1
+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:p2tsh-pqc-0.0.1 -rpcconnect=192.168.122.1 -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest'
+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 P2TSH UTXO
+== 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
+export BITCOIN_NETWORK=regtest
-----
+
-Doing so influences the P2TSH address that you'll create later in this tutorial.
+Doing so influences the P2MR address that you'll create later in this tutorial.
-. OPTIONAL: Indicate what type of cryptography to use in the locking scripts of your TapTree leaves.
-Valid options are: `SLH_DSA_ONLY`, `SCHNORR_ONLY`, `SCHNORR_AND_SLH_DSA`.
-Default is `SCHNORR_ONLY`.
+. Define number of total leaves in tap tree :
+
[source,bash]
-----
-$ export LEAF_SCRIPT_TYPE=SLH_DSA_ONLY
+export TOTAL_LEAF_COUNT=5
-----
-. OPTIONAL: Define number of leaves in tap tree as well as the tap leaf to later use as the unlocking script:
+. 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 TOTAL_LEAF_COUNT=5 \
- && export LEAF_OF_INTEREST=4
+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:
+
-NOTE: Defaults are 4 leaves with the first leaf (leaf 0 ) as the script to later use as the unlocking script.
+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].
-. Generate a P2TSH scripPubKey with multi-leaf taptree:
+.. 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 BITCOIN_ADDRESS_INFO=$( cargo run --example p2tsh_construction ) \
+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 P2TSH address that starts with: `bcrt1z` .
+NOTE: In `regtest`, you can expect a P2MR address that starts with: `bcrt1z` .
+
[subs=+quotes]
++++
What just happened?
-The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction with a `p2tsh` UTXO as follows:
+The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction with a `p2mr` UTXO as follows:
- A configurable number of leaves are generated each with their own locking script.
- Each of these leaves are added to a Huffman tree that sorts the leaves by weight.
- - The merkle root of the tree is calculated and subsequently used to generate the p2tsh witness program and BIP0350 address.
+ - The merkle root of the tree is calculated and subsequently used to generate the p2mr witness program and BIP0350 address.
The source code for the above logic is found in this project: src/lib.rs
@@ -116,29 +162,37 @@ The source code for the above logic is found in this project: src/lib.rs
++++
+. 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 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 P2TSH_ADDR=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.bech32m_address' )
+ && 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'
+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 P2TSH address with the coinbase reward of a newly generated block:
+. Fund this P2MR address with the coinbase reward of a newly generated block:
+
Choose from one of the following networks:
@@ -148,7 +202,7 @@ If on `regtest` network, execute the following:
+
[source,bash]
-----
-$ export COINBASE_REWARD_TX_ID=$( b-cli -regtest -named generatetoaddress nblocks=1 address="$P2TSH_ADDR" maxtries=5 | jq -r '.[]' ) \
+export COINBASE_REWARD_TX_ID=$( b-cli -named generatetoaddress nblocks=1 address="$P2MR_ADDR" maxtries=5 | jq -r '.[]' ) \
&& echo $COINBASE_REWARD_TX_ID
-----
+
@@ -161,26 +215,26 @@ 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 $P2TSH_ADDR \
+ --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 P2TSH address
+. view summary of all txs that have funded P2MR address
+
[source,bash]
-----
-$ export P2TSH_DESC=$( b-cli getdescriptorinfo "addr($P2TSH_ADDR)" | jq -r '.descriptor' ) \
- && echo $P2TSH_DESC \
- && b-cli scantxoutset start '[{"desc": "'''$P2TSH_DESC'''"}]'
+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": "'''$P2TSH_DESC'''"}]' | jq -r '.unspents[0].txid' ) \
+export FUNDING_TX_ID=$( b-cli scantxoutset start '[{"desc": "'''$P2MR_DESC'''"}]' | jq -r '.unspents[0].txid' ) \
&& echo $FUNDING_TX_ID
-----
@@ -188,36 +242,36 @@ $ export FUNDING_TX_ID=$( b-cli scantxoutset start '[{"desc": "'''$P2TSH_DESC'''
+
[source,bash]
-----
-$ export FUNDING_UTXO_INDEX=0
+export FUNDING_UTXO_INDEX=0
-----
-. view details of funding UTXO to the P2TSH address:
+. 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''']' ) \
+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 P2TSH UTXO
+== Spend P2MR UTXO
-In the previous section, you created and funded a P2TSH 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 P2TSH 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 P2TSH UTXO.
+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 P2TSH 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}') \
+export FUNDING_UTXO_AMOUNT_SATS=$(echo $FUNDING_UTXO | jq -r '.value' | awk '{printf "%.0f", $1 * 100000000}') \
&& echo $FUNDING_UTXO_AMOUNT_SATS
-----
@@ -233,7 +287,7 @@ _bad-txns-premature-spend-of-coinbase, tried to spend coinbase at depth 1_
+
[source,bash]
-----
-$ b-cli -generate 110
+b-cli -generate 110
-----
.. signet
@@ -247,25 +301,18 @@ The `common/utils` directory of this project provides a script called: link:../.
+
[source,bash]
-----
-$ export SPEND_DETAILS=$( cargo run --example p2tsh_spend )
-
-$ export RAW_P2TSH_SPEND_TX=$( echo $SPEND_DETAILS | jq -r '.tx_hex' ) \
- && echo "RAW_P2TSH_SPEND_TX = $RAW_P2TSH_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"
+export SPEND_DETAILS=$( cargo run --example p2mr_spend )
-----
+
[subs=+quotes]
++++
What just happened?
-The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction that spends from the `p2tsh` UTXO as follows:
+The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction that spends from the `p2mr` UTXO as follows:
- Create a transaction template (aka: SigHash) that serves as the message to be signed.
- - Using the known private key and the SigHash, create a signature that is capable of unlocking one of the leaf scripts of the P2TSH tree.
+ - 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.
- Add this signature to the witness section of the transaction.
@@ -274,41 +321,53 @@ The source code for the above logic is found in this project: src/lib.rs
++++
+. 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_P2TSH_SPEND_TX
+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 p2tsh enabled Bitcoin Core:
+. Test standardness of the spending tx by sending to local mempool of p2mr enabled Bitcoin Core:
+
[source,bash]
-----
-$ b-cli testmempoolaccept '["'''$RAW_P2TSH_SPEND_TX'''"]'
+b-cli testmempoolaccept '["'''$RAW_P2MR_SPEND_TX'''"]'
-----
. Submit tx:
+
[source,bash]
-----
-$ export P2TSH_SPENDING_TX_ID=$( b-cli sendrawtransaction $RAW_P2TSH_SPEND_TX ) \
- && echo $P2TSH_SPENDING_TX_ID
+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_P2TSH_SPEND_TX
+NOTE: Should return same tx id as was included in $RAW_P2MR_SPEND_TX
-== Mine P2TSH Spend TX
+== Mine P2MR Spend TX
. View tx in mempool:
+
[source,bash]
-----
-$ b-cli getrawtransaction $P2TSH_SPENDING_TX_ID 1
+b-cli getrawtransaction $P2MR_SPENDING_TX_ID 1
-----
+
NOTE: There will not yet be a field `blockhash` in the response.
@@ -319,7 +378,7 @@ NOTE: There will not yet be a field `blockhash` in the response.
+
[source,bash]
-----
-$ b-cli -generate 1
+b-cli -generate 1
-----
.. signet:
@@ -328,8 +387,8 @@ 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 $P2TSH_ADDR \
+$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"
@@ -339,7 +398,7 @@ $ $BITCOIN_SOURCE_DIR/contrib/signet/miner --cli "bitcoin-cli -conf=$HOME/anduro
+
[source,bash]
-----
-$ export BLOCK_HASH=$( b-cli getrawtransaction $P2TSH_SPENDING_TX_ID 1 | jq -r '.blockhash' ) \
+export BLOCK_HASH=$( b-cli getrawtransaction $P2MR_SPENDING_TX_ID 1 | jq -r '.blockhash' ) \
&& echo $BLOCK_HASH
-----
@@ -347,37 +406,37 @@ $ export BLOCK_HASH=$( b-cli getrawtransaction $P2TSH_SPENDING_TX_ID 1 | jq -r '
+
[source,bash]
-----
-$ b-cli getblock $BLOCK_HASH | jq -r .tx
+b-cli getblock $BLOCK_HASH | jq -r .tx
-----
== Appendix
-[[build_p2tsh]]
-=== Build P2TSH / PQC Enabled Bitcoin Core
+[[build_p2mr]]
+=== Build P2MR / PQC Enabled Bitcoin Core
-The link:https://github.com/jbride/bitcoin/tree/p2tsh[p2tsh branch] of bitcoin core is needed.
+The link:https://github.com/jbride/bitcoin/tree/p2mr[p2mr branch] of bitcoin core is needed.
-Build instructions for the `p2tsh` branch are the same as `master` and is documented link:https://github.com/bitcoin/bitcoin/blob/master/doc/build-unix.md[here].
+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 `p2tsh` branch of bitcoin core:
+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
+export BITCOIN_SOURCE_DIR=/path/to/root/dir/of/cloned/bitcoin/source
-----
. build
+
-----
-$ cmake -B 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)
+cmake --build build -j$(nproc)
-----
. run in either `regtest` or `signet` mode:
@@ -385,13 +444,13 @@ $ cmake --build build -j$(nproc)
.. regtest:
+
-----
-$ ./build/bin/bitcoind -daemon=0 -regtest=1 -txindex -prune=0
+./build/bin/bitcoind -daemon=0 -regtest=1 -txindex -prune=0
-----
.. signet:
+
-----
-$ ./build/bin/bitcoind -daemon=0 -signet=1 -txindex -prune=0
+./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.
@@ -399,29 +458,29 @@ link:https://edil.com.br/blog/creating-a-custom-bitcoin-signet[This tutorial] pr
=== libbitcoinpqc build
-The `p2tsh-pqc` branch of this project includes a dependency on the link:https://crates.io/crates/libbitcoinpqc[libbitcoinpqc crate].
+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 `p2tsh-ref` must have cmake and a C toolchain installed locally.
+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
+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
+sudo dnf5 update
+sudo dnf5 install cmake make gcc gcc-c++ clang clang-libs llvm-devel
-----
==== OSX
@@ -435,10 +494,10 @@ This tutorial assumes that a bitcoin core wallet is available.
+
-----
-$ export W_NAME=anduro
+export W_NAME=anduro
-$ b-cli -named createwallet \
- wallet_name=$W_NAME \
+b-cli -named createwallet \
+ wallet_name=$W_NAME \
descriptors=true \
load_on_startup=true
-----
diff --git a/bip-0360/ref-impl/rust/docs/p2tsh-workshop.adoc b/bip-0360/ref-impl/rust/docs/p2mr-signet-workshop.adoc
similarity index 61%
rename from bip-0360/ref-impl/rust/docs/p2tsh-workshop.adoc
rename to bip-0360/ref-impl/rust/docs/p2mr-signet-workshop.adoc
index 7ff10b867e..0afaaac8a4 100644
--- a/bip-0360/ref-impl/rust/docs/p2tsh-workshop.adoc
+++ b/bip-0360/ref-impl/rust/docs/p2mr-signet-workshop.adoc
@@ -3,21 +3,21 @@
:toc2:
:linkattrs:
-= P2TSH End-to-End workshop
+= P2MR End-to-End workshop
:numbered:
-Welcome to the BIP-360 / _Pay-To-Tap-Script-Hash_ (P2TSH) workshop !
+Welcome to the BIP-360 / _Pay-To-Tap-Script-Hash_ (P2MR) workshop !
-In this workshop, you will interact with a custom Signet environment to create, fund and spend from a _P2TSH_ address.
+In this workshop, you will interact with a custom Signet environment to create, fund and spend from a _P2MR_ address.
-_P2TSH_ is a new Bitcoin address type defined in link:https://bip360.org/bip360.html[bip-360].
+_P2MR_ is a new Bitcoin address type defined in link:https://bip360.org/bip360.html[bip-360].
-In addition, this workshop allows for the (un)locking mechanism of the leaf scripts of your P2TSH address to optionally use _Post Quantum Cryptography_ (PQC).
+In addition, this workshop allows for the (un)locking mechanism of the leaf scripts of your P2MR address to optionally use _Post Quantum Cryptography_ (PQC).
The use of PQC is alluded to in BIP-360 and will be further defined in future BIPs.
-The steps outlined in this workshop are executed using a P2TSH/PQC enabled Bitcoin Core instance running on a signet environment.
+The steps outlined in this workshop are executed using a P2MR/PQC enabled Bitcoin Core instance running on a signet environment.
*The target audience of the workshop is Bitcoin developers and ops personnel.
As such, the workshop makes heavy use of the _bitcoin-cli_ at the command line.*
@@ -28,7 +28,7 @@ As such, the workshop makes heavy use of the _bitcoin-cli_ at the command line.*
This workshop environment is provided as a _docker_ container.
Subsequently, ensure your host machine has either link:https://docs.docker.com/desktop/[docker] or link:https://podman.io/docs/installation[podman] installed.
-=== *p2tsh_demo* docker container
+=== *p2mr_demo* docker container
==== Obtain container
@@ -38,7 +38,7 @@ Once docker or podman is installed on your machine, you can obtain the workshop
+
[source,bash]
-----
-sudo docker pull quay.io/jbride2000/p2tsh_demo:0.1
+sudo docker pull quay.io/jbride2000/p2mr_demo:0.1
-----
+
NOTE: The container image is 1.76GB in size. This approach may be slow depending on network bandwidth.
@@ -52,12 +52,12 @@ NOTE: The container image is 1.76GB in size. This approach may be slow dependi
.. Workshop instructors have the container image available via USB thumb drives.
If you feel comfortable with this approach, ask an instructor for a thumb drive.
-... Mount the USB thumb drive and copy for the file called: _p2tsh_demo-image.tar_.
+... Mount the USB thumb drive and copy for the file called: _p2mr_demo-image.tar_.
... Load the container image into your docker environment as per the following:
+
[source,bash]
-----
-docker load -i /path/to/p2tsh_demo-image.tar
+docker load -i /path/to/p2mr_demo-image.tar
-----
==== Start container
@@ -71,7 +71,7 @@ You will need to start the workshop container using the docker infrastructure on
sudo docker run -it --rm --entrypoint /bin/bash --network host \
-e RPC_CONNECT=10.21.3.194 \
- quay.io/jbride2000/p2tsh_demo:0.1
+ quay.io/jbride2000/p2mr_demo:0.1
-----
. You should see a _bash_ shell command prompt similar to the following:
@@ -83,10 +83,10 @@ bip360@0aa9edf3d201:~/bips/bip-0360/ref-impl/rust$ ls
Cargo.lock Cargo.toml README.md docs examples src tests
-----
+
-As per the `ls` command seen above, your command prompt path defaults to the link:https://github.com/jbride/bips/tree/p2tsh/bip-0360/ref-impl/rust[Rust based reference implementation] for BIP-360.
+As per the `ls` command seen above, your command prompt path defaults to the link:https://github.com/jbride/bips/tree/p2mr/bip-0360/ref-impl/rust[Rust based reference implementation] for BIP-360.
==== Container contents
-Your docker environment already includes a P2TSH/PQC enabled `bitcoin-cli` utility.
+Your docker environment already includes a P2MR/PQC enabled `bitcoin-cli` utility.
In addition, an alias to this custom bitcoin-cli utility configured for the signet workshop environment has also been provided.
. You can view this alias as follows (execute all commands within workshop container image):
@@ -125,42 +125,55 @@ b-cli getblockcount
== Bitcoin Environment
-Your workshop instructors have provided you with a _P2TSH/PQC_ enabled Bitcoin environment running in _signet_.
+Your workshop instructors have provided you with a _P2MR/PQC_ enabled Bitcoin environment running in _signet_.
You will send RPC commands to this custom Bitcoin node via the _b-cli_of your docker container.
image::images/workshop_deployment_arch.png[]
-Via your browser, you will interact with the P2TSH enabled _mempool.space_ for the workshop at: link:http://signet.bip360.org[signet.bip360.org].
+Via your browser, you will interact with the P2MR enabled _mempool.space_ for the workshop at: link:http://signet.bip360.org[signet.bip360.org].
-== Create & Fund P2TSH Address
+== Create & Fund P2MR Address
-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] _P2TSH_ address (optionally using _Post-Quantum Cryptography_).
+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 P2TSH address.
+In this section of the workshop, you create and fund a P2MR address.
-The following depicts the construction of a P2TSH _TapTree_ and computation its _scriptPubKey_.
+The following depicts the construction of a P2MR _TapTree_ and computation its _scriptPubKey_.
-image::images/p2tsh_construction.png[]
+image::images/p2mr_construction.png[]
-A P2TSH address is created by adding locking scripts to leaves of a _TapTree_.
+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.
-. OPTIONAL: In your container image, indicate what type of cryptography to use in the locking scripts of your TapTree leaves.
-Valid options are: `SLH_DSA_ONLY`, `SCHNORR_ONLY`, `SCHNORR_AND_SLH_DSA`.
-Default is `SCHNORR_ONLY`.
+. Define number of total leaves in tap tree :
++
+[source,bash]
+-----
+export TOTAL_LEAF_COUNT=5
+-----
+
+. In your container image, indicate what type of cryptography to use in the locking scripts of your TapTree leaves.
+Valid options are: `MIXED`, `SLH_DSA_ONLY`, `SCHNORR_ONLY`, `CONCATENATED_SCHNORR_AND_SLH_DSA`.
+Default is `MIXED`.
+
[source,bash]
-----
-$ export LEAF_SCRIPT_TYPE=SLH_DSA_ONLY
+export TAP_TREE_LOCK_TYPE=MIXED
-----
-.. If you set _LEAF_SCRIPT_TYPE=SCHNORR_ONLY_, then the locking script of your TapTree leaves will utilize _Schnorr_ cryptography.
+.. 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 _LEAF_SCRIPT_TYPE=SLH_DSA_ONLY_, then the locking script of your TapTree leaves will utilize _SLH-DSA_ cryptography.
+.. 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.
+
@@ -168,44 +181,46 @@ 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 _LEAF_SCRIPT_TYPE=SCHNORR_AND_SLH_DSA_, then the locking script of your TapTree leaves will be secured using both SCHNORR and SLH-DSA cryptography.
-
-. Define number of total leaves in tap tree :
+.. 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:
+
-[source,bash]
------
-export TOTAL_LEAF_COUNT=5
------
+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].
-. Set the tap leaf index to later use as the unlocking script (when spending)
+.. 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_OF_INTEREST=4
+export LEAF_TO_SPEND_FROM=4
-----
-+
-NOTE: Defaults is 4 leaves with the first leaf (leaf 0 ) as the script to later use to unlock during spending.
-. Generate a P2TSH scripPubKey with multi-leaf taptree:
+. Generate a P2MR scripPubKey with multi-leaf taptree:
+
[source,bash]
-----
-export BITCOIN_ADDRESS_INFO=$( cargo run --example p2tsh_construction ) \
+export BITCOIN_ADDRESS_INFO=$( cargo run --example p2mr_construction ) \
&& echo $BITCOIN_ADDRESS_INFO | jq -r .
-----
+
-NOTE: In signet, you can expect a P2TSH address that starts with the following prefix: `tb1z` .
+NOTE: In signet, you can expect a P2MR address that starts with the following prefix: `tb1z` .
+
[subs=+quotes]
++++
What just happened?
-The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction with a P2TSH UTXO as follows:
+The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction with a P2MR UTXO as follows:
- A configurable number of leaves are generated each with their own locking script.
- Each of these leaves are added to a Huffman tree that sorts the leaves by weight.
- - The merkle root of the tree is calculated and subsequently used to generate the P2TSH witness program and BIP0350 address.
+ - The merkle root of the tree is calculated and subsequently used to generate the P2MR witness program and BIP0350 address.
@@ -214,7 +229,14 @@ The Rust based reference implementation for BIP-0360 is leveraged to construct a
+
The source code for the above logic is found in this project's source file: link:../src/lib.rs[src/lib.rs]
-. Fund this P2TSH address using workshop's signet faucet
+. 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
+-----
+
+. Fund this P2MR address using workshop's signet faucet
.. In a browser tab, navigate to: link:http://faucet.bip360.org/[faucet.bip360.org].
.. Copy-n-paste the value of `bech32m_address` (found in the json response from the previous step)
@@ -228,7 +250,7 @@ image::images/faucet_2.png[]
.. Click on the link of your transaction id.
This will take you a detailed view of the transaction.
-Scroll down to the the _Inputs & Outputs_ section of the transaction and identify the _vout_ index of funds sent to your _P2TSH_ address.
+Scroll down to the the _Inputs & Outputs_ section of the transaction and identify the _vout_ index of funds sent to your _P2MR_ address.
+
image::images/funding_utxo_id.png[]
@@ -239,7 +261,7 @@ image::images/funding_utxo_id.png[]
export FUNDING_UTXO_INDEX=
-----
-.. Return back to your browser tab at navigate to: link:https://signet.bip360.org[signet.bip360.org] and wait until a new block mines the transaction from the faucet that funded your P2TSH address.
+.. Return back to your browser tab at navigate to: link:https://signet.bip360.org[signet.bip360.org] and wait until a new block mines the transaction from the faucet that funded your P2MR address.
+
image::images/mempool_next_block.png[]
@@ -252,7 +274,7 @@ export MERKLE_ROOT=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.taptree_return.tree_r
&& 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 P2TSH_ADDR=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.bech32m_address' )
+ && export P2MR_ADDR=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.bech32m_address' )
-----
. View tapscript used in target leaf of taptree:
@@ -270,26 +292,26 @@ If using SCHNORR + SLH_DSA, then you should see a locking script in the leaf sim
886fc1edb7a8a364da65aef57343de451c1449d8a6c5b766fe150667d50d3e80 OP_CHECKSIG 479f93fbd251863c3e3e72da6e26ea82f87313da13090de10e57eca1f8b5e0f3 OP_SUBSTR OP_BOOLAND OP_VERIFY
-----
-. view summary of all txs that have funded P2TSH address
+. view summary of all txs that have funded P2MR address
+
[source,bash]
-----
-export P2TSH_DESC=$( b-cli getdescriptorinfo "addr($P2TSH_ADDR)" | jq -r '.descriptor' ) \
- && echo $P2TSH_DESC \
- && b-cli scantxoutset start '[{"desc": "'''$P2TSH_DESC'''"}]'
+export P2MR_DESC=$( b-cli getdescriptorinfo "addr($P2MR_ADDR)" | jq -r '.descriptor' ) \
+ && echo $P2MR_DESC \
+ && b-cli scantxoutset start '[{"desc": "'''$P2MR_DESC'''"}]'
-----
+
-NOTE: You will likely have to wait a few minutes until a new block (containing the tx that funds your P2TSH address) is mined.
+NOTE: You will likely have to wait a few minutes until a new block (containing the tx that funds your P2MR address) is mined.
. grab txid of first tx with unspent funds:
+
[source,bash]
-----
-export FUNDING_TX_ID=$( b-cli scantxoutset start '[{"desc": "'''$P2TSH_DESC'''"}]' | jq -r '.unspents[0].txid' ) \
+export FUNDING_TX_ID=$( b-cli scantxoutset start '[{"desc": "'''$P2MR_DESC'''"}]' | jq -r '.unspents[0].txid' ) \
&& echo $FUNDING_TX_ID
-----
-. view details of funding UTXO to the P2TSH address:
+. view details of funding UTXO to the P2MR address:
+
[source,bash]
-----
@@ -298,18 +320,18 @@ export FUNDING_UTXO=$( b-cli getrawtransaction $FUNDING_TX_ID 1 | jq -r '.vout['
-----
-== Spend P2TSH UTXO
+== Spend P2MR UTXO
-In the previous section, you created and funded a P2TSH 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 P2TSH 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 P2TSH UTXO.
+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.
The target address type that you send funds to is: P2WPKH.
-. Determine value (in sats) of the funding P2TSH utxo:
+. Determine value (in sats) of the funding P2MR utxo:
+
[source,bash]
-----
@@ -321,18 +343,18 @@ export FUNDING_UTXO_AMOUNT_SATS=$(echo $FUNDING_UTXO | jq -r '.value' | awk '{pr
+
[source,bash]
-----
-export SPEND_DETAILS=$( cargo run --example p2tsh_spend )
+export SPEND_DETAILS=$( cargo run --example p2mr_spend )
-----
+
[subs=+quotes]
++++
What just happened?
-The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction that spends from the P2TSH UTXO as follows:
+The Rust based reference implementation for BIP-0360 is leveraged to construct a transaction that spends from the P2MR UTXO as follows:
- Create a transaction template (aka: SigHash) that serves as the message to be signed.
- - Using the known private key and the SigHash, create a signature that is capable of unlocking one of the leaf scripts of the P2TSH tree.
+ - 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.
- Add this signature to the witness section of the transaction.
@@ -346,8 +368,8 @@ The source code for the above logic is found in this project's source file: lin
+
[source,bash]
-----
-export RAW_P2TSH_SPEND_TX=$( echo $SPEND_DETAILS | jq -r '.tx_hex' ) \
- && echo "RAW_P2TSH_SPEND_TX = $RAW_P2TSH_SPEND_TX" \
+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' ) \
@@ -358,49 +380,49 @@ export RAW_P2TSH_SPEND_TX=$( echo $SPEND_DETAILS | jq -r '.tx_hex' ) \
+
[source,bash]
-----
-b-cli decoderawtransaction $RAW_P2TSH_SPEND_TX
+b-cli decoderawtransaction $RAW_P2MR_SPEND_TX
-----
+
Pay particular attention to the `vin.txinwitness` field.
-The following depicts the elements of a P2TSH witness stack.
+The following depicts the elements of a P2MR witness stack.
+
-image::images/p2tsh_witness.png[]
+image::images/p2mr_witness.png[]
+
Do the three elements (script input, script and control block) of the witness stack for this _script path spend_ correspond ?
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 P2TSH enabled Bitcoin Core:
+. Test standardness of the spending tx by sending to local mempool of P2MR enabled Bitcoin Core:
+
[source,bash]
-----
-b-cli testmempoolaccept '["'''$RAW_P2TSH_SPEND_TX'''"]'
+b-cli testmempoolaccept '["'''$RAW_P2MR_SPEND_TX'''"]'
-----
. Submit tx:
+
[source,bash]
-----
-export P2TSH_SPENDING_TX_ID=$( b-cli sendrawtransaction $RAW_P2TSH_SPEND_TX ) \
- && echo $P2TSH_SPENDING_TX_ID
+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_P2TSH_SPEND_TX
+NOTE: Should return same tx id as was included in $RAW_P2MR_SPEND_TX
-== Mine P2TSH Spend TX
+== Mine P2MR Spend TX
. View tx in mempool:
+
[source,bash]
-----
-b-cli getrawtransaction $P2TSH_SPENDING_TX_ID 1
+b-cli getrawtransaction $P2MR_SPENDING_TX_ID 1
-----
+
NOTE: There will not yet be a field `blockhash` in the response.
. Monitor the mempool.space instance at link:http://signet.bip360.org[signet.bip360.org] until a new block is mined.
-. While still in the mempool.space instance at link:http://signet.bip360.org[signet.bip360.org], lookup your tx (denoted by: $P2TSH_SPENDING_TX_ID ) in the top-right search bar:
+. While still in the mempool.space instance at link:http://signet.bip360.org[signet.bip360.org], lookup your tx (denoted by: $P2MR_SPENDING_TX_ID ) in the top-right search bar:
+
image::images/mempool_spending_tx_1.png[]
+
@@ -416,7 +438,7 @@ image::images/mempool_spending_tx_2.png[]
+
[source,bash]
-----
-export BLOCK_HASH=$( b-cli getrawtransaction $P2TSH_SPENDING_TX_ID 1 | jq -r '.blockhash' ) \
+export BLOCK_HASH=$( b-cli getrawtransaction $P2MR_SPENDING_TX_ID 1 | jq -r '.blockhash' ) \
&& echo $BLOCK_HASH
-----
@@ -427,54 +449,46 @@ export BLOCK_HASH=$( b-cli getrawtransaction $P2TSH_SPENDING_TX_ID 1 | jq -r '.b
b-cli getblock $BLOCK_HASH | jq -r .tx
-----
+
-You should see your tx (as per $P2TSH_SPENDING_TX_ID) in the list.
+You should see your tx (as per $P2MR_SPENDING_TX_ID) in the list.
+
-Congratulations!! You have created, funded and spent from a P2TSH address.
+Congratulations!! You have created, funded and spent from a P2MR address.
== Appendix
-[[build_p2tsh]]
-=== Build P2TSH / PQC Enabled Bitcoin Core
+[[build_p2mr]]
+=== Build P2MR / PQC Enabled Bitcoin Core
*FOR THE PURPOSE OF THE WORKSHOP, YOU CAN IGNORE THIS SECTION*
-The link:https://github.com/jbride/bitcoin/tree/p2tsh[p2tsh branch] of bitcoin core is needed.
+The link:https://github.com/jbride/bitcoin/tree/p2mr[p2mr branch] of bitcoin core is needed.
-Build instructions for the `p2tsh` branch are the same as `master` and is documented link:https://github.com/bitcoin/bitcoin/blob/master/doc/build-unix.md[here].
+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 `p2tsh` branch of bitcoin core:
+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
+export BITCOIN_SOURCE_DIR=/path/to/root/dir/of/cloned/bitcoin/source
-----
. build
+
-----
-$ cmake -B 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
+cmake --build build -j$(nproc)
-----
-.. signet:
+. run bitcoind in signet mode:
+
-----
-$ ./build/bin/bitcoind -daemon=0 -signet=1 -txindex -prune=0
+./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.
@@ -484,27 +498,27 @@ link:https://edil.com.br/blog/creating-a-custom-bitcoin-signet[This workshop] pr
*FOR THE PURPOSE OF THE WORKSHOP, YOU CAN IGNORE THIS SECTION*
-The `p2tsh-pqc` branch of this project includes a dependency on the link:https://crates.io/crates/libbitcoinpqc[libbitcoinpqc crate].
+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 `p2tsh-ref` must have cmake and a C toolchain installed locally.
+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
+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
+sudo dnf5 update
+sudo dnf5 install cmake make gcc gcc-c++ clang clang-libs llvm-devel
-----
diff --git a/bip-0360/ref-impl/rust/docs/p2tr-end-to-end.adoc b/bip-0360/ref-impl/rust/docs/p2tr-end-to-end.adoc
index df73a038f3..51e0a2fdb1 100644
--- a/bip-0360/ref-impl/rust/docs/p2tr-end-to-end.adoc
+++ b/bip-0360/ref-impl/rust/docs/p2tr-end-to-end.adoc
@@ -17,11 +17,11 @@ Execute in Bitcoin Core `regtest` mode.
=== Bitcoin Core
-The link:https://github.com/jbride/bitcoin/tree/p2tsh-pqc[p2tsh branch] of bitcoin core is needed.
+The link:https://github.com/jbride/bitcoin/tree/p2mr-pqc[p2mr branch] of bitcoin core is needed.
-Build instructions for the `p2tsh` branch are the same as `master` and is documented link:https://github.com/bitcoin/bitcoin/blob/master/doc/build-unix.md[here].
+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 `p2tsh` branch of bitcoin core:
+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
+
@@ -71,7 +71,7 @@ $ b-reg -named createwallet \
+
-----
$ export TOTAL_LEAF_COUNT=5 \
- && export LEAF_OF_INTEREST=4
+ && 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.
diff --git a/bip-0360/ref-impl/rust/docs/stack_element_size_performance_tests.adoc b/bip-0360/ref-impl/rust/docs/stack_element_size_performance_tests.adoc
index 468760626a..0addb22bc3 100644
--- a/bip-0360/ref-impl/rust/docs/stack_element_size_performance_tests.adoc
+++ b/bip-0360/ref-impl/rust/docs/stack_element_size_performance_tests.adoc
@@ -243,7 +243,7 @@ This analysis shows that the OP_DUP operation has a measurable but manageable pe
=== Procedure
-* Testing is done using functionality found in the link:https://github.com/jbride/bitcoin/tree/p2tsh-pqc[p2tsh branch] of Bitcoin Core.
+* Testing is done using functionality found in the link:https://github.com/jbride/bitcoin/tree/p2mr-pqc[p2mr branch] of Bitcoin Core.
* Compilation of Bitcoin Core is done using the following `cmake` flags:
+
diff --git a/bip-0360/ref-impl/rust/examples/p2tsh-end-to-end.sh b/bip-0360/ref-impl/rust/examples/p2mr-end-to-end.sh
similarity index 69%
rename from bip-0360/ref-impl/rust/examples/p2tsh-end-to-end.sh
rename to bip-0360/ref-impl/rust/examples/p2mr-end-to-end.sh
index 26273f40d2..2029420dbd 100644
--- a/bip-0360/ref-impl/rust/examples/p2tsh-end-to-end.sh
+++ b/bip-0360/ref-impl/rust/examples/p2mr-end-to-end.sh
@@ -3,14 +3,14 @@ export BITCOIN_SOURCE_DIR=$HOME/bitcoin
export W_NAME=anduro
export USE_PQC=false
export TOTAL_LEAF_COUNT=5
-export LEAF_OF_INTEREST=4
+export LEAF_TO_SPEND_FROM=4
b-cli -named createwallet \
wallet_name=$W_NAME \
descriptors=true \
load_on_startup=true
-export BITCOIN_ADDRESS_INFO=$( cargo run --example p2tsh_construction ) \
+export BITCOIN_ADDRESS_INFO=$( cargo run --example p2mr_construction ) \
&& echo $BITCOIN_ADDRESS_INFO | jq -r .
export QUANTUM_ROOT=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.taptree_return.tree_root_hex' ) \
@@ -18,13 +18,13 @@ export QUANTUM_ROOT=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.taptree_return.tree_
&& 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 P2TSH_ADDR=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.bech32m_address' )
+ && export P2MR_ADDR=$( echo $BITCOIN_ADDRESS_INFO | jq -r '.utxo_return.bech32m_address' )
b-cli decodescript $LEAF_SCRIPT_HEX | jq -r '.asm'
-export COINBASE_REWARD_TX_ID=$( b-cli -named generatetoaddress 1 $P2TSH_ADDR 5 | jq -r '.[]' ) \
+export COINBASE_REWARD_TX_ID=$( b-cli -named generatetoaddress 1 $P2MR_ADDR 5 | jq -r '.[]' ) \
&& echo $COINBASE_REWARD_TX_ID
-export P2TSH_DESC=$( b-cli getdescriptorinfo "addr($P2TSH_ADDR)" | jq -r '.descriptor' ) \
- && echo $P2TSH_DESC \
- && b-cli scantxoutset start '[{"desc": "'''$P2TSH_DESC'''"}]'
+export P2MR_DESC=$( b-cli getdescriptorinfo "addr($P2MR_ADDR)" | jq -r '.descriptor' ) \
+ && echo $P2MR_DESC \
+ && b-cli scantxoutset start '[{"desc": "'''$P2MR_DESC'''"}]'
diff --git a/bip-0360/ref-impl/rust/examples/p2mr_construction.rs b/bip-0360/ref-impl/rust/examples/p2mr_construction.rs
new file mode 100644
index 0000000000..517ea619b1
--- /dev/null
+++ b/bip-0360/ref-impl/rust/examples/p2mr_construction.rs
@@ -0,0 +1,27 @@
+use p2mr_ref::{create_p2mr_utxo, create_p2mr_multi_leaf_taptree, tap_tree_lock_type};
+use p2mr_ref::data_structures::{UtxoReturn, TaptreeReturn, ConstructionReturn, LeafScriptType};
+use std::env;
+use log::{info, error};
+
+// Inspired by: https://learnmeabitcoin.com/technical/upgrades/taproot/#example-3-script-path-spend-signature
+fn main() -> ConstructionReturn {
+
+ let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
+
+ let tap_tree_lock_type = tap_tree_lock_type();
+ info!("tap_tree_lock_type: {:?}", tap_tree_lock_type);
+
+ let taptree_return: TaptreeReturn = create_p2mr_multi_leaf_taptree();
+ let p2mr_utxo_return: UtxoReturn = create_p2mr_utxo(taptree_return.clone().tree_root_hex);
+
+ // Alert user about SPENDING_LEAF_TYPE requirement when using MIXED mode
+ if tap_tree_lock_type == LeafScriptType::Mixed {
+ info!("NOTE: TAP_TREE_LOCK_TYPE=MIXED requires setting SPENDING_LEAF_TYPE when spending (based on leaf_script_type in output above) as follows:");
+ info!(" export SPENDING_LEAF_TYPE={}", taptree_return.leaf_script_type);
+ }
+
+ return ConstructionReturn {
+ taptree_return: taptree_return,
+ utxo_return: p2mr_utxo_return,
+ };
+}
diff --git a/bip-0360/ref-impl/rust/examples/p2tsh_spend.rs b/bip-0360/ref-impl/rust/examples/p2mr_spend.rs
similarity index 81%
rename from bip-0360/ref-impl/rust/examples/p2tsh_spend.rs
rename to bip-0360/ref-impl/rust/examples/p2mr_spend.rs
index fc326f5c45..7dcbc15a63 100644
--- a/bip-0360/ref-impl/rust/examples/p2tsh_spend.rs
+++ b/bip-0360/ref-impl/rust/examples/p2mr_spend.rs
@@ -1,6 +1,6 @@
-use p2tsh_ref::{ pay_to_p2wpkh_tx, verify_schnorr_signature_via_bytes, verify_slh_dsa_via_bytes, parse_leaf_script_type };
+use p2mr_ref::{ pay_to_p2wpkh_tx, verify_schnorr_signature_via_bytes, verify_slh_dsa_via_bytes, tap_tree_lock_type };
-use p2tsh_ref::data_structures::{SpendDetails, LeafScriptType};
+use p2mr_ref::data_structures::{SpendDetails, LeafScriptType};
use std::env;
use log::{info, error};
@@ -52,14 +52,42 @@ fn main() -> SpendDetails {
error!("CONTROL_BLOCK_HEX environment variable is required but not set");
std::process::exit(1);
});
- info!("P2TSH control block size: {}", control_block_bytes.len());
+ info!("P2MR control block size: {}", control_block_bytes.len());
- // LEAF_SCRIPT_TYPE environment variable is required to determine key structure
- let leaf_script_type: LeafScriptType = parse_leaf_script_type();
+ // TAP_TREE_LOCK_TYPE environment variable is required to determine key structure
+ let leaf_script_type: LeafScriptType = tap_tree_lock_type();
info!("leaf_script_type: {:?}", leaf_script_type);
- // Parse private keys based on script type
- let leaf_script_priv_keys_bytes: Vec> = match leaf_script_type {
+ // For Mixed trees, we need to determine the actual leaf type via SPENDING_LEAF_TYPE
+ let effective_leaf_type: LeafScriptType = if leaf_script_type == LeafScriptType::Mixed {
+ match env::var("SPENDING_LEAF_TYPE") {
+ Ok(value) => match value.as_str() {
+ "SCHNORR_ONLY" => {
+ info!("SPENDING_LEAF_TYPE: SCHNORR_ONLY");
+ LeafScriptType::SchnorrOnly
+ },
+ "SLH_DSA_ONLY" => {
+ info!("SPENDING_LEAF_TYPE: SLH_DSA_ONLY");
+ LeafScriptType::SlhDsaOnly
+ },
+ _ => {
+ error!("Invalid SPENDING_LEAF_TYPE '{}'. Must be SCHNORR_ONLY or SLH_DSA_ONLY", value);
+ std::process::exit(1);
+ }
+ },
+ Err(_) => {
+ error!("SPENDING_LEAF_TYPE environment variable is required when TAP_TREE_LOCK_TYPE=MIXED");
+ error!("Set SPENDING_LEAF_TYPE to the actual type of the leaf being spent (SCHNORR_ONLY or SLH_DSA_ONLY).");
+ error!("The leaf type is returned in the 'leaf_script_type' field of the tree construction output.");
+ std::process::exit(1);
+ }
+ }
+ } else {
+ leaf_script_type
+ };
+
+ // Parse private keys based on effective script type
+ let leaf_script_priv_keys_bytes: Vec> = match effective_leaf_type {
LeafScriptType::SlhDsaOnly => {
let priv_keys_hex_array = env::var("LEAF_SCRIPT_PRIV_KEYS_HEX")
.unwrap_or_else(|_| {
@@ -110,7 +138,7 @@ fn main() -> SpendDetails {
}
vec![priv_keys_bytes]
},
- LeafScriptType::SchnorrAndSlhDsa => {
+ LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf => {
let priv_keys_hex_array = env::var("LEAF_SCRIPT_PRIV_KEYS_HEX")
.unwrap_or_else(|_| {
error!("LEAF_SCRIPT_PRIV_KEYS_HEX environment variable is required for SCHNORR_AND_SLH_DSA");
@@ -145,6 +173,10 @@ fn main() -> SpendDetails {
vec![schnorr_priv_key_bytes, slh_dsa_priv_key_bytes]
},
+ LeafScriptType::Mixed => {
+ // This case should never be reached because Mixed is resolved to effective_leaf_type above
+ unreachable!("Mixed should have been resolved to effective_leaf_type");
+ },
LeafScriptType::NotApplicable => {
panic!("LeafScriptType::NotApplicable is not applicable");
}
@@ -179,13 +211,13 @@ fn main() -> SpendDetails {
leaf_script_priv_keys_bytes, // Now passing Vec> instead of Vec
spend_output_pubkey_hash_bytes,
spend_output_amount_sats,
- leaf_script_type
+ effective_leaf_type // Use effective type (resolved from SPENDING_LEAF_TYPE if Mixed)
);
// Remove first and last byte from leaf_script_bytes to get tapleaf_pubkey_bytes
let tapleaf_pubkey_bytes: Vec = leaf_script_bytes[1..leaf_script_bytes.len()-1].to_vec();
-
- match leaf_script_type {
+
+ match effective_leaf_type {
LeafScriptType::SlhDsaOnly => {
let is_valid: bool = verify_slh_dsa_via_bytes(&result.sig_bytes, &result.sighash, &tapleaf_pubkey_bytes);
info!("is_valid: {}", is_valid);
@@ -197,7 +229,7 @@ fn main() -> SpendDetails {
&tapleaf_pubkey_bytes);
info!("is_valid: {}", is_valid);
},
- LeafScriptType::SchnorrAndSlhDsa => {
+ LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf => {
// For combined scripts, we need to separate the signatures
// The sig_bytes contains: [schnorr_sig (64 bytes), slh_dsa_sig (7856 bytes)] (raw signatures without sighash)
let schnorr_sig_len = 64; // Schnorr signature is 64 bytes
@@ -239,6 +271,10 @@ fn main() -> SpendDetails {
let both_valid = schnorr_is_valid && slh_dsa_is_valid;
info!("Both signatures valid: {}", both_valid);
}
+ LeafScriptType::Mixed => {
+ // This case should never be reached because Mixed is resolved to effective_leaf_type above
+ unreachable!("Mixed should have been resolved to effective_leaf_type");
+ }
LeafScriptType::NotApplicable => {
panic!("LeafScriptType::NotApplicable is not applicable");
}
diff --git a/bip-0360/ref-impl/rust/examples/p2tr_construction.rs b/bip-0360/ref-impl/rust/examples/p2tr_construction.rs
index 506fc5591a..328c16fd71 100644
--- a/bip-0360/ref-impl/rust/examples/p2tr_construction.rs
+++ b/bip-0360/ref-impl/rust/examples/p2tr_construction.rs
@@ -1,5 +1,5 @@
-use p2tsh_ref::{create_p2tr_utxo, create_p2tr_multi_leaf_taptree};
-use p2tsh_ref::data_structures::{UtxoReturn, TaptreeReturn, ConstructionReturn};
+use p2mr_ref::{create_p2tr_utxo, create_p2tr_multi_leaf_taptree};
+use p2mr_ref::data_structures::{UtxoReturn, TaptreeReturn, ConstructionReturn};
// Inspired by: https://learnmeabitcoin.com/technical/upgrades/taproot/#example-3-script-path-spend-signature
fn main() -> ConstructionReturn {
diff --git a/bip-0360/ref-impl/rust/examples/p2tr_spend.rs b/bip-0360/ref-impl/rust/examples/p2tr_spend.rs
index cf60ffd744..636468c168 100644
--- a/bip-0360/ref-impl/rust/examples/p2tr_spend.rs
+++ b/bip-0360/ref-impl/rust/examples/p2tr_spend.rs
@@ -1,6 +1,6 @@
-use p2tsh_ref::{ pay_to_p2wpkh_tx , verify_schnorr_signature_via_bytes};
+use p2mr_ref::{ pay_to_p2wpkh_tx , verify_schnorr_signature_via_bytes};
-use p2tsh_ref::data_structures::{SpendDetails, LeafScriptType};
+use p2mr_ref::data_structures::{SpendDetails, LeafScriptType};
use std::env;
use log::{info, error};
diff --git a/bip-0360/ref-impl/rust/examples/p2tsh_construction.rs b/bip-0360/ref-impl/rust/examples/p2tsh_construction.rs
deleted file mode 100644
index 1130708903..0000000000
--- a/bip-0360/ref-impl/rust/examples/p2tsh_construction.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use p2tsh_ref::{create_p2tsh_utxo, create_p2tsh_multi_leaf_taptree, parse_leaf_script_type};
-use p2tsh_ref::data_structures::{UtxoReturn, TaptreeReturn, ConstructionReturn, LeafScriptType};
-use std::env;
-use log::{info, error};
-
-// Inspired by: https://learnmeabitcoin.com/technical/upgrades/taproot/#example-3-script-path-spend-signature
-fn main() -> ConstructionReturn {
-
- let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
-
- let leaf_script_type = parse_leaf_script_type();
- info!("leaf_script_type: {:?}", leaf_script_type);
-
- let taptree_return: TaptreeReturn = create_p2tsh_multi_leaf_taptree();
- let p2tsh_utxo_return: UtxoReturn = create_p2tsh_utxo(taptree_return.clone().tree_root_hex);
-
- return ConstructionReturn {
- taptree_return: taptree_return,
- utxo_return: p2tsh_utxo_return,
- };
-}
diff --git a/bip-0360/ref-impl/rust/examples/schnorr_example.rs b/bip-0360/ref-impl/rust/examples/schnorr_example.rs
index 84b5aa3907..4723d99bd5 100644
--- a/bip-0360/ref-impl/rust/examples/schnorr_example.rs
+++ b/bip-0360/ref-impl/rust/examples/schnorr_example.rs
@@ -5,7 +5,7 @@ use bitcoin::key::{Secp256k1};
use bitcoin::hashes::{sha256::Hash, Hash as HashTrait};
use bitcoin::secp256k1::{Message};
-use p2tsh_ref::{ acquire_schnorr_keypair, verify_schnorr_signature };
+use p2mr_ref::{ acquire_schnorr_keypair, verify_schnorr_signature };
/* Secp256k1 implements the Signing trait when it's initialized in signing mode.
It's important to note that Secp256k1 has different capabilities depending on how it's constructed:
diff --git a/bip-0360/ref-impl/rust/src/data_structures.rs b/bip-0360/ref-impl/rust/src/data_structures.rs
index d5e6ad06ee..11f653aec7 100644
--- a/bip-0360/ref-impl/rust/src/data_structures.rs
+++ b/bip-0360/ref-impl/rust/src/data_structures.rs
@@ -14,7 +14,9 @@ pub enum LeafScriptType {
/// Script requires only Schnorr signature
SchnorrOnly,
/// Script requires both Schnorr and SLH-DSA signatures (in that order)
- SchnorrAndSlhDsa,
+ ConcatenatedSchnorrAndSlhDsaSameLeaf,
+ /// Leaves of TapTree are mixed. Some leaves are locked using Schnorr and others are locked using SLH-DSA
+ Mixed,
/// Script type is not applicable
NotApplicable,
}
@@ -22,23 +24,50 @@ pub enum LeafScriptType {
impl LeafScriptType {
/// Check if this script type uses SLH-DSA
pub fn uses_slh_dsa(&self) -> bool {
- matches!(self, LeafScriptType::SlhDsaOnly | LeafScriptType::SchnorrAndSlhDsa)
+ matches!(self, LeafScriptType::SlhDsaOnly | LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf)
}
-
+
/// Check if this script type uses Schnorr
pub fn uses_schnorr(&self) -> bool {
- matches!(self, LeafScriptType::SchnorrOnly | LeafScriptType::SchnorrAndSlhDsa)
+ matches!(self, LeafScriptType::SchnorrOnly | LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf)
}
-
+
/// Check if this script type requires both signature types
pub fn requires_both(&self) -> bool {
- matches!(self, LeafScriptType::SchnorrAndSlhDsa)
+ matches!(self, LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf)
}
-
+
+ /// Check if TapTree uses Schnorr for some leaves and SLH-DSA for others
+ pub fn uses_mixed(&self) -> bool {
+ matches!(self, LeafScriptType::Mixed)
+ }
+
/// Check if this script type is not applicable
pub fn is_not_applicable(&self) -> bool {
matches!(self, LeafScriptType::NotApplicable)
}
+
+ /// Convert to string representation for serialization
+ pub fn to_string(&self) -> String {
+ match self {
+ LeafScriptType::SlhDsaOnly => "SLH_DSA_ONLY".to_string(),
+ LeafScriptType::SchnorrOnly => "SCHNORR_ONLY".to_string(),
+ LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf => "CONCATENATED_SCHNORR_AND_SLH_DSA".to_string(),
+ LeafScriptType::Mixed => "MIXED".to_string(),
+ LeafScriptType::NotApplicable => "NOT_APPLICABLE".to_string(),
+ }
+ }
+
+ /// Parse from string representation
+ pub fn from_string(s: &str) -> Self {
+ match s {
+ "SLH_DSA_ONLY" => LeafScriptType::SlhDsaOnly,
+ "SCHNORR_ONLY" => LeafScriptType::SchnorrOnly,
+ "CONCATENATED_SCHNORR_AND_SLH_DSA" => LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf,
+ "MIXED" => LeafScriptType::Mixed,
+ _ => LeafScriptType::NotApplicable,
+ }
+ }
}
#[derive(Debug, Serialize)]
@@ -89,6 +118,9 @@ pub struct TestVector {
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TestVectorGiven {
+ #[serde(rename = "internalPubkey")]
+ pub internal_pubkey: Option,
+
#[serde(rename = "scriptTree")]
pub script_tree: Option,
@@ -328,6 +360,8 @@ pub struct TaptreeReturn {
pub leaf_script_hex: String,
pub tree_root_hex: String,
pub control_block_hex: String,
+ /// The script type of the leaf being returned (needed for spending)
+ pub leaf_script_type: String,
}
impl std::process::Termination for TaptreeReturn {
@@ -442,6 +476,62 @@ impl MultiKeypair {
}
}
+/// Information about a single leaf in a mixed-type tree
+/// Used when different leaves in the same tree use different algorithms
+#[derive(Debug, Clone)]
+pub struct MixedLeafInfo {
+ /// The leaf index in the tree
+ pub leaf_index: u32,
+ /// The script type for this specific leaf
+ pub leaf_script_type: LeafScriptType,
+ /// The keypairs for this leaf
+ pub keypairs: MultiKeypair,
+ /// The script for this leaf
+ pub script: Vec,
+}
+
+impl MixedLeafInfo {
+ /// Create a new MixedLeafInfo for a Schnorr-only leaf
+ pub fn new_schnorr(leaf_index: u32, keypairs: MultiKeypair, script: Vec) -> Self {
+ Self {
+ leaf_index,
+ leaf_script_type: LeafScriptType::SchnorrOnly,
+ keypairs,
+ script,
+ }
+ }
+
+ /// Create a new MixedLeafInfo for an SLH-DSA-only leaf
+ pub fn new_slh_dsa(leaf_index: u32, keypairs: MultiKeypair, script: Vec) -> Self {
+ Self {
+ leaf_index,
+ leaf_script_type: LeafScriptType::SlhDsaOnly,
+ keypairs,
+ script,
+ }
+ }
+
+ /// Create a new MixedLeafInfo for a combined Schnorr+SLH-DSA leaf
+ pub fn new_combined(leaf_index: u32, keypairs: MultiKeypair, script: Vec) -> Self {
+ Self {
+ leaf_index,
+ leaf_script_type: LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf,
+ keypairs,
+ script,
+ }
+ }
+
+ /// Get the secret key bytes for this leaf
+ pub fn secret_key_bytes(&self) -> Vec> {
+ self.keypairs.secret_key_bytes()
+ }
+
+ /// Get the public key bytes for this leaf
+ pub fn public_key_bytes(&self) -> Vec> {
+ self.keypairs.public_key_bytes()
+ }
+}
+
impl UnifiedKeypair {
/// Create a new Schnorr keypair
pub fn new_schnorr(secret_key: SecretKey, public_key: XOnlyPublicKey) -> Self {
diff --git a/bip-0360/ref-impl/rust/src/error.rs b/bip-0360/ref-impl/rust/src/error.rs
index 100a48b7da..d2cb98b9e5 100644
--- a/bip-0360/ref-impl/rust/src/error.rs
+++ b/bip-0360/ref-impl/rust/src/error.rs
@@ -1,8 +1,13 @@
use thiserror::Error;
#[derive(Error, Debug)]
-pub enum P2TSHError {
- #[error("P2TSH requires a script tree with at least one leaf")]
+pub enum P2MRError {
+
+ #[error("P2TR requires witness version of 1")]
+ P2trRequiresWitnessVersion1,
+
+
+ #[error("P2MR requires a script tree with at least one leaf")]
MissingScriptTreeLeaf,
// We can add more specific error variants here as needed
diff --git a/bip-0360/ref-impl/rust/src/lib.rs b/bip-0360/ref-impl/rust/src/lib.rs
index 3a7b82c0d2..e9c9816d14 100644
--- a/bip-0360/ref-impl/rust/src/lib.rs
+++ b/bip-0360/ref-impl/rust/src/lib.rs
@@ -18,13 +18,13 @@ use bitcoin::{ Amount, TxOut, WPubkeyHash,
transaction::{Transaction, Sequence}
};
-use bitcoin::p2tsh::{P2tshScriptBuf, P2tshBuilder, P2tshSpendInfo, P2tshControlBlock, P2TSH_LEAF_VERSION};
+use bitcoin::p2mr::{P2mrScriptBuf, P2mrBuilder, P2mrSpendInfo, P2mrControlBlock, P2MR_LEAF_VERSION};
use bitcoinpqc::{
generate_keypair, public_key_size, secret_key_size, Algorithm, KeyPair, sign, verify,
};
-use data_structures::{SpendDetails, UtxoReturn, TaptreeReturn, UnifiedKeypair, MultiKeypair, LeafScriptType};
+use data_structures::{SpendDetails, UtxoReturn, TaptreeReturn, UnifiedKeypair, MultiKeypair, LeafScriptType, MixedLeafInfo};
/* Secp256k1 implements the Signing trait when it's initialized in signing mode.
It's important to note that Secp256k1 has different capabilities depending on how it's constructed:
@@ -34,7 +34,13 @@ use data_structures::{SpendDetails, UtxoReturn, TaptreeReturn, UnifiedKeypair, M
*/
static SECP: Lazy> = Lazy::new(Secp256k1::new);
-fn create_huffman_tree(leaf_script_type: LeafScriptType) -> (Vec<(u32, ScriptBuf)>, MultiKeypair, ScriptBuf) {
+/// Creates a Huffman tree with leaves of the specified script type.
+///
+/// For Mixed type, leaves alternate between Schnorr (even indices) and SLH-DSA (odd indices).
+/// The LEAF_TO_SPEND_FROM_TYPE env var can override the type for the leaf of interest.
+///
+/// Returns: (huffman_entries, keypairs_of_interest, script_buf_of_interest, actual_leaf_type)
+fn create_huffman_tree(leaf_script_type: LeafScriptType) -> (Vec<(u32, ScriptBuf)>, MultiKeypair, ScriptBuf, LeafScriptType) {
let mut total_leaf_count: u32 = 1;
if let Ok(env_value) = env::var("TOTAL_LEAF_COUNT") {
@@ -42,30 +48,56 @@ fn create_huffman_tree(leaf_script_type: LeafScriptType) -> (Vec<(u32, ScriptBuf
total_leaf_count = parsed_value;
}
}
-
- let mut leaf_of_interest: u32 = 0;
- if let Ok(env_value) = env::var("LEAF_OF_INTEREST") {
+
+ let mut leaf_to_spend_from: u32 = 0;
+ if let Ok(env_value) = env::var("LEAF_TO_SPEND_FROM") {
if let Ok(parsed_value) = env_value.parse::() {
- leaf_of_interest = parsed_value;
+ leaf_to_spend_from = parsed_value;
}
}
+ // For Mixed mode, allow overriding the type of the leaf of interest
+ let leaf_to_spend_from_type: Option = if leaf_script_type == LeafScriptType::Mixed {
+ env::var("LEAF_TO_SPEND_FROM_TYPE").ok().map(|s| LeafScriptType::from_string(&s))
+ } else {
+ None
+ };
+
if total_leaf_count < 1 {
panic!("total_leaf_count must be greater than 0");
}
- if leaf_of_interest >= total_leaf_count {
- panic!("leaf_of_interest must be less than total_leaf_count and greater than 0");
+ if leaf_to_spend_from >= total_leaf_count {
+ panic!("leaf_to_spend_from must be less than total_leaf_count and greater than 0");
}
- debug!("Creating multi-leaf taptree with total_leaf_count: {}, leaf_of_interest: {}", total_leaf_count, leaf_of_interest);
+ debug!("Creating multi-leaf taptree with total_leaf_count: {}, leaf_to_spend_from: {}", total_leaf_count, leaf_to_spend_from);
let mut huffman_entries: Vec<(u32, ScriptBuf)> = vec![];
let mut keypairs_of_interest: Option = None;
let mut script_buf_of_interest: Option = None;
+ let mut actual_leaf_type_of_interest: LeafScriptType = leaf_script_type;
+
for leaf_index in 0..total_leaf_count {
let keypairs: MultiKeypair;
let script_buf: ScriptBuf;
-
- match leaf_script_type {
+
+ // Determine the effective script type for this leaf
+ let effective_script_type = if leaf_script_type == LeafScriptType::Mixed {
+ // For Mixed mode, check if this is the leaf of interest with an override
+ if leaf_index == leaf_to_spend_from && leaf_to_spend_from_type.is_some() {
+ leaf_to_spend_from_type.unwrap()
+ } else {
+ // Default pattern: even indices use Schnorr, odd indices use SLH-DSA
+ if leaf_index % 2 == 0 {
+ LeafScriptType::SchnorrOnly
+ } else {
+ LeafScriptType::SlhDsaOnly
+ }
+ }
+ } else {
+ leaf_script_type
+ };
+
+ match effective_script_type {
LeafScriptType::SchnorrOnly => {
let schnorr_keypair = acquire_schnorr_keypair();
keypairs = MultiKeypair::new_schnorr_only(schnorr_keypair);
@@ -86,19 +118,19 @@ fn create_huffman_tree(leaf_script_type: LeafScriptType) -> (Vec<(u32, ScriptBuf
script_buf_bytes.push(0x7f); // OP_SUBSTR
script_buf = ScriptBuf::from_bytes(script_buf_bytes);
},
- LeafScriptType::SchnorrAndSlhDsa => {
+ LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf => {
// For combined scripts, we need both keypairs
let schnorr_keypair = acquire_schnorr_keypair();
let slh_dsa_keypair = acquire_slh_dsa_keypair();
keypairs = MultiKeypair::new_combined(schnorr_keypair, slh_dsa_keypair);
-
+
let schnorr_pubkey = keypairs.schnorr_keypair().unwrap().public_key_bytes();
let slh_dsa_pubkey = keypairs.slh_dsa_keypair().unwrap().public_key_bytes();
-
+
// Debug: Print the private key used for script construction
info!("SLH-DSA DEBUG: Script construction using private key: {}", hex::encode(keypairs.slh_dsa_keypair().unwrap().secret_key_bytes()));
info!("SLH-DSA DEBUG: Script construction using public key: {}", hex::encode(&slh_dsa_pubkey));
-
+
// Combined script: OP_CHECKSIG OP_SUBSTR OP_BOOLAND OP_VERIFY
let mut script_buf_bytes = vec![0x20]; // OP_PUSHBYTES_32
script_buf_bytes.extend_from_slice(&schnorr_pubkey);
@@ -110,61 +142,79 @@ fn create_huffman_tree(leaf_script_type: LeafScriptType) -> (Vec<(u32, ScriptBuf
script_buf_bytes.push(0x69); // OP_VERIFY
script_buf = ScriptBuf::from_bytes(script_buf_bytes);
}
+ LeafScriptType::Mixed => {
+ // This shouldn't happen as Mixed is resolved to a specific type above
+ panic!("LeafScriptType::Mixed should have been resolved to a specific type");
+ }
LeafScriptType::NotApplicable => {
panic!("LeafScriptType::NotApplicable is not applicable");
}
}
-
+
let random_weight = thread_rng().gen_range(0..total_leaf_count);
-
+
let huffman_entry = (random_weight, script_buf.clone());
huffman_entries.push(huffman_entry);
- if leaf_index == leaf_of_interest {
+ if leaf_index == leaf_to_spend_from {
keypairs_of_interest = Some(keypairs);
script_buf_of_interest = Some(script_buf.clone());
- debug!("Selected leaf: weight: {}, script: {:?}", random_weight, script_buf);
+ actual_leaf_type_of_interest = effective_script_type;
+ debug!("Selected leaf {}: type: {:?}, weight: {}, script: {:?}",
+ leaf_index, effective_script_type, random_weight, script_buf);
}
}
- return (huffman_entries, keypairs_of_interest.unwrap(), script_buf_of_interest.unwrap());
+ return (huffman_entries, keypairs_of_interest.unwrap(), script_buf_of_interest.unwrap(), actual_leaf_type_of_interest);
}
-/// Parses the LEAF_SCRIPT_TYPE environment variable and returns the corresponding LeafScriptType.
-/// Defaults to LeafScriptType::SchnorrOnly if the environment variable is not set or has an invalid value.
-pub fn parse_leaf_script_type() -> LeafScriptType {
- match env::var("LEAF_SCRIPT_TYPE")
- .unwrap_or_else(|_| "SCHNORR_ONLY".to_string())
- .as_str() {
- "SLH_DSA_ONLY" => LeafScriptType::SlhDsaOnly,
- "SCHNORR_ONLY" => LeafScriptType::SchnorrOnly,
- "SCHNORR_AND_SLH_DSA" => LeafScriptType::SchnorrAndSlhDsa,
- _ => {
- error!("Invalid LEAF_SCRIPT_TYPE. Must be one of: SLH_DSA_ONLY, SCHNORR_ONLY, SCHNORR_AND_SLH_DSA");
- LeafScriptType::SchnorrOnly
+/// Parses the TAP_TREE_LOCK_TYPE environment variable and returns the corresponding LeafScriptType.
+/// Defaults to LeafScriptType::SchnorrOnly if the environment variable is not set.
+/// Exits with error code 1 if an invalid value is provided.
+///
+/// Supported values:
+/// - SLH_DSA_ONLY: All leaves use SLH-DSA signatures
+/// - SCHNORR_ONLY: All leaves use Schnorr signatures
+/// - CONCATENATED_SCHNORR_AND_SLH_DSA: All leaves require both Schnorr and SLH-DSA signatures
+/// - MIXED: Different leaves use different algorithms (Schnorr or SLH-DSA) (default)
+pub fn tap_tree_lock_type() -> LeafScriptType {
+ match env::var("TAP_TREE_LOCK_TYPE") {
+ Ok(value) => match value.as_str() {
+ "SLH_DSA_ONLY" => LeafScriptType::SlhDsaOnly,
+ "SCHNORR_ONLY" => LeafScriptType::SchnorrOnly,
+ "CONCATENATED_SCHNORR_AND_SLH_DSA" => LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf,
+ "MIXED" => LeafScriptType::Mixed,
+ _ => {
+ error!("Invalid TAP_TREE_LOCK_TYPE '{}'. Must be one of: SLH_DSA_ONLY, SCHNORR_ONLY, CONCATENATED_SCHNORR_AND_SLH_DSA, MIXED", value);
+ std::process::exit(1);
+ }
+ },
+ Err(_) => {
+ // Default to Mixed if not set
+ LeafScriptType::Mixed
}
}
}
-pub fn create_p2tsh_multi_leaf_taptree() -> TaptreeReturn {
- let leaf_script_type = parse_leaf_script_type();
-
- let (huffman_entries, keypairs_of_interest, script_buf_of_interest) = create_huffman_tree(leaf_script_type);
- let p2tsh_builder: P2tshBuilder = P2tshBuilder::with_huffman_tree(huffman_entries).unwrap();
+pub fn create_p2mr_multi_leaf_taptree() -> TaptreeReturn {
+ let leaf_script_type = tap_tree_lock_type();
+ let (huffman_entries, keypairs_of_interest, script_buf_of_interest, actual_leaf_type) = create_huffman_tree(leaf_script_type);
+ let p2mr_builder: P2mrBuilder = P2mrBuilder::with_huffman_tree(huffman_entries).unwrap();
- let p2tsh_spend_info: P2tshSpendInfo = p2tsh_builder.clone().finalize().unwrap();
- let merkle_root:TapNodeHash = p2tsh_spend_info.merkle_root.unwrap();
-
- let tap_tree: TapTree = p2tsh_builder.clone().into_inner().try_into_taptree().unwrap();
+ let p2mr_spend_info: P2mrSpendInfo = p2mr_builder.clone().finalize().unwrap();
+ let merkle_root:TapNodeHash = p2mr_spend_info.merkle_root.unwrap();
+
+
+ let tap_tree: TapTree = p2mr_builder.clone().into_inner().try_into_taptree().unwrap();
let mut script_leaves: ScriptLeaves = tap_tree.script_leaves();
let script_leaf = script_leaves
.find(|leaf| leaf.script() == script_buf_of_interest.as_script())
.expect("Script leaf not found");
- let merkle_root_node_info: NodeInfo = p2tsh_builder.clone().into_inner().try_into_node_info().unwrap();
+ let merkle_root_node_info: NodeInfo = p2mr_builder.clone().into_inner().try_into_node_info().unwrap();
let merkle_root: TapNodeHash = merkle_root_node_info.node_hash();
- let leaf_hash: TapLeafHash = TapLeafHash::from_script(script_leaf.script(), LeafVersion::from_consensus(P2TSH_LEAF_VERSION).unwrap());
+ let leaf_hash: TapLeafHash = TapLeafHash::from_script(script_leaf.script(), LeafVersion::from_consensus(P2MR_LEAF_VERSION).unwrap());
// Convert leaf hash to big-endian for display (like Bitcoin Core)
let mut leaf_hash_bytes = leaf_hash.as_raw_hash().to_byte_array().to_vec();
@@ -180,11 +230,11 @@ pub fn create_p2tsh_multi_leaf_taptree() -> TaptreeReturn {
info!("Leaf script: {}, merkle branch: {:?}", leaf_script, merkle_branch);
- let control_block: P2tshControlBlock = P2tshControlBlock{
+ let control_block: P2mrControlBlock = P2mrControlBlock{
merkle_branch: merkle_branch.clone(),
};
- // Not a requirement here but useful to demonstrate what Bitcoin Core does as the verifier when spending from a p2tsh UTXO
+ // Not a requirement here but useful to demonstrate what Bitcoin Core does as the verifier when spending from a p2mr UTXO
control_block.verify_script_in_merkle_root_path(leaf_script, merkle_root);
let control_block_hex: String = hex::encode(control_block.serialize());
@@ -197,17 +247,18 @@ pub fn create_p2tsh_multi_leaf_taptree() -> TaptreeReturn {
leaf_script_hex: leaf_script.to_hex_string(),
tree_root_hex: hex::encode(merkle_root.to_byte_array()),
control_block_hex: control_block_hex,
+ leaf_script_type: actual_leaf_type.to_string(),
};
}
pub fn create_p2tr_multi_leaf_taptree(p2tr_internal_pubkey_hex: String) -> TaptreeReturn {
- let (huffman_entries, keypairs_of_interest, script_buf_of_interest) = create_huffman_tree(LeafScriptType::SchnorrOnly);
+ let (huffman_entries, keypairs_of_interest, script_buf_of_interest, actual_leaf_type) = create_huffman_tree(LeafScriptType::SchnorrOnly);
let pub_key_string = format!("02{}", p2tr_internal_pubkey_hex);
let internal_pubkey: PublicKey = pub_key_string.parse::().unwrap();
let internal_xonly_pubkey: XOnlyPublicKey = internal_pubkey.inner.into();
-
+
let p2tr_builder: TaprootBuilder = TaprootBuilder::with_huffman_tree(huffman_entries).unwrap();
let p2tr_spend_info: TaprootSpendInfo = p2tr_builder.clone().finalize(&SECP, internal_xonly_pubkey).unwrap();
let merkle_root: TapNodeHash = p2tr_spend_info.merkle_root().unwrap();
@@ -253,6 +304,7 @@ pub fn create_p2tr_multi_leaf_taptree(p2tr_internal_pubkey_hex: String) -> Taptr
leaf_script_hex: leaf_script,
tree_root_hex: hex::encode(merkle_root.to_byte_array()),
control_block_hex: control_block_hex,
+ leaf_script_type: actual_leaf_type.to_string(),
};
}
@@ -277,23 +329,23 @@ pub fn get_bitcoin_network() -> Network {
bitcoin_network
}
-pub fn create_p2tsh_utxo(merkle_root_hex: String) -> UtxoReturn {
+pub fn create_p2mr_utxo(merkle_root_hex: String) -> UtxoReturn {
let merkle_root_bytes= hex::decode(merkle_root_hex.clone()).unwrap();
let merkle_root: TapNodeHash = TapNodeHash::from_byte_array(merkle_root_bytes.try_into().unwrap());
/* commit (in scriptPubKey) to the merkle root of all the script path leaves. ie:
- This output key is what gets committed to in the final P2TSH address (ie: scriptPubKey)
+ This output key is what gets committed to in the final P2MR address (ie: scriptPubKey)
*/
- let script_buf: P2tshScriptBuf = P2tshScriptBuf::new_p2tsh(merkle_root);
+ let script_buf: P2mrScriptBuf = P2mrScriptBuf::new_p2mr(merkle_root);
let script: &Script = script_buf.as_script();
let script_pubkey = script.to_hex_string();
let bitcoin_network = get_bitcoin_network();
// derive bech32m address and verify against test vector
- // p2tsh address is comprised of network HRP + WitnessProgram (version + program)
- let bech32m_address = Address::p2tsh(Some(merkle_root), bitcoin_network);
+ // p2mr address is comprised of network HRP + WitnessProgram (version + program)
+ let bech32m_address = Address::p2mr(Some(merkle_root), bitcoin_network);
return UtxoReturn {
script_pubkey_hex: script_pubkey,
@@ -303,7 +355,7 @@ pub fn create_p2tsh_utxo(merkle_root_hex: String) -> UtxoReturn {
}
-// Given script path p2tr or p2tsh UTXO details, spend to p2wpkh
+// Given script path p2tr or p2mr UTXO details, spend to p2wpkh
pub fn pay_to_p2wpkh_tx(
funding_tx_id_bytes: Vec,
funding_utxo_index: u32,
@@ -415,7 +467,7 @@ pub fn pay_to_p2wpkh_tx(
derived_witness.push(&sig_bytes_with_sighash);
debug!("SchnorrOnly signature bytes: {:?}", sig_bytes.len());
},
- LeafScriptType::SchnorrAndSlhDsa => {
+ LeafScriptType::ConcatenatedSchnorrAndSlhDsaSameLeaf => {
if leaf_script_priv_keys_bytes.len() != 2 {
panic!("SchnorrAndSlhDsa requires exactly two private keys (Schnorr first, then SLH-DSA)");
}
@@ -452,6 +504,10 @@ pub fn pay_to_p2wpkh_tx(
witness_sig_bytes.push(TapSighashType::All as u8);
derived_witness.push(&witness_sig_bytes);
}
+ LeafScriptType::Mixed => {
+ // Mixed is not a valid type for spending - the actual leaf type should be used
+ panic!("LeafScriptType::Mixed is not valid for spending. Use the actual leaf type (SchnorrOnly or SlhDsaOnly).");
+ }
LeafScriptType::NotApplicable => {
panic!("LeafScriptType::NotApplicable is not applicable");
}
@@ -496,7 +552,7 @@ pub fn create_p2tr_utxo(merkle_root_hex: String, internal_pubkey_hex: String) ->
let bitcoin_network = get_bitcoin_network();
// 4) derive bech32m address and verify against test vector
- // p2tsh address is comprised of network HRP + WitnessProgram (version + program)
+ // p2mr address is comprised of network HRP + WitnessProgram (version + program)
let bech32m_address = Address::p2tr(
&SECP,
internal_xonly_pubkey,
@@ -561,21 +617,21 @@ fn compact_size(n: u64) -> Vec {
pub fn acquire_schnorr_keypair() -> UnifiedKeypair {
- /* OsRng typically draws from the OS's entropy pool (hardware random num generators, system events, etc), ie:
- * 1. $ cat /proc/sys/kernel/random/entropy_avail
- * 2. $ sudo dmesg | grep -i "random\|rng\|entropy"
-
- The Linux kernel's RNG (/dev/random and /dev/urandom) typically combines multiple entropy sources: ie:
- * Hardware RNG (if available)
- * CPU RNG instructions (RDRAND/RDSEED)
- * Hardware events (disk I/O, network packets, keyboard/mouse input)
- * Timer jitter
- * Interrupt timing
- */
- let keypair = Keypair::new(&SECP, &mut OsRng);
+ /* OsRng typically draws from the OS's entropy pool (hardware random num generators, system events, etc), ie:
+ * 1. $ cat /proc/sys/kernel/random/entropy_avail
+ * 2. $ sudo dmesg | grep -i "random\|rng\|entropy"
+
+ The Linux kernel's RNG (/dev/random and /dev/urandom) typically combines multiple entropy sources: ie:
+ * Hardware RNG (if available)
+ * CPU RNG instructions (RDRAND/RDSEED)
+ * Hardware events (disk I/O, network packets, keyboard/mouse input)
+ * Timer jitter
+ * Interrupt timing
+ */
+ let keypair = Keypair::new(&SECP, &mut OsRng);
- let privkey: SecretKey = keypair.secret_key();
- let pubkey: (XOnlyPublicKey, Parity) = XOnlyPublicKey::from_keypair(&keypair);
+ let privkey: SecretKey = keypair.secret_key();
+ let pubkey: (XOnlyPublicKey, Parity) = XOnlyPublicKey::from_keypair(&keypair);
UnifiedKeypair::new_schnorr(privkey, pubkey.0)
}
diff --git a/bip-0360/ref-impl/rust/tests/p2tsh_pqc_construction.rs b/bip-0360/ref-impl/rust/tests/p2mr_construction.rs
similarity index 66%
rename from bip-0360/ref-impl/rust/tests/p2tsh_pqc_construction.rs
rename to bip-0360/ref-impl/rust/tests/p2mr_construction.rs
index dcbf104f20..38d790d104 100644
--- a/bip-0360/ref-impl/rust/tests/p2tsh_pqc_construction.rs
+++ b/bip-0360/ref-impl/rust/tests/p2mr_construction.rs
@@ -1,108 +1,130 @@
use std::collections::HashSet;
use bitcoin::{Network, ScriptBuf};
use bitcoin::taproot::{LeafVersion, TapTree, ScriptLeaves, TapLeafHash, TaprootMerkleBranch, TapNodeHash};
-use bitcoin::p2tsh::{P2tshBuilder, P2tshControlBlock, P2tshSpendInfo};
+use bitcoin::p2mr::{P2mrBuilder, P2mrControlBlock, P2mrSpendInfo};
use bitcoin::hashes::Hash;
use hex;
use log::debug;
use once_cell::sync::Lazy;
-use p2tsh_ref::data_structures::{TVScriptTree, TestVector, Direction, TestVectors, UtxoReturn};
-use p2tsh_ref::error::P2TSHError;
-use p2tsh_ref::{create_p2tsh_utxo, tagged_hash};
+use p2mr_ref::data_structures::{TVScriptTree, TestVector, Direction, TestVectors, UtxoReturn};
+use p2mr_ref::error::P2MRError;
+use p2mr_ref::{create_p2mr_utxo, tagged_hash};
// This file contains tests that execute against the BIP360 script-path-only test vectors.
static TEST_VECTORS: Lazy = Lazy::new(|| {
- let bip360_test_vectors = include_str!("../../common/tests/data/p2tsh_pqc_construction.json");
+ let bip360_test_vectors = include_str!("../../common/tests/data/p2mr_construction.json");
let test_vectors: TestVectors = serde_json::from_str(bip360_test_vectors).unwrap();
assert_eq!(test_vectors.version, 1);
test_vectors
});
-static P2TSH_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST: &str = "p2tsh_missing_leaf_script_tree_error";
-static P2TSH_SINGLE_LEAF_SCRIPT_TREE_TEST: &str = "p2tsh_single_leaf_script_tree";
-static P2TSH_DIFFERENT_VERSION_LEAVES_TEST: &str = "p2tsh_different_version_leaves";
-static P2TSH_TWO_LEAF_SAME_VERSION_TEST: &str = "p2tsh_two_leaf_same_version";
-static P2TSH_THREE_LEAF_COMPLEX_TEST: &str = "p2tsh_three_leaf_complex";
-static P2TSH_THREE_LEAF_ALTERNATIVE_TEST: &str = "p2tsh_three_leaf_alternative";
-static P2TSH_SIMPLE_LIGHTNING_CONTRACT_TEST: &str = "p2tsh_simple_lightning_contract";
+static P2TR_USING_V2_WITNESS_VERSION_ERROR: &str = "p2tr_using_v2_witness_version_error";
+static P2MR_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST: &str = "p2mr_missing_leaf_script_tree_error";
+static P2MR_SINGLE_LEAF_SCRIPT_TREE_TEST: &str = "p2mr_single_leaf_script_tree";
+static P2MR_DIFFERENT_VERSION_LEAVES_TEST: &str = "p2mr_different_version_leaves";
+static P2MR_TWO_LEAF_SAME_VERSION_TEST: &str = "p2mr_two_leaf_same_version";
+static P2MR_THREE_LEAF_COMPLEX_TEST: &str = "p2mr_three_leaf_complex";
+static P2MR_THREE_LEAF_ALTERNATIVE_TEST: &str = "p2mr_three_leaf_alternative";
+static P2MR_SIMPLE_LIGHTNING_CONTRACT_TEST: &str = "p2mr_simple_lightning_contract";
+
+#[test]
+fn test_p2tr_using_v2_witness_version_error() {
+
+ let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
+
+ let test_vectors = &*TEST_VECTORS;
+ let test_vector = test_vectors.test_vector_map.get(P2TR_USING_V2_WITNESS_VERSION_ERROR).unwrap();
+ let test_result: anyhow::Result<()> = process_test_vector_p2tr(test_vector);
+ assert!(matches!(test_result.unwrap_err().downcast_ref::(),
+ Some(P2MRError::P2trRequiresWitnessVersion1)));
+}
// https://learnmeabitcoin.com/technical/upgrades/taproot/#example-2-script-path-spend-simple
#[test]
-fn test_p2tsh_pqc_missing_leaf_script_tree_error() {
+fn test_p2mr_missing_leaf_script_tree_error() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST).unwrap();
- let test_result: anyhow::Result<()> = process_test_vector_p2tsh(test_vector);
- assert!(matches!(test_result.unwrap_err().downcast_ref::(),
- Some(P2TSHError::MissingScriptTreeLeaf)));
+ let test_vector = test_vectors.test_vector_map.get(P2MR_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST).unwrap();
+ let test_result: anyhow::Result<()> = process_test_vector_p2mr(test_vector);
+ assert!(matches!(test_result.unwrap_err().downcast_ref::(),
+ Some(P2MRError::MissingScriptTreeLeaf)));
}
// https://learnmeabitcoin.com/technical/upgrades/taproot/#example-2-script-path-spend-simple
#[test]
-fn test_p2tsh_pqc_single_leaf_script_tree() {
+fn test_p2mr_single_leaf_script_tree() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_SINGLE_LEAF_SCRIPT_TREE_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_SINGLE_LEAF_SCRIPT_TREE_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_pqc_different_version_leaves() {
+fn test_p2mr_different_version_leaves() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_DIFFERENT_VERSION_LEAVES_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_DIFFERENT_VERSION_LEAVES_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_pqc_simple_lightning_contract() {
+fn test_p2mr_simple_lightning_contract() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_SIMPLE_LIGHTNING_CONTRACT_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_SIMPLE_LIGHTNING_CONTRACT_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_pqc_two_leaf_same_version() {
+fn test_p2mr_two_leaf_same_version() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_TWO_LEAF_SAME_VERSION_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_TWO_LEAF_SAME_VERSION_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_pqc_three_leaf_complex() {
+fn test_p2mr_three_leaf_complex() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_THREE_LEAF_COMPLEX_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_THREE_LEAF_COMPLEX_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_pqc_three_leaf_alternative() {
+fn test_p2mr_three_leaf_alternative() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_THREE_LEAF_ALTERNATIVE_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_THREE_LEAF_ALTERNATIVE_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
+}
+
+fn process_test_vector_p2tr(test_vector: &TestVector) -> anyhow::Result<()> {
+ let script_pubkey_hex = test_vector.expected.script_pubkey.as_ref().unwrap();
+ let script_pubkey_bytes = hex::decode(script_pubkey_hex).unwrap();
+ if script_pubkey_bytes[0] != 0x51 {
+ return Err(P2MRError::P2trRequiresWitnessVersion1.into());
+ }
+ Ok(())
}
-fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
+fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
let tv_script_tree: Option<&TVScriptTree> = test_vector.given.script_tree.as_ref();
@@ -112,11 +134,11 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
// TaprootBuilder expects the addition of each leaf script with its associated depth
// It then constructs the binary tree in DFS order, sorting siblings lexicographically & combining them via BIP341's tapbranch_hash
// Use of TaprootBuilder avoids user error in constructing branches manually and ensures Merkle tree correctness and determinism
- let mut p2tsh_builder: P2tshBuilder = P2tshBuilder::new();
+ let mut p2mr_builder: P2mrBuilder = P2mrBuilder::new();
let mut control_block_data: Vec<(ScriptBuf, LeafVersion)> = Vec::new();
- // 1) traverse test vector script tree and add leaves to P2TSH builder
+ // 1) traverse test vector script tree and add leaves to P2MR builder
if let Some(script_tree) = tv_script_tree {
script_tree.traverse_with_right_subtree_first(0, Direction::Root,&mut |node, depth, direction| {
@@ -138,7 +160,7 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
tv_leaf_count, depth, modified_depth, direction, tv_leaf.script);
// NOTE: Some of the the test vectors in this project specify leaves with non-standard versions (ie: 250 / 0xfa)
- p2tsh_builder = p2tsh_builder.clone().add_leaf_with_ver(depth, tv_leaf_script_buf.clone(), tv_leaf_version)
+ p2mr_builder = p2mr_builder.clone().add_leaf_with_ver(depth, tv_leaf_script_buf.clone(), tv_leaf_version)
.unwrap_or_else(|e| {
panic!("Failed to add leaf: {:?}", e);
});
@@ -152,10 +174,10 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
}
});
}else {
- return Err(P2TSHError::MissingScriptTreeLeaf.into());
+ return Err(P2MRError::MissingScriptTreeLeaf.into());
}
- let spend_info: P2tshSpendInfo = p2tsh_builder.clone()
+ let spend_info: P2mrSpendInfo = p2mr_builder.clone()
.finalize()
.unwrap_or_else(|e| {
panic!("finalize failed: {:?}", e);
@@ -176,7 +198,7 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
let test_vector_leaf_hash_set: HashSet = test_vector_leaf_hashes_vec.iter().cloned().collect();
let test_vector_control_blocks_vec = &test_vector.expected.script_path_control_blocks;
let test_vector_control_blocks_set: HashSet = test_vector_control_blocks_vec.as_ref().unwrap().iter().cloned().collect();
- let tap_tree: TapTree = p2tsh_builder.clone().into_inner().try_into_taptree().unwrap();
+ let tap_tree: TapTree = p2mr_builder.clone().into_inner().try_into_taptree().unwrap();
let script_leaves: ScriptLeaves = tap_tree.script_leaves();
// TO-DO: Investigate why the ordering of script leaves seems to be reverse of test vectors.
@@ -202,7 +224,7 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
// There is no consensus limit on n, but large Merkle trees increase the witness size, impacting block weight.
// NOTE: Control blocks could have also been obtained from spend_info.control_block(..) using the data in control_block_data
debug!("merkle_branch nodes: {:?}", merkle_branch);
- let derived_control_block: P2tshControlBlock = P2tshControlBlock{
+ let derived_control_block: P2mrControlBlock = P2mrControlBlock{
merkle_branch: merkle_branch.clone(),
};
let serialized_control_block = derived_control_block.serialize();
@@ -220,19 +242,19 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
}
- let p2tsh_utxo_return: UtxoReturn = create_p2tsh_utxo(derived_merkle_root.to_string());
+ let p2mr_utxo_return: UtxoReturn = create_p2mr_utxo(derived_merkle_root.to_string());
assert_eq!(
- p2tsh_utxo_return.script_pubkey_hex,
+ p2mr_utxo_return.script_pubkey_hex,
*test_vector.expected.script_pubkey.as_ref().unwrap(),
"Script pubkey mismatch"
);
- debug!("just passed script_pubkey validation. script_pubkey = {}", p2tsh_utxo_return.script_pubkey_hex);
+ debug!("just passed script_pubkey validation. script_pubkey = {}", p2mr_utxo_return.script_pubkey_hex);
- let bech32m_address: String = p2tsh_utxo_return.bech32m_address;
- debug!("derived bech32m address for bitcoin_network: {} : {}", p2tsh_utxo_return.bitcoin_network, bech32m_address);
+ let bech32m_address: String = p2mr_utxo_return.bech32m_address;
+ debug!("derived bech32m address for bitcoin_network: {} : {}", p2mr_utxo_return.bitcoin_network, bech32m_address);
- if p2tsh_utxo_return.bitcoin_network == Network::Bitcoin {
+ if p2mr_utxo_return.bitcoin_network == Network::Bitcoin {
assert_eq!(bech32m_address, *test_vector.expected.bip350_address.as_ref().unwrap(), "Bech32m address mismatch.");
}
diff --git a/bip-0360/ref-impl/rust/tests/p2tsh_construction.rs b/bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs
similarity index 71%
rename from bip-0360/ref-impl/rust/tests/p2tsh_construction.rs
rename to bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs
index 66431d9a36..a5ca855cad 100644
--- a/bip-0360/ref-impl/rust/tests/p2tsh_construction.rs
+++ b/bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs
@@ -1,108 +1,108 @@
use std::collections::HashSet;
use bitcoin::{Network, ScriptBuf};
use bitcoin::taproot::{LeafVersion, TapTree, ScriptLeaves, TapLeafHash, TaprootMerkleBranch, TapNodeHash};
-use bitcoin::p2tsh::{P2tshBuilder, P2tshControlBlock, P2tshSpendInfo};
+use bitcoin::p2mr::{P2mrBuilder, P2mrControlBlock, P2mrSpendInfo};
use bitcoin::hashes::Hash;
use hex;
use log::debug;
use once_cell::sync::Lazy;
-use p2tsh_ref::data_structures::{TVScriptTree, TestVector, Direction, TestVectors, UtxoReturn};
-use p2tsh_ref::error::P2TSHError;
-use p2tsh_ref::{create_p2tsh_utxo, tagged_hash};
+use p2mr_ref::data_structures::{TVScriptTree, TestVector, Direction, TestVectors, UtxoReturn};
+use p2mr_ref::error::P2MRError;
+use p2mr_ref::{create_p2mr_utxo, tagged_hash};
// This file contains tests that execute against the BIP360 script-path-only test vectors.
static TEST_VECTORS: Lazy = Lazy::new(|| {
- let bip360_test_vectors = include_str!("../../common/tests/data/p2tsh_construction.json");
+ let bip360_test_vectors = include_str!("../../common/tests/data/p2mr_pqc_construction.json");
let test_vectors: TestVectors = serde_json::from_str(bip360_test_vectors).unwrap();
assert_eq!(test_vectors.version, 1);
test_vectors
});
-static P2TSH_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST: &str = "p2tsh_missing_leaf_script_tree_error";
-static P2TSH_SINGLE_LEAF_SCRIPT_TREE_TEST: &str = "p2tsh_single_leaf_script_tree";
-static P2TSH_DIFFERENT_VERSION_LEAVES_TEST: &str = "p2tsh_different_version_leaves";
-static P2TSH_TWO_LEAF_SAME_VERSION_TEST: &str = "p2tsh_two_leaf_same_version";
-static P2TSH_THREE_LEAF_COMPLEX_TEST: &str = "p2tsh_three_leaf_complex";
-static P2TSH_THREE_LEAF_ALTERNATIVE_TEST: &str = "p2tsh_three_leaf_alternative";
-static P2TSH_SIMPLE_LIGHTNING_CONTRACT_TEST: &str = "p2tsh_simple_lightning_contract";
+static P2MR_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST: &str = "p2mr_missing_leaf_script_tree_error";
+static P2MR_SINGLE_LEAF_SCRIPT_TREE_TEST: &str = "p2mr_single_leaf_script_tree";
+static P2MR_DIFFERENT_VERSION_LEAVES_TEST: &str = "p2mr_different_version_leaves";
+static P2MR_TWO_LEAF_SAME_VERSION_TEST: &str = "p2mr_two_leaf_same_version";
+static P2MR_THREE_LEAF_COMPLEX_TEST: &str = "p2mr_three_leaf_complex";
+static P2MR_THREE_LEAF_ALTERNATIVE_TEST: &str = "p2mr_three_leaf_alternative";
+static P2MR_SIMPLE_LIGHTNING_CONTRACT_TEST: &str = "p2mr_simple_lightning_contract";
// https://learnmeabitcoin.com/technical/upgrades/taproot/#example-2-script-path-spend-simple
#[test]
-fn test_p2tsh_missing_leaf_script_tree_error() {
+fn test_p2mr_pqc_missing_leaf_script_tree_error() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST).unwrap();
- let test_result: anyhow::Result<()> = process_test_vector_p2tsh(test_vector);
- assert!(matches!(test_result.unwrap_err().downcast_ref::(),
- Some(P2TSHError::MissingScriptTreeLeaf)));
+ let test_vector = test_vectors.test_vector_map.get(P2MR_MISSING_LEAF_SCRIPT_TREE_ERROR_TEST).unwrap();
+ let test_result: anyhow::Result<()> = process_test_vector_p2mr(test_vector);
+ assert!(matches!(test_result.unwrap_err().downcast_ref::(),
+ Some(P2MRError::MissingScriptTreeLeaf)));
}
// https://learnmeabitcoin.com/technical/upgrades/taproot/#example-2-script-path-spend-simple
#[test]
-fn test_p2tsh_single_leaf_script_tree() {
+fn test_p2mr_pqc_single_leaf_script_tree() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_SINGLE_LEAF_SCRIPT_TREE_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_SINGLE_LEAF_SCRIPT_TREE_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_different_version_leaves() {
+fn test_p2mr_pqc_different_version_leaves() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_DIFFERENT_VERSION_LEAVES_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_DIFFERENT_VERSION_LEAVES_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_simple_lightning_contract() {
+fn test_p2mr_pqc_simple_lightning_contract() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_SIMPLE_LIGHTNING_CONTRACT_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_SIMPLE_LIGHTNING_CONTRACT_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_two_leaf_same_version() {
+fn test_p2mr_pqc_two_leaf_same_version() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_TWO_LEAF_SAME_VERSION_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_TWO_LEAF_SAME_VERSION_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_three_leaf_complex() {
+fn test_p2mr_pqc_three_leaf_complex() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_THREE_LEAF_COMPLEX_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_THREE_LEAF_COMPLEX_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
#[test]
-fn test_p2tsh_three_leaf_alternative() {
+fn test_p2mr_pqc_three_leaf_alternative() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
let test_vectors = &*TEST_VECTORS;
- let test_vector = test_vectors.test_vector_map.get(P2TSH_THREE_LEAF_ALTERNATIVE_TEST).unwrap();
- process_test_vector_p2tsh(test_vector).unwrap();
+ let test_vector = test_vectors.test_vector_map.get(P2MR_THREE_LEAF_ALTERNATIVE_TEST).unwrap();
+ process_test_vector_p2mr(test_vector).unwrap();
}
-fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
+fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
let tv_script_tree: Option<&TVScriptTree> = test_vector.given.script_tree.as_ref();
@@ -112,11 +112,11 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
// TaprootBuilder expects the addition of each leaf script with its associated depth
// It then constructs the binary tree in DFS order, sorting siblings lexicographically & combining them via BIP341's tapbranch_hash
// Use of TaprootBuilder avoids user error in constructing branches manually and ensures Merkle tree correctness and determinism
- let mut p2tsh_builder: P2tshBuilder = P2tshBuilder::new();
+ let mut p2mr_builder: P2mrBuilder = P2mrBuilder::new();
let mut control_block_data: Vec<(ScriptBuf, LeafVersion)> = Vec::new();
- // 1) traverse test vector script tree and add leaves to P2TSH builder
+ // 1) traverse test vector script tree and add leaves to P2MR builder
if let Some(script_tree) = tv_script_tree {
script_tree.traverse_with_right_subtree_first(0, Direction::Root,&mut |node, depth, direction| {
@@ -138,7 +138,7 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
tv_leaf_count, depth, modified_depth, direction, tv_leaf.script);
// NOTE: Some of the the test vectors in this project specify leaves with non-standard versions (ie: 250 / 0xfa)
- p2tsh_builder = p2tsh_builder.clone().add_leaf_with_ver(depth, tv_leaf_script_buf.clone(), tv_leaf_version)
+ p2mr_builder = p2mr_builder.clone().add_leaf_with_ver(depth, tv_leaf_script_buf.clone(), tv_leaf_version)
.unwrap_or_else(|e| {
panic!("Failed to add leaf: {:?}", e);
});
@@ -152,10 +152,10 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
}
});
}else {
- return Err(P2TSHError::MissingScriptTreeLeaf.into());
+ return Err(P2MRError::MissingScriptTreeLeaf.into());
}
- let spend_info: P2tshSpendInfo = p2tsh_builder.clone()
+ let spend_info: P2mrSpendInfo = p2mr_builder.clone()
.finalize()
.unwrap_or_else(|e| {
panic!("finalize failed: {:?}", e);
@@ -176,7 +176,7 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
let test_vector_leaf_hash_set: HashSet = test_vector_leaf_hashes_vec.iter().cloned().collect();
let test_vector_control_blocks_vec = &test_vector.expected.script_path_control_blocks;
let test_vector_control_blocks_set: HashSet = test_vector_control_blocks_vec.as_ref().unwrap().iter().cloned().collect();
- let tap_tree: TapTree = p2tsh_builder.clone().into_inner().try_into_taptree().unwrap();
+ let tap_tree: TapTree = p2mr_builder.clone().into_inner().try_into_taptree().unwrap();
let script_leaves: ScriptLeaves = tap_tree.script_leaves();
// TO-DO: Investigate why the ordering of script leaves seems to be reverse of test vectors.
@@ -202,7 +202,7 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
// There is no consensus limit on n, but large Merkle trees increase the witness size, impacting block weight.
// NOTE: Control blocks could have also been obtained from spend_info.control_block(..) using the data in control_block_data
debug!("merkle_branch nodes: {:?}", merkle_branch);
- let derived_control_block: P2tshControlBlock = P2tshControlBlock{
+ let derived_control_block: P2mrControlBlock = P2mrControlBlock{
merkle_branch: merkle_branch.clone(),
};
let serialized_control_block = derived_control_block.serialize();
@@ -220,19 +220,19 @@ fn process_test_vector_p2tsh(test_vector: &TestVector) -> anyhow::Result<()> {
}
- let p2tsh_utxo_return: UtxoReturn = create_p2tsh_utxo(derived_merkle_root.to_string());
+ let p2mr_utxo_return: UtxoReturn = create_p2mr_utxo(derived_merkle_root.to_string());
assert_eq!(
- p2tsh_utxo_return.script_pubkey_hex,
+ p2mr_utxo_return.script_pubkey_hex,
*test_vector.expected.script_pubkey.as_ref().unwrap(),
"Script pubkey mismatch"
);
- debug!("just passed script_pubkey validation. script_pubkey = {}", p2tsh_utxo_return.script_pubkey_hex);
+ debug!("just passed script_pubkey validation. script_pubkey = {}", p2mr_utxo_return.script_pubkey_hex);
- let bech32m_address: String = p2tsh_utxo_return.bech32m_address;
- debug!("derived bech32m address for bitcoin_network: {} : {}", p2tsh_utxo_return.bitcoin_network, bech32m_address);
+ let bech32m_address: String = p2mr_utxo_return.bech32m_address;
+ debug!("derived bech32m address for bitcoin_network: {} : {}", p2mr_utxo_return.bitcoin_network, bech32m_address);
- if p2tsh_utxo_return.bitcoin_network == Network::Bitcoin {
+ if p2mr_utxo_return.bitcoin_network == Network::Bitcoin {
assert_eq!(bech32m_address, *test_vector.expected.bip350_address.as_ref().unwrap(), "Bech32m address mismatch.");
}
diff --git a/bip-0360/ref-impl/rust/tests/p2tsh_spend.rs b/bip-0360/ref-impl/rust/tests/p2mr_spend.rs
similarity index 88%
rename from bip-0360/ref-impl/rust/tests/p2tsh_spend.rs
rename to bip-0360/ref-impl/rust/tests/p2mr_spend.rs
index fd6b2bd42d..c06063bb50 100644
--- a/bip-0360/ref-impl/rust/tests/p2tsh_spend.rs
+++ b/bip-0360/ref-impl/rust/tests/p2mr_spend.rs
@@ -1,9 +1,9 @@
use log::info;
use bitcoin::blockdata::witness::Witness;
-use p2tsh_ref::{ pay_to_p2wpkh_tx, serialize_script };
+use p2mr_ref::{ pay_to_p2wpkh_tx, serialize_script };
-use p2tsh_ref::data_structures::{SpendDetails, LeafScriptType};
+use p2mr_ref::data_structures::{SpendDetails, LeafScriptType};
/* The rust-bitcoin crate does not provide a single high-level API that builds the full Taproot script-path witness stack for you.
It does expose all the necessary types and primitives to build it manually and correctly.
@@ -39,7 +39,7 @@ fn test_script_path_spend_simple() {
// Inspired by: https://learnmeabitcoin.com/technical/upgrades/taproot/#example-3-script-path-spend-signature
-// Spends from a p2tsh UTXO to a p2wpk UTXO
+// Spends from a p2mr UTXO to a p2wpk UTXO
#[test]
fn test_script_path_spend_signatures() {
let _ = env_logger::try_init(); // Use try_init to avoid reinitialization error
@@ -57,7 +57,7 @@ fn test_script_path_spend_signatures() {
hex::decode("206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac").unwrap();
// Modified from learnmeabitcoin example
- // Changed from c0 to c1 control byte to reflect p2tsh specification: The parity bit of the control byte is always 1 since P2TSH does not have a key-spend path.
+ // Changed from c0 to c1 control byte to reflect p2mr specification: The parity bit of the control byte is always 1 since P2MR does not have a key-spend path.
let input_control_block_bytes: Vec =
hex::decode("c1924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329").unwrap();
@@ -76,10 +76,10 @@ fn test_script_path_spend_signatures() {
let spend_output_amount_sats: u64 = 15000;
let test_sighash_bytes: Vec = hex::decode("752453d473e511a0da2097d664d69fe5eb89d8d9d00eab924b42fc0801a980c9").unwrap();
- let test_signature_bytes: Vec = hex::decode("01769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f01").unwrap();
+ let test_signature_bytes: Vec = hex::decode("01769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f").unwrap();
// Modified from learnmeabitcoin example
- // Changed from c0 to c1 control byte to reflect p2tsh specification: The parity bit of the control byte is always 1 since P2TSH does not have a key-spend path.
+ // Changed from c0 to c1 control byte to reflect p2mr specification: The parity bit of the control byte is always 1 since P2MR does not have a key-spend path.
let test_witness_bytes: Vec = hex::decode("01769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f01206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0acc1924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329").unwrap();
let result: SpendDetails = pay_to_p2wpkh_tx(funding_tx_id_bytes,