Constructs a "mid-state" Merkle tree, designed for generating secure SPV (Simplified Payment Verification) proofs. This structure, when used with the corresponding `calculate_root_with_merkle_proof` (or `BlockInclusionProof::get_root`) method, helps mitigate vulnerabilities associated with standard Bitcoin Merkle trees in SPV contexts, such as certain forms of hash duplication or ambiguity attacks
(transactions: &[CircuitTransaction])
| 103 | /// This acts as a domain separation, ensuring that the internal nodes of this mid-state tree cannot be misinterpreted |
| 104 | /// as leaf txids or other hash types during verification. |
| 105 | pub fn new_mid_state(transactions: &[CircuitTransaction]) -> Self { |
| 106 | if transactions.len() == 1 { |
| 107 | // root is the coinbase mid-state txid (which is a standard txid) |
| 108 | return BitcoinMerkleTree { |
| 109 | nodes: vec![vec![transactions[0].mid_state_txid()]], |
| 110 | }; |
| 111 | } |
| 112 | |
| 113 | let mid_state_txids: Vec<[u8; 32]> = |
| 114 | transactions.iter().map(|tx| tx.mid_state_txid()).collect(); |
| 115 | |
| 116 | let mut tree = BitcoinMerkleTree { |
| 117 | nodes: vec![mid_state_txids], // Level 0: Leaf nodes (txids) |
| 118 | }; |
| 119 | |
| 120 | // Construct the tree |
| 121 | let mut curr_level_offset: usize = 1; |
| 122 | let mut prev_level_size = tree.nodes[0].len(); |
| 123 | let mut prev_level_index_offset = 0; |
| 124 | let mut preimage: [u8; 64] = [0; 64]; // Preimage for SHA256(SHA256(LeftChild) || SHA256(RightChild)) |
| 125 | while prev_level_size > 1 { |
| 126 | tree.nodes.push(vec![]); |
| 127 | for i in 0..(prev_level_size / 2) { |
| 128 | let left_child_node = |
| 129 | tree.nodes[curr_level_offset - 1][prev_level_index_offset + i * 2]; |
| 130 | let right_child_node = |
| 131 | tree.nodes[curr_level_offset - 1][prev_level_index_offset + i * 2 + 1]; |
| 132 | |
| 133 | if left_child_node == right_child_node { |
| 134 | // This check is also present in the mid-state tree construction. |
| 135 | // While the primary defense is in the proof verification, preventing duplicate |
| 136 | // inputs at this stage is good practice. |
| 137 | panic!("Duplicate hashes in the Merkle tree, indicating mutation"); |
| 138 | } |
| 139 | // Preimage construction: SHA256(LeftChildNode) || SHA256(RightChildNode) |
| 140 | preimage[..32].copy_from_slice(&calculate_sha256(&left_child_node)); |
| 141 | preimage[32..].copy_from_slice(&calculate_sha256(&right_child_node)); |
| 142 | // The new node is SHA256 of this preimage |
| 143 | let combined_mid_state_hash = calculate_sha256(&preimage); |
| 144 | tree.nodes[curr_level_offset].push(combined_mid_state_hash); |
| 145 | } |
| 146 | // Handle odd number of nodes at the previous level by duplicating the last node's hash processing |
| 147 | if prev_level_size % 2 == 1 { |
| 148 | let mut preimage: [u8; 64] = [0; 64]; |
| 149 | let last_node = tree.nodes[curr_level_offset - 1] |
| 150 | [prev_level_index_offset + prev_level_size - 1]; |
| 151 | // Preimage: SHA256(LastNode) || SHA256(LastNode) |
| 152 | preimage[..32].copy_from_slice(&calculate_sha256(&last_node)); |
| 153 | preimage[32..].copy_from_slice(&calculate_sha256(&last_node)); |
| 154 | let combined_mid_state_hash = calculate_sha256(&preimage); |
| 155 | tree.nodes[curr_level_offset].push(combined_mid_state_hash); |
| 156 | } |
| 157 | curr_level_offset += 1; |
| 158 | prev_level_size = prev_level_size.div_ceil(2); |
| 159 | prev_level_index_offset = 0; |
| 160 | } |
| 161 | tree |
| 162 | } |
nothing calls this directly
no test coverage detected