Skip to content

Commit 7e98bd0

Browse files
authored
bip360 test vectors: elements in control block array should be ordered to match tree structure of test vectors (#45)
1 parent 351ceef commit 7e98bd0

4 files changed

Lines changed: 50 additions & 86 deletions

File tree

bip-0360/ref-impl/common/tests/data/p2mr_construction.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@
123123
"scriptPubKey": "522041646f8c1fe2a96ddad7f5471bc4fee7da98794ef8c45a4f4fc6a559d60c9f6b",
124124
"bip350Address": "bc1zg9jxlrqlu25kmkkh74r3h387uldfs72wlrz95n60c6j4n4svna4s4lhfhe",
125125
"scriptPathControlBlocks": [
126-
"c1c81451874bd9ebd4b6fd4bba1f84cdfb533c532365d22a0a702205ff658b17c9",
127-
"c1632c8632b4f29c6291416e23135cf78ecb82e525788ea5ed6483e3c6ce943b42"
126+
"c1632c8632b4f29c6291416e23135cf78ecb82e525788ea5ed6483e3c6ce943b42",
127+
"c1c81451874bd9ebd4b6fd4bba1f84cdfb533c532365d22a0a702205ff658b17c9"
128128
]
129129
}
130130
},
@@ -205,8 +205,8 @@
205205
"bip350Address": "bc1zej7kd3hhar76k3an5jr0t8fgyc47s4lnp4rh8uk4afrlwasuur3qzgewqq",
206206
"scriptPathControlBlocks": [
207207
"c1ffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553",
208-
"c1ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",
209-
"c19e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817"
208+
"c19e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",
209+
"c1ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817"
210210
]
211211
}
212212
},
@@ -251,8 +251,8 @@
251251
"bip350Address": "bc1z9a4jc5uhkmtgegvwpx3lq5tpv68layaf3pvz64wx7paatvejnhhsv52lcv",
252252
"scriptPathControlBlocks": [
253253
"c13cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91",
254-
"c1737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",
255-
"c1d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d"
254+
"c1d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",
255+
"c1737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d"
256256
]
257257
}
258258
}

bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@
7171
"scriptPubKey": "52201619ce6d22a46dea045c4adf7f5f33d6810d00d0e9c8a4c7ba35db37b915c604",
7272
"bip350Address": "bc1zzcvuumfz53k75pzuft0h7hen66qs6qxsa8y2f3a6xhdn0wg4cczq0h84sj",
7373
"scriptPathControlBlocks": [
74-
"c13bb0db8c6adcd87330a4a8c91be0fe1b23da3c151b6f2fb4f269429c43b8d8bc",
75-
"c1f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a"
74+
"c1f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a",
75+
"c13bb0db8c6adcd87330a4a8c91be0fe1b23da3c151b6f2fb4f269429c43b8d8bc"
7676
]
7777
}
7878
},
@@ -110,8 +110,8 @@
110110
"scriptPubKey": "52202794771cd51f215ba3a19fbcdf08c771edb7de782a0c34457e0e9be5d0e4008f",
111111
"bip350Address": "bc1zy728w8x4rus4hgapn77d7zx8w8km0hnc9gxrg3t7p6d7t58yqz8sg0nccq",
112112
"scriptPathControlBlocks": [
113-
"c1cfd5fc07ac39947cba799e14f933f20e7c233dea72dc2792f5547c58cdce743e",
114-
"c1a9745ac96d4f3702b78751f1e08f3040fbe6347e7b4f520d22d3f907730cbb7e"
113+
"c1a9745ac96d4f3702b78751f1e08f3040fbe6347e7b4f520d22d3f907730cbb7e",
114+
"c1cfd5fc07ac39947cba799e14f933f20e7c233dea72dc2792f5547c58cdce743e"
115115
]
116116
}
117117
},
@@ -147,8 +147,8 @@
147147
"scriptPubKey": "52205112b3edfd2c0b717491e9d4888ed2d5dfeaa25115143540e0a08516b68c008c",
148148
"bip350Address": "bc1z2yft8m0a9s9hzay3a82g3rkj6h074gj3z52r2s8q5zz3dd5vqzxqngpk2w",
149149
"scriptPathControlBlocks": [
150-
"c19de7eeded7832c28c6f80de76904dd79f98fd302747823b5bc5be440186b0c6d",
151-
"c12cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb"
150+
"c12cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb",
151+
"c19de7eeded7832c28c6f80de76904dd79f98fd302747823b5bc5be440186b0c6d"
152152
]
153153
}
154154
},
@@ -194,9 +194,9 @@
194194
"scriptPubKey": "5220eaf8f557fdb9673de7bb9bad7e7452da9f44a3e65133fdadf2849c55cfb3cf5b",
195195
"bip350Address": "bc1zatu024lah9nnmeamnwkhuazjm205fglx2yelmt0jsjw9tnaneadszq7wg7",
196196
"scriptPathControlBlocks": [
197-
"c1837ef6677aeb0df2b0de47f45024684cc6ca03bda10fa30bb5bc05a94beb8dd1b2a5304f678cc5a2ed51feb377dd0a609bd22ec979cc608bfcf884d0f8e6f93a",
197+
"c118781f42f664d67acaf0ce7c6826437e5440eb1789f232af05e9a09fdf547903",
198198
"c10840c39e59eda6c9deee687a480cb169130c2f053ed2eb3134511ec1cfd8a2c7b2a5304f678cc5a2ed51feb377dd0a609bd22ec979cc608bfcf884d0f8e6f93a",
199-
"c118781f42f664d67acaf0ce7c6826437e5440eb1789f232af05e9a09fdf547903"
199+
"c1837ef6677aeb0df2b0de47f45024684cc6ca03bda10fa30bb5bc05a94beb8dd1b2a5304f678cc5a2ed51feb377dd0a609bd22ec979cc608bfcf884d0f8e6f93a"
200200
]
201201
}
202202
},
@@ -242,9 +242,9 @@
242242
"scriptPubKey": "522051e3c1151ba73d9efce801837773331bf9030977242f62dfeb6756795f482409",
243243
"bip350Address": "bc1z283uz9gm5u7eal8gqxphwuenr0usxzthyshk9hltvat8jh6gysys28twnc",
244244
"scriptPathControlBlocks": [
245-
"c1dcef3ce86cc8cea78c9e00f3d9ef58360cb6ed3cb90ec62efe00b9703854ba5cddb521a44e33ff4974e618d8b8b7794275b7dc754d847c537404f84330454361",
245+
"c1b45680a7821e4b9450096ab38adbc3c99225af8f6c7ec121a0a5f1ae02893ba3",
246246
"c152e9326c2bf04d926b7e9f6c7645dd853f3f007b870201de9b814952750c9310ddb521a44e33ff4974e618d8b8b7794275b7dc754d847c537404f84330454361",
247-
"c1b45680a7821e4b9450096ab38adbc3c99225af8f6c7ec121a0a5f1ae02893ba3"
247+
"c1dcef3ce86cc8cea78c9e00f3d9ef58360cb6ed3cb90ec62efe00b9703854ba5cddb521a44e33ff4974e618d8b8b7794275b7dc754d847c537404f84330454361"
248248
]
249249
}
250250
}

