diff --git a/bip-0360/ref-impl/common/tests/data/p2mr_construction.json b/bip-0360/ref-impl/common/tests/data/p2mr_construction.json index f6d76d07ca..166b7a8d8c 100644 --- a/bip-0360/ref-impl/common/tests/data/p2mr_construction.json +++ b/bip-0360/ref-impl/common/tests/data/p2mr_construction.json @@ -123,8 +123,8 @@ "scriptPubKey": "522041646f8c1fe2a96ddad7f5471bc4fee7da98794ef8c45a4f4fc6a559d60c9f6b", "bip350Address": "bc1zg9jxlrqlu25kmkkh74r3h387uldfs72wlrz95n60c6j4n4svna4s4lhfhe", "scriptPathControlBlocks": [ - "c1c81451874bd9ebd4b6fd4bba1f84cdfb533c532365d22a0a702205ff658b17c9", - "c1632c8632b4f29c6291416e23135cf78ecb82e525788ea5ed6483e3c6ce943b42" + "c1632c8632b4f29c6291416e23135cf78ecb82e525788ea5ed6483e3c6ce943b42", + "c1c81451874bd9ebd4b6fd4bba1f84cdfb533c532365d22a0a702205ff658b17c9" ] } }, @@ -205,8 +205,8 @@ "bip350Address": "bc1zej7kd3hhar76k3an5jr0t8fgyc47s4lnp4rh8uk4afrlwasuur3qzgewqq", "scriptPathControlBlocks": [ "c1ffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553", - "c1ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", - "c19e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817" + "c19e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", + "c1ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817" ] } }, @@ -251,8 +251,8 @@ "bip350Address": "bc1z9a4jc5uhkmtgegvwpx3lq5tpv68layaf3pvz64wx7paatvejnhhsv52lcv", "scriptPathControlBlocks": [ "c13cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91", - "c1737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", - "c1d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d" + "c1d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", + "c1737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d" ] } } diff --git a/bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json b/bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json index 86a7f2fca7..75caf80812 100644 --- a/bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json +++ b/bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json @@ -71,8 +71,8 @@ "scriptPubKey": "52201619ce6d22a46dea045c4adf7f5f33d6810d00d0e9c8a4c7ba35db37b915c604", "bip350Address": "bc1zzcvuumfz53k75pzuft0h7hen66qs6qxsa8y2f3a6xhdn0wg4cczq0h84sj", "scriptPathControlBlocks": [ - "c13bb0db8c6adcd87330a4a8c91be0fe1b23da3c151b6f2fb4f269429c43b8d8bc", - "c1f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a" + "c1f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a", + "c13bb0db8c6adcd87330a4a8c91be0fe1b23da3c151b6f2fb4f269429c43b8d8bc" ] } }, @@ -110,8 +110,8 @@ "scriptPubKey": "52202794771cd51f215ba3a19fbcdf08c771edb7de782a0c34457e0e9be5d0e4008f", "bip350Address": "bc1zy728w8x4rus4hgapn77d7zx8w8km0hnc9gxrg3t7p6d7t58yqz8sg0nccq", "scriptPathControlBlocks": [ - "c1cfd5fc07ac39947cba799e14f933f20e7c233dea72dc2792f5547c58cdce743e", - "c1a9745ac96d4f3702b78751f1e08f3040fbe6347e7b4f520d22d3f907730cbb7e" + "c1a9745ac96d4f3702b78751f1e08f3040fbe6347e7b4f520d22d3f907730cbb7e", + "c1cfd5fc07ac39947cba799e14f933f20e7c233dea72dc2792f5547c58cdce743e" ] } }, @@ -147,8 +147,8 @@ "scriptPubKey": "52205112b3edfd2c0b717491e9d4888ed2d5dfeaa25115143540e0a08516b68c008c", "bip350Address": "bc1z2yft8m0a9s9hzay3a82g3rkj6h074gj3z52r2s8q5zz3dd5vqzxqngpk2w", "scriptPathControlBlocks": [ - "c19de7eeded7832c28c6f80de76904dd79f98fd302747823b5bc5be440186b0c6d", - "c12cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb" + "c12cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb", + "c19de7eeded7832c28c6f80de76904dd79f98fd302747823b5bc5be440186b0c6d" ] } }, @@ -194,9 +194,9 @@ "scriptPubKey": "5220eaf8f557fdb9673de7bb9bad7e7452da9f44a3e65133fdadf2849c55cfb3cf5b", "bip350Address": "bc1zatu024lah9nnmeamnwkhuazjm205fglx2yelmt0jsjw9tnaneadszq7wg7", "scriptPathControlBlocks": [ - "c1837ef6677aeb0df2b0de47f45024684cc6ca03bda10fa30bb5bc05a94beb8dd1b2a5304f678cc5a2ed51feb377dd0a609bd22ec979cc608bfcf884d0f8e6f93a", + "c118781f42f664d67acaf0ce7c6826437e5440eb1789f232af05e9a09fdf547903", "c10840c39e59eda6c9deee687a480cb169130c2f053ed2eb3134511ec1cfd8a2c7b2a5304f678cc5a2ed51feb377dd0a609bd22ec979cc608bfcf884d0f8e6f93a", - "c118781f42f664d67acaf0ce7c6826437e5440eb1789f232af05e9a09fdf547903" + "c1837ef6677aeb0df2b0de47f45024684cc6ca03bda10fa30bb5bc05a94beb8dd1b2a5304f678cc5a2ed51feb377dd0a609bd22ec979cc608bfcf884d0f8e6f93a" ] } }, @@ -242,9 +242,9 @@ "scriptPubKey": "522051e3c1151ba73d9efce801837773331bf9030977242f62dfeb6756795f482409", "bip350Address": "bc1z283uz9gm5u7eal8gqxphwuenr0usxzthyshk9hltvat8jh6gysys28twnc", "scriptPathControlBlocks": [ - "c1dcef3ce86cc8cea78c9e00f3d9ef58360cb6ed3cb90ec62efe00b9703854ba5cddb521a44e33ff4974e618d8b8b7794275b7dc754d847c537404f84330454361", + "c1b45680a7821e4b9450096ab38adbc3c99225af8f6c7ec121a0a5f1ae02893ba3", "c152e9326c2bf04d926b7e9f6c7645dd853f3f007b870201de9b814952750c9310ddb521a44e33ff4974e618d8b8b7794275b7dc754d847c537404f84330454361", - "c1b45680a7821e4b9450096ab38adbc3c99225af8f6c7ec121a0a5f1ae02893ba3" + "c1dcef3ce86cc8cea78c9e00f3d9ef58360cb6ed3cb90ec62efe00b9703854ba5cddb521a44e33ff4974e618d8b8b7794275b7dc754d847c537404f84330454361" ] } } diff --git a/bip-0360/ref-impl/rust/tests/p2mr_construction.rs b/bip-0360/ref-impl/rust/tests/p2mr_construction.rs index 38d790d104..bf1f9bbf09 100644 --- a/bip-0360/ref-impl/rust/tests/p2mr_construction.rs +++ b/bip-0360/ref-impl/rust/tests/p2mr_construction.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::HashMap; use bitcoin::{Network, ScriptBuf}; use bitcoin::taproot::{LeafVersion, TapTree, ScriptLeaves, TapLeafHash, TaprootMerkleBranch, TapNodeHash}; use bitcoin::p2mr::{P2mrBuilder, P2mrControlBlock, P2mrSpendInfo}; @@ -136,7 +136,7 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { // Use of TaprootBuilder avoids user error in constructing branches manually and ensures Merkle tree correctness and determinism let mut p2mr_builder: P2mrBuilder = P2mrBuilder::new(); - let mut control_block_data: Vec<(ScriptBuf, LeafVersion)> = Vec::new(); + let mut script_to_id: HashMap = HashMap::new(); // 1) traverse test vector script tree and add leaves to P2MR builder if let Some(script_tree) = tv_script_tree { @@ -144,13 +144,12 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { script_tree.traverse_with_right_subtree_first(0, Direction::Root,&mut |node, depth, direction| { if let TVScriptTree::Leaf(tv_leaf) = node { - + let tv_leaf_script_bytes = hex::decode(&tv_leaf.script).unwrap(); - - // NOTE: IOT to execute script_info.control_block(..), will add these to a vector + let tv_leaf_script_buf = ScriptBuf::from_bytes(tv_leaf_script_bytes.clone()); let tv_leaf_version = LeafVersion::from_consensus(tv_leaf.leaf_version).unwrap(); - control_block_data.push((tv_leaf_script_buf.clone(), tv_leaf_version)); + script_to_id.insert(tv_leaf_script_buf.clone(), tv_leaf.id); let mut modified_depth = depth + 1; if direction == Direction::Root { @@ -194,14 +193,10 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { ); debug!("just passed merkle root validation: {}", test_vector_merkle_root); - let test_vector_leaf_hashes_vec: Vec = test_vector.intermediary.leaf_hashes.clone(); - 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 expected_control_blocks = test_vector.expected.script_path_control_blocks.as_ref().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. // 3) Iterate through leaves of derived script tree and verify both script leaf hashes and control blocks for derived_leaf in script_leaves { @@ -211,34 +206,21 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { let derived_leaf_hash: TapLeafHash = TapLeafHash::from_script(script, version); let leaf_hash = hex::encode(derived_leaf_hash.as_raw_hash().to_byte_array()); - assert!( - test_vector_leaf_hash_set.contains(&leaf_hash), - "Leaf hash not found in expected set for {}", leaf_hash - ); - debug!("just passed leaf_hash validation: {}", leaf_hash); - - // Each leaf in the script tree has a corresponding control block. - // Specific to P2TR, the 3 sections of the control block (control byte, public key & merkle path) are highlighted here: - // https://learnmeabitcoin.com/technical/upgrades/taproot/#script-path-spend-control-block - // The control block, which includes the Merkle path, must be 33 + 32 * n bytes, where n is the number of Merkle path hashes (n ≥ 0). - // 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 leaf_id = script_to_id.get(script) + .unwrap_or_else(|| panic!("leaf script not found in script_to_id map: {}", hex::encode(script.as_bytes()))); + let derived_control_block: P2mrControlBlock = P2mrControlBlock{ merkle_branch: merkle_branch.clone(), }; - let serialized_control_block = derived_control_block.serialize(); - debug!("derived_control_block: {:?}, merkle_branch size: {}, control_block size: {}, serialized size: {}", - derived_control_block, - merkle_branch.len(), - derived_control_block.size(), - serialized_control_block.len()); - let derived_serialized_control_block = hex::encode(serialized_control_block); - assert!( - test_vector_control_blocks_set.contains(&derived_serialized_control_block), - "Control block mismatch: {}, expected: {:?}", derived_serialized_control_block, test_vector_control_blocks_set + let derived_serialized_control_block = hex::encode(derived_control_block.serialize()); + + let expected_cb = &expected_control_blocks[*leaf_id as usize]; + assert_eq!( + derived_serialized_control_block, *expected_cb, + "Control block mismatch for leaf id {}: derived {}, expected {}", leaf_id, derived_serialized_control_block, expected_cb ); - debug!("leaf_hash: {}, derived_serialized_control_block: {}", leaf_hash, derived_serialized_control_block); + debug!("leaf_id: {}, leaf_hash: {}, derived_serialized_control_block: {}", leaf_id, leaf_hash, derived_serialized_control_block); } diff --git a/bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs b/bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs index a5ca855cad..6a7ec8f926 100644 --- a/bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs +++ b/bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::HashMap; use bitcoin::{Network, ScriptBuf}; use bitcoin::taproot::{LeafVersion, TapTree, ScriptLeaves, TapLeafHash, TaprootMerkleBranch, TapNodeHash}; use bitcoin::p2mr::{P2mrBuilder, P2mrControlBlock, P2mrSpendInfo}; @@ -114,7 +114,7 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { // Use of TaprootBuilder avoids user error in constructing branches manually and ensures Merkle tree correctness and determinism let mut p2mr_builder: P2mrBuilder = P2mrBuilder::new(); - let mut control_block_data: Vec<(ScriptBuf, LeafVersion)> = Vec::new(); + let mut script_to_id: HashMap = HashMap::new(); // 1) traverse test vector script tree and add leaves to P2MR builder if let Some(script_tree) = tv_script_tree { @@ -122,13 +122,12 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { script_tree.traverse_with_right_subtree_first(0, Direction::Root,&mut |node, depth, direction| { if let TVScriptTree::Leaf(tv_leaf) = node { - + let tv_leaf_script_bytes = hex::decode(&tv_leaf.script).unwrap(); - - // NOTE: IOT to execute script_info.control_block(..), will add these to a vector + let tv_leaf_script_buf = ScriptBuf::from_bytes(tv_leaf_script_bytes.clone()); let tv_leaf_version = LeafVersion::from_consensus(tv_leaf.leaf_version).unwrap(); - control_block_data.push((tv_leaf_script_buf.clone(), tv_leaf_version)); + script_to_id.insert(tv_leaf_script_buf.clone(), tv_leaf.id); let mut modified_depth = depth + 1; if direction == Direction::Root { @@ -172,14 +171,10 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { ); debug!("just passed merkle root validation: {}", test_vector_merkle_root); - let test_vector_leaf_hashes_vec: Vec = test_vector.intermediary.leaf_hashes.clone(); - 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 expected_control_blocks = test_vector.expected.script_path_control_blocks.as_ref().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. // 3) Iterate through leaves of derived script tree and verify both script leaf hashes and control blocks for derived_leaf in script_leaves { @@ -189,34 +184,21 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> { let derived_leaf_hash: TapLeafHash = TapLeafHash::from_script(script, version); let leaf_hash = hex::encode(derived_leaf_hash.as_raw_hash().to_byte_array()); - assert!( - test_vector_leaf_hash_set.contains(&leaf_hash), - "Leaf hash not found in expected set for {}", leaf_hash - ); - debug!("just passed leaf_hash validation: {}", leaf_hash); - - // Each leaf in the script tree has a corresponding control block. - // Specific to P2TR, the 3 sections of the control block (control byte, public key & merkle path) are highlighted here: - // https://learnmeabitcoin.com/technical/upgrades/taproot/#script-path-spend-control-block - // The control block, which includes the Merkle path, must be 33 + 32 * n bytes, where n is the number of Merkle path hashes (n ≥ 0). - // 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 leaf_id = script_to_id.get(script) + .unwrap_or_else(|| panic!("leaf script not found in script_to_id map: {}", hex::encode(script.as_bytes()))); + let derived_control_block: P2mrControlBlock = P2mrControlBlock{ merkle_branch: merkle_branch.clone(), }; - let serialized_control_block = derived_control_block.serialize(); - debug!("derived_control_block: {:?}, merkle_branch size: {}, control_block size: {}, serialized size: {}", - derived_control_block, - merkle_branch.len(), - derived_control_block.size(), - serialized_control_block.len()); - let derived_serialized_control_block = hex::encode(serialized_control_block); - assert!( - test_vector_control_blocks_set.contains(&derived_serialized_control_block), - "Control block mismatch: {}, expected: {:?}", derived_serialized_control_block, test_vector_control_blocks_set + let derived_serialized_control_block = hex::encode(derived_control_block.serialize()); + + let expected_cb = &expected_control_blocks[*leaf_id as usize]; + assert_eq!( + derived_serialized_control_block, *expected_cb, + "Control block mismatch for leaf id {}: derived {}, expected {}", leaf_id, derived_serialized_control_block, expected_cb ); - debug!("leaf_hash: {}, derived_serialized_control_block: {}", leaf_hash, derived_serialized_control_block); + debug!("leaf_id: {}, leaf_hash: {}, derived_serialized_control_block: {}", leaf_id, leaf_hash, derived_serialized_control_block); }