bip-0360/ref-impl/rust/tests/p2mr_construction.rs

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashSet;
1+
use std::collections::HashMap;
22
use bitcoin::{Network, ScriptBuf};
33
use bitcoin::taproot::{LeafVersion, TapTree, ScriptLeaves, TapLeafHash, TaprootMerkleBranch, TapNodeHash};
44
use bitcoin::p2mr::{P2mrBuilder, P2mrControlBlock, P2mrSpendInfo};
@@ -136,21 +136,20 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
136136
// Use of TaprootBuilder avoids user error in constructing branches manually and ensures Merkle tree correctness and determinism
137137
let mut p2mr_builder: P2mrBuilder = P2mrBuilder::new();
138138

139-
let mut control_block_data: Vec<(ScriptBuf, LeafVersion)> = Vec::new();
139+
let mut script_to_id: HashMap<ScriptBuf, u8> = HashMap::new();
140140

141141
// 1) traverse test vector script tree and add leaves to P2MR builder
142142
if let Some(script_tree) = tv_script_tree {
143143

144144
script_tree.traverse_with_right_subtree_first(0, Direction::Root,&mut |node, depth, direction| {
145145

146146
if let TVScriptTree::Leaf(tv_leaf) = node {
147-
147+
148148
let tv_leaf_script_bytes = hex::decode(&tv_leaf.script).unwrap();
149-
150-
// NOTE: IOT to execute script_info.control_block(..), will add these to a vector
149+
151150
let tv_leaf_script_buf = ScriptBuf::from_bytes(tv_leaf_script_bytes.clone());
152151
let tv_leaf_version = LeafVersion::from_consensus(tv_leaf.leaf_version).unwrap();
153-
control_block_data.push((tv_leaf_script_buf.clone(), tv_leaf_version));
152+
script_to_id.insert(tv_leaf_script_buf.clone(), tv_leaf.id);
154153

155154
let mut modified_depth = depth + 1;
156155
if direction == Direction::Root {
@@ -194,14 +193,10 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
194193
);
195194
debug!("just passed merkle root validation: {}", test_vector_merkle_root);
196195

197-
let test_vector_leaf_hashes_vec: Vec<String> = test_vector.intermediary.leaf_hashes.clone();
198-
let test_vector_leaf_hash_set: HashSet<String> = test_vector_leaf_hashes_vec.iter().cloned().collect();
199-
let test_vector_control_blocks_vec = &test_vector.expected.script_path_control_blocks;
200-
let test_vector_control_blocks_set: HashSet<String> = test_vector_control_blocks_vec.as_ref().unwrap().iter().cloned().collect();
196+
let expected_control_blocks = test_vector.expected.script_path_control_blocks.as_ref().unwrap();
201197
let tap_tree: TapTree = p2mr_builder.clone().into_inner().try_into_taptree().unwrap();
202198
let script_leaves: ScriptLeaves = tap_tree.script_leaves();
203199

204-
// TO-DO: Investigate why the ordering of script leaves seems to be reverse of test vectors.
205200
// 3) Iterate through leaves of derived script tree and verify both script leaf hashes and control blocks
206201
for derived_leaf in script_leaves {
207202

@@ -211,34 +206,21 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
211206

212207
let derived_leaf_hash: TapLeafHash = TapLeafHash::from_script(script, version);
213208
let leaf_hash = hex::encode(derived_leaf_hash.as_raw_hash().to_byte_array());
214-
assert!(
215-
test_vector_leaf_hash_set.contains(&leaf_hash),
216-
"Leaf hash not found in expected set for {}", leaf_hash
217-
);
218-
debug!("just passed leaf_hash validation: {}", leaf_hash);
219-
220-
// Each leaf in the script tree has a corresponding control block.
221-
// Specific to P2TR, the 3 sections of the control block (control byte, public key & merkle path) are highlighted here:
222-
// https://learnmeabitcoin.com/technical/upgrades/taproot/#script-path-spend-control-block
223-
// 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).
224-
// There is no consensus limit on n, but large Merkle trees increase the witness size, impacting block weight.
225-
// NOTE: Control blocks could have also been obtained from spend_info.control_block(..) using the data in control_block_data
226-
debug!("merkle_branch nodes: {:?}", merkle_branch);
209+
210+
let leaf_id = script_to_id.get(script)
211+
.unwrap_or_else(|| panic!("leaf script not found in script_to_id map: {}", hex::encode(script.as_bytes())));
212+
227213
let derived_control_block: P2mrControlBlock = P2mrControlBlock{
228214
merkle_branch: merkle_branch.clone(),
229215
};
230-
let serialized_control_block = derived_control_block.serialize();
231-
debug!("derived_control_block: {:?}, merkle_branch size: {}, control_block size: {}, serialized size: {}",
232-
derived_control_block,
233-
merkle_branch.len(),
234-
derived_control_block.size(),
235-
serialized_control_block.len());
236-
let derived_serialized_control_block = hex::encode(serialized_control_block);
237-
assert!(
238-
test_vector_control_blocks_set.contains(&derived_serialized_control_block),
239-
"Control block mismatch: {}, expected: {:?}", derived_serialized_control_block, test_vector_control_blocks_set
216+
let derived_serialized_control_block = hex::encode(derived_control_block.serialize());
217+
218+
let expected_cb = &expected_control_blocks[*leaf_id as usize];
219+
assert_eq!(
220+
derived_serialized_control_block, *expected_cb,
221+
"Control block mismatch for leaf id {}: derived {}, expected {}", leaf_id, derived_serialized_control_block, expected_cb
240222
);
241-
debug!("leaf_hash: {}, derived_serialized_control_block: {}", leaf_hash, derived_serialized_control_block);
223+
debug!("leaf_id: {}, leaf_hash: {}, derived_serialized_control_block: {}", leaf_id, leaf_hash, derived_serialized_control_block);
242224

243225
}
244226

bip-0360/ref-impl/rust/tests/p2mr_pqc_construction.rs

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashSet;
1+
use std::collections::HashMap;
22
use bitcoin::{Network, ScriptBuf};
33
use bitcoin::taproot::{LeafVersion, TapTree, ScriptLeaves, TapLeafHash, TaprootMerkleBranch, TapNodeHash};
44
use bitcoin::p2mr::{P2mrBuilder, P2mrControlBlock, P2mrSpendInfo};
@@ -114,21 +114,20 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
114114
// Use of TaprootBuilder avoids user error in constructing branches manually and ensures Merkle tree correctness and determinism
115115
let mut p2mr_builder: P2mrBuilder = P2mrBuilder::new();
116116

117-
let mut control_block_data: Vec<(ScriptBuf, LeafVersion)> = Vec::new();
117+
let mut script_to_id: HashMap<ScriptBuf, u8> = HashMap::new();
118118

119119
// 1) traverse test vector script tree and add leaves to P2MR builder
120120
if let Some(script_tree) = tv_script_tree {
121121

122122
script_tree.traverse_with_right_subtree_first(0, Direction::Root,&mut |node, depth, direction| {
123123

124124
if let TVScriptTree::Leaf(tv_leaf) = node {
125-
125+
126126
let tv_leaf_script_bytes = hex::decode(&tv_leaf.script).unwrap();
127-
128-
// NOTE: IOT to execute script_info.control_block(..), will add these to a vector
127+
129128
let tv_leaf_script_buf = ScriptBuf::from_bytes(tv_leaf_script_bytes.clone());
130129
let tv_leaf_version = LeafVersion::from_consensus(tv_leaf.leaf_version).unwrap();
131-
control_block_data.push((tv_leaf_script_buf.clone(), tv_leaf_version));
130+
script_to_id.insert(tv_leaf_script_buf.clone(), tv_leaf.id);
132131

133132
let mut modified_depth = depth + 1;
134133
if direction == Direction::Root {
@@ -172,14 +171,10 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
172171
);
173172
debug!("just passed merkle root validation: {}", test_vector_merkle_root);
174173

175-
let test_vector_leaf_hashes_vec: Vec<String> = test_vector.intermediary.leaf_hashes.clone();
176-
let test_vector_leaf_hash_set: HashSet<String> = test_vector_leaf_hashes_vec.iter().cloned().collect();
177-
let test_vector_control_blocks_vec = &test_vector.expected.script_path_control_blocks;
178-
let test_vector_control_blocks_set: HashSet<String> = test_vector_control_blocks_vec.as_ref().unwrap().iter().cloned().collect();
174+
let expected_control_blocks = test_vector.expected.script_path_control_blocks.as_ref().unwrap();
179175
let tap_tree: TapTree = p2mr_builder.clone().into_inner().try_into_taptree().unwrap();
180176
let script_leaves: ScriptLeaves = tap_tree.script_leaves();
181177

182-
// TO-DO: Investigate why the ordering of script leaves seems to be reverse of test vectors.
183178
// 3) Iterate through leaves of derived script tree and verify both script leaf hashes and control blocks
184179
for derived_leaf in script_leaves {
185180

@@ -189,34 +184,21 @@ fn process_test_vector_p2mr(test_vector: &TestVector) -> anyhow::Result<()> {
189184

190185
let derived_leaf_hash: TapLeafHash = TapLeafHash::from_script(script, version);
191186
let leaf_hash = hex::encode(derived_leaf_hash.as_raw_hash().to_byte_array());
192-
assert!(
193-
test_vector_leaf_hash_set.contains(&leaf_hash),
194-
"Leaf hash not found in expected set for {}", leaf_hash
195-
);
196-
debug!("just passed leaf_hash validation: {}", leaf_hash);
197-
198-
// Each leaf in the script tree has a corresponding control block.
199-
// Specific to P2TR, the 3 sections of the control block (control byte, public key & merkle path) are highlighted here:
200-
// https://learnmeabitcoin.com/technical/upgrades/taproot/#script-path-spend-control-block
201-
// 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).
202-
// There is no consensus limit on n, but large Merkle trees increase the witness size, impacting block weight.
203-
// NOTE: Control blocks could have also been obtained from spend_info.control_block(..) using the data in control_block_data
204-
debug!("merkle_branch nodes: {:?}", merkle_branch);
187+
188+
let leaf_id = script_to_id.get(script)
189+
.unwrap_or_else(|| panic!("leaf script not found in script_to_id map: {}", hex::encode(script.as_bytes())));
190+
205191
let derived_control_block: P2mrControlBlock = P2mrControlBlock{
206192
merkle_branch: merkle_branch.clone(),
207193
};
208-
let serialized_control_block = derived_control_block.serialize();
209-
debug!("derived_control_block: {:?}, merkle_branch size: {}, control_block size: {}, serialized size: {}",
210-
derived_control_block,
211-
merkle_branch.len(),
212-
derived_control_block.size(),
213-
serialized_control_block.len());
214-
let derived_serialized_control_block = hex::encode(serialized_control_block);
215-
assert!(
216-
test_vector_control_blocks_set.contains(&derived_serialized_control_block),
217-
"Control block mismatch: {}, expected: {:?}", derived_serialized_control_block, test_vector_control_blocks_set
194+
let derived_serialized_control_block = hex::encode(derived_control_block.serialize());
195+
196+
let expected_cb = &expected_control_blocks[*leaf_id as usize];
197+
assert_eq!(
198+
derived_serialized_control_block, *expected_cb,
199+
"Control block mismatch for leaf id {}: derived {}, expected {}", leaf_id, derived_serialized_control_block, expected_cb
218200
);
219-
debug!("leaf_hash: {}, derived_serialized_control_block: {}", leaf_hash, derived_serialized_control_block);
201+
debug!("leaf_id: {}, leaf_hash: {}, derived_serialized_control_block: {}", leaf_id, leaf_hash, derived_serialized_control_block);
220202

221203
}
222204

0 commit comments

Comments
 (0)