diff --git a/crates/trie/sparse/src/state.rs b/crates/trie/sparse/src/state.rs index 7c79b58e86..edaca5c1cf 100644 --- a/crates/trie/sparse/src/state.rs +++ b/crates/trie/sparse/src/state.rs @@ -103,12 +103,17 @@ impl SparseStateTrie { } /// Reveal unknown trie paths from provided leaf path and its proof for the account. + /// + /// Panics if trie updates retention is enabled. + /// /// NOTE: This method does not extensively validate the proof. pub fn reveal_account( &mut self, account: B256, proof: impl IntoIterator, ) -> SparseStateTrieResult<()> { + assert!(!self.retain_updates); + if self.is_account_revealed(&account) { return Ok(()); } @@ -121,13 +126,14 @@ impl SparseStateTrie { let trie = self.state.reveal_root_with_provider( self.provider_factory.account_node_provider(), root_node, + None, self.retain_updates, )?; // Reveal the remaining proof nodes. for (path, bytes) in proof { let node = TrieNode::decode(&mut &bytes[..])?; - trie.reveal_node(path, node)?; + trie.reveal_node(path, node, None)?; } // Mark leaf path as revealed. @@ -137,6 +143,9 @@ impl SparseStateTrie { } /// Reveal unknown trie paths from provided leaf path and its proof for the storage slot. + /// + /// Panics if trie updates retention is enabled. + /// /// NOTE: This method does not extensively validate the proof. pub fn reveal_storage_slot( &mut self, @@ -144,6 +153,8 @@ impl SparseStateTrie { slot: B256, proof: impl IntoIterator, ) -> SparseStateTrieResult<()> { + assert!(!self.retain_updates); + if self.is_storage_slot_revealed(&account, &slot) { return Ok(()); } @@ -156,13 +167,14 @@ impl SparseStateTrie { let trie = self.storages.entry(account).or_default().reveal_root_with_provider( self.provider_factory.storage_node_provider(account), root_node, + None, self.retain_updates, )?; // Reveal the remaining proof nodes. for (path, bytes) in proof { let node = TrieNode::decode(&mut &bytes[..])?; - trie.reveal_node(path, node)?; + trie.reveal_node(path, node, None)?; } // Mark leaf path as revealed. @@ -186,34 +198,48 @@ impl SparseStateTrie { let trie = self.state.reveal_root_with_provider( self.provider_factory.account_node_provider(), root_node, + multiproof.branch_node_hash_masks.get(&Nibbles::default()).copied(), self.retain_updates, )?; // Reveal the remaining proof nodes. for (path, bytes) in account_nodes { let node = TrieNode::decode(&mut &bytes[..])?; - trace!(target: "trie::sparse", ?path, ?node, "Revealing account node"); - trie.reveal_node(path, node)?; + let hash_mask = if let TrieNode::Branch(_) = node { + multiproof.branch_node_hash_masks.get(&path).copied() + } else { + None + }; + + trace!(target: "trie::sparse", ?path, ?node, ?hash_mask, "Revealing account node"); + trie.reveal_node(path, node, hash_mask)?; } } for (account, storage_subtree) in multiproof.storages { - let storage_subtree = storage_subtree.subtree.into_nodes_sorted(); - let mut storage_nodes = storage_subtree.into_iter().peekable(); + let subtree = storage_subtree.subtree.into_nodes_sorted(); + let mut nodes = subtree.into_iter().peekable(); - if let Some(root_node) = self.validate_root_node(&mut storage_nodes)? { + if let Some(root_node) = self.validate_root_node(&mut nodes)? { // Reveal root node if it wasn't already. let trie = self.storages.entry(account).or_default().reveal_root_with_provider( self.provider_factory.storage_node_provider(account), root_node, + storage_subtree.branch_node_hash_masks.get(&Nibbles::default()).copied(), self.retain_updates, )?; // Reveal the remaining proof nodes. - for (path, bytes) in storage_nodes { + for (path, bytes) in nodes { let node = TrieNode::decode(&mut &bytes[..])?; - trace!(target: "trie::sparse", ?account, ?path, ?node, "Revealing storage node"); - trie.reveal_node(path, node)?; + let hash_mask = if let TrieNode::Branch(_) = node { + storage_subtree.branch_node_hash_masks.get(&path).copied() + } else { + None + }; + + trace!(target: "trie::sparse", ?account, ?path, ?node, ?hash_mask, "Revealing storage node"); + trie.reveal_node(path, node, hash_mask)?; } } } @@ -394,27 +420,27 @@ mod tests { use rand::{rngs::StdRng, Rng, SeedableRng}; use reth_primitives_traits::Account; use reth_trie::{updates::StorageTrieUpdates, HashBuilder, TrieAccount, EMPTY_ROOT_HASH}; - use reth_trie_common::proof::ProofRetainer; + use reth_trie_common::{proof::ProofRetainer, StorageMultiProof, TrieMask}; #[test] - fn validate_proof_first_node_not_root() { + fn validate_root_node_first_node_not_root() { let sparse = SparseStateTrie::default(); let proof = [(Nibbles::from_nibbles([0x1]), Bytes::from([EMPTY_STRING_CODE]))]; assert_matches!( - sparse.validate_root_node(&mut proof.into_iter().peekable()), + sparse.validate_root_node(&mut proof.into_iter().peekable(),), Err(SparseStateTrieError::InvalidRootNode { .. }) ); } #[test] - fn validate_proof_invalid_proof_with_empty_root() { + fn validate_root_node_invalid_proof_with_empty_root() { let sparse = SparseStateTrie::default(); let proof = [ (Nibbles::default(), Bytes::from([EMPTY_STRING_CODE])), (Nibbles::from_nibbles([0x1]), Bytes::new()), ]; assert_matches!( - sparse.validate_root_node(&mut proof.into_iter().peekable()), + sparse.validate_root_node(&mut proof.into_iter().peekable(),), Err(SparseStateTrieError::InvalidRootNode { .. }) ); } @@ -429,6 +455,7 @@ mod tests { let mut sparse = SparseStateTrie::default(); assert_eq!(sparse.state, SparseTrie::Blind); + sparse.reveal_account(Default::default(), proofs.into_inner()).unwrap(); assert_eq!(sparse.state, SparseTrie::revealed_empty()); } @@ -443,6 +470,7 @@ mod tests { let mut sparse = SparseStateTrie::default(); assert!(sparse.storages.is_empty()); + sparse .reveal_storage_slot(Default::default(), Default::default(), proofs.into_inner()) .unwrap(); @@ -477,13 +505,15 @@ mod tests { slot_path_1.clone(), slot_path_2.clone(), ])); - storage_hash_builder.add_leaf(slot_path_1.clone(), &alloy_rlp::encode_fixed_size(&value_1)); - storage_hash_builder.add_leaf(slot_path_2.clone(), &alloy_rlp::encode_fixed_size(&value_2)); + storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1)); + storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2)); let storage_root = storage_hash_builder.root(); - let proof_nodes = storage_hash_builder.take_proof_nodes(); - let storage_proof_1 = proof_nodes.matching_nodes_sorted(&slot_path_1); - let storage_proof_2 = proof_nodes.matching_nodes_sorted(&slot_path_2); + let storage_proof_nodes = storage_hash_builder.take_proof_nodes(); + let storage_branch_node_hash_masks = HashMap::from_iter([ + (Nibbles::default(), TrieMask::new(0b010)), + (Nibbles::from_nibbles([0x1]), TrieMask::new(0b11)), + ]); let address_1 = b256!("1000000000000000000000000000000000000000000000000000000000000000"); let address_path_1 = Nibbles::unpack(address_1); @@ -504,16 +534,41 @@ mod tests { let root = hash_builder.root(); let proof_nodes = hash_builder.take_proof_nodes(); - let proof_1 = proof_nodes.matching_nodes_sorted(&address_path_1); - let proof_2 = proof_nodes.matching_nodes_sorted(&address_path_2); let mut sparse = SparseStateTrie::default().with_updates(true); - sparse.reveal_account(address_1, proof_1).unwrap(); - sparse.reveal_account(address_2, proof_2).unwrap(); - sparse.reveal_storage_slot(address_1, slot_1, storage_proof_1.clone()).unwrap(); - sparse.reveal_storage_slot(address_1, slot_2, storage_proof_2.clone()).unwrap(); - sparse.reveal_storage_slot(address_2, slot_1, storage_proof_1).unwrap(); - sparse.reveal_storage_slot(address_2, slot_2, storage_proof_2).unwrap(); + sparse + .reveal_multiproof( + HashMap::from_iter([ + (address_1, HashSet::from_iter([slot_1, slot_2])), + (address_2, HashSet::from_iter([slot_1, slot_2])), + ]), + MultiProof { + account_subtree: proof_nodes, + branch_node_hash_masks: HashMap::from_iter([( + Nibbles::from_nibbles([0x1]), + TrieMask::new(0b00), + )]), + storages: HashMap::from_iter([ + ( + address_1, + StorageMultiProof { + root, + subtree: storage_proof_nodes.clone(), + branch_node_hash_masks: storage_branch_node_hash_masks.clone(), + }, + ), + ( + address_2, + StorageMultiProof { + root, + subtree: storage_proof_nodes, + branch_node_hash_masks: storage_branch_node_hash_masks, + }, + ), + ]), + }, + ) + .unwrap(); assert_eq!(sparse.root(), Some(root)); diff --git a/crates/trie/sparse/src/trie.rs b/crates/trie/sparse/src/trie.rs index df5dd25486..8fff0819bc 100644 --- a/crates/trie/sparse/src/trie.rs +++ b/crates/trie/sparse/src/trie.rs @@ -59,9 +59,10 @@ impl SparseTrie { pub fn reveal_root( &mut self, root: TrieNode, + hash_mask: Option, retain_updates: bool, ) -> SparseTrieResult<&mut RevealedSparseTrie> { - self.reveal_root_with_provider(Default::default(), root, retain_updates) + self.reveal_root_with_provider(Default::default(), root, hash_mask, retain_updates) } } @@ -89,12 +90,14 @@ impl

SparseTrie

{ &mut self, provider: P, root: TrieNode, + hash_mask: Option, retain_updates: bool, ) -> SparseTrieResult<&mut RevealedSparseTrie

> { if self.is_blind() { *self = Self::Revealed(Box::new(RevealedSparseTrie::from_provider_and_root( provider, root, + hash_mask, retain_updates, )?)) } @@ -153,6 +156,8 @@ pub struct RevealedSparseTrie

{ provider: P, /// All trie nodes. nodes: HashMap, + /// All branch node hash masks. + branch_node_hash_masks: HashMap, /// All leaf values. values: HashMap>, /// Prefix set. @@ -167,6 +172,7 @@ impl

fmt::Debug for RevealedSparseTrie

{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RevealedSparseTrie") .field("nodes", &self.nodes) + .field("branch_hash_masks", &self.branch_node_hash_masks) .field("values", &self.values) .field("prefix_set", &self.prefix_set) .field("updates", &self.updates) @@ -180,6 +186,7 @@ impl Default for RevealedSparseTrie { Self { provider: Default::default(), nodes: HashMap::from_iter([(Nibbles::default(), SparseNode::Empty)]), + branch_node_hash_masks: HashMap::default(), values: HashMap::default(), prefix_set: PrefixSetMut::default(), updates: None, @@ -190,17 +197,22 @@ impl Default for RevealedSparseTrie { impl RevealedSparseTrie { /// Create new revealed sparse trie from the given root node. - pub fn from_root(node: TrieNode, retain_updates: bool) -> SparseTrieResult { + pub fn from_root( + node: TrieNode, + hash_mask: Option, + retain_updates: bool, + ) -> SparseTrieResult { let mut this = Self { provider: Default::default(), nodes: HashMap::default(), + branch_node_hash_masks: HashMap::default(), values: HashMap::default(), prefix_set: PrefixSetMut::default(), rlp_buf: Vec::new(), updates: None, } .with_updates(retain_updates); - this.reveal_node(Nibbles::default(), node)?; + this.reveal_node(Nibbles::default(), node, hash_mask)?; Ok(this) } } @@ -210,18 +222,20 @@ impl

RevealedSparseTrie

{ pub fn from_provider_and_root( provider: P, node: TrieNode, + hash_mask: Option, retain_updates: bool, ) -> SparseTrieResult { let mut this = Self { provider, nodes: HashMap::default(), + branch_node_hash_masks: HashMap::default(), values: HashMap::default(), prefix_set: PrefixSetMut::default(), rlp_buf: Vec::new(), updates: None, } .with_updates(retain_updates); - this.reveal_node(Nibbles::default(), node)?; + this.reveal_node(Nibbles::default(), node, hash_mask)?; Ok(this) } @@ -230,6 +244,7 @@ impl

RevealedSparseTrie

{ RevealedSparseTrie { provider, nodes: self.nodes, + branch_node_hash_masks: self.branch_node_hash_masks, values: self.values, prefix_set: self.prefix_set, updates: self.updates, @@ -261,7 +276,16 @@ impl

RevealedSparseTrie

{ } /// Reveal the trie node only if it was not known already. - pub fn reveal_node(&mut self, path: Nibbles, node: TrieNode) -> SparseTrieResult<()> { + pub fn reveal_node( + &mut self, + path: Nibbles, + node: TrieNode, + hash_mask: Option, + ) -> SparseTrieResult<()> { + if let Some(hash_mask) = hash_mask { + self.branch_node_hash_masks.insert(path.clone(), hash_mask); + } + match node { TrieNode::EmptyRoot => { debug_assert!(path.is_empty()); @@ -345,7 +369,7 @@ impl

RevealedSparseTrie

{ return Ok(()) } - self.reveal_node(path, TrieNode::decode(&mut &child[..])?) + self.reveal_node(path, TrieNode::decode(&mut &child[..])?, None) } /// Update the leaf node with provided value. @@ -720,25 +744,44 @@ impl

RevealedSparseTrie

{ // Update the masks only if we need to retain trie updates if self.updates.is_some() { // Set the trie mask - if node_type.store_in_db_trie() { + let tree_mask_value = if node_type.store_in_db_trie() { // A branch or an extension node explicitly set the // `store_in_db_trie` flag - tree_mask_values.push(true); + true } else { // Set the flag according to whether a child node was - // pre-calculated - // (`calculated = false`), meaning that it wasn't in the - // database - tree_mask_values.push(!calculated); - } + // pre-calculated (`calculated = false`), meaning that it wasn't + // in the database + !calculated + }; + tree_mask_values.push(tree_mask_value); - // Set the hash mask. If a child node has a hash value AND is a - // branch node, set the hash mask and save the hash. - let hash = child.as_hash().filter(|_| node_type.is_branch()); - hash_mask_values.push(hash.is_some()); + // Set the hash mask. If a child node is a revealed branch node OR + // is a blinded node that has its hash mask bit set according to the + // database, set the hash mask bit and save the hash. + let hash = child.as_hash().filter(|_| { + node_type.is_branch() || + (node_type.is_hash() && + self.branch_node_hash_masks + .get(&path) + .is_some_and(|mask| { + mask.is_bit_set(child_path.last().unwrap()) + })) + }); + let hash_mask_value = hash.is_some(); + hash_mask_values.push(hash_mask_value); if let Some(hash) = hash { hashes.push(hash); } + + trace!( + target: "trie::sparse", + ?path, + ?child_path, + ?tree_mask_value, + ?hash_mask_value, + "Updating branch node child masks" + ); } // Insert children in the resulting buffer in a normal order, @@ -933,7 +976,10 @@ where if let Some(node) = self.provider.blinded_node(child_path.clone())? { let decoded = TrieNode::decode(&mut &node[..])?; trace!(target: "trie::sparse", ?child_path, ?decoded, "Revealing remaining blinded branch child"); - self.reveal_node(child_path.clone(), decoded)?; + // We'll never have to update the revealed branch node, only remove + // or do nothing, so we can safely ignore the hash mask here and + // pass `None`. + self.reveal_node(child_path.clone(), decoded, None)?; } } @@ -1029,6 +1075,10 @@ enum SparseNodeType { } impl SparseNodeType { + const fn is_hash(&self) -> bool { + matches!(self, Self::Hash) + } + const fn is_branch(&self) -> bool { matches!(self, Self::Branch { .. }) } @@ -1215,7 +1265,7 @@ mod tests { state: impl IntoIterator + Clone, destroyed_accounts: HashSet, proof_targets: impl IntoIterator, - ) -> (B256, TrieUpdates, ProofNodes) { + ) -> (B256, TrieUpdates, ProofNodes, HashMap) { let mut account_rlp = Vec::new(); let mut hash_builder = HashBuilder::default() @@ -1255,12 +1305,19 @@ mod tests { } let root = hash_builder.root(); let proof_nodes = hash_builder.take_proof_nodes(); + let branch_node_hash_masks = hash_builder + .updated_branch_nodes + .clone() + .unwrap_or_default() + .iter() + .map(|(path, node)| (path.clone(), node.hash_mask)) + .collect(); let mut trie_updates = TrieUpdates::default(); let removed_keys = node_iter.walker.take_removed_keys(); trie_updates.finalize(hash_builder, removed_keys, destroyed_accounts); - (root, trie_updates, proof_nodes) + (root, trie_updates, proof_nodes, branch_node_hash_masks) } /// Assert that the sparse trie nodes and the proof nodes from the hash builder are equal. @@ -1322,7 +1379,7 @@ mod tests { account_rlp }; - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = run_hash_builder([(key.clone(), value())], Default::default(), [key.clone()]); let mut sparse = RevealedSparseTrie::default().with_updates(true); @@ -1347,11 +1404,12 @@ mod tests { account_rlp }; - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = run_hash_builder( - paths.iter().cloned().zip(std::iter::repeat_with(value)), - Default::default(), - paths.clone(), - ); + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = + run_hash_builder( + paths.iter().cloned().zip(std::iter::repeat_with(value)), + Default::default(), + paths.clone(), + ); let mut sparse = RevealedSparseTrie::default().with_updates(true); for path in &paths { @@ -1375,11 +1433,12 @@ mod tests { account_rlp }; - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = run_hash_builder( - paths.iter().cloned().zip(std::iter::repeat_with(value)), - Default::default(), - paths.clone(), - ); + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = + run_hash_builder( + paths.iter().cloned().zip(std::iter::repeat_with(value)), + Default::default(), + paths.clone(), + ); let mut sparse = RevealedSparseTrie::default().with_updates(true); for path in &paths { @@ -1411,11 +1470,12 @@ mod tests { account_rlp }; - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = run_hash_builder( - paths.iter().sorted_unstable().cloned().zip(std::iter::repeat_with(value)), - Default::default(), - paths.clone(), - ); + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = + run_hash_builder( + paths.iter().sorted_unstable().cloned().zip(std::iter::repeat_with(value)), + Default::default(), + paths.clone(), + ); let mut sparse = RevealedSparseTrie::default().with_updates(true); for path in &paths { @@ -1448,11 +1508,12 @@ mod tests { account_rlp }; - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = run_hash_builder( - paths.iter().cloned().zip(std::iter::repeat_with(|| old_value)), - Default::default(), - paths.clone(), - ); + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = + run_hash_builder( + paths.iter().cloned().zip(std::iter::repeat_with(|| old_value)), + Default::default(), + paths.clone(), + ); let mut sparse = RevealedSparseTrie::default().with_updates(true); for path in &paths { @@ -1465,11 +1526,12 @@ mod tests { assert_eq!(sparse_updates.updated_nodes, hash_builder_updates.account_nodes); assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = run_hash_builder( - paths.iter().cloned().zip(std::iter::repeat_with(|| new_value)), - Default::default(), - paths.clone(), - ); + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = + run_hash_builder( + paths.iter().cloned().zip(std::iter::repeat_with(|| new_value)), + Default::default(), + paths.clone(), + ); for path in &paths { sparse.update_leaf(path.clone(), new_value_encoded.clone()).unwrap(); @@ -1737,15 +1799,17 @@ mod tests { TrieMask::new(0b11), )); - let mut sparse = RevealedSparseTrie::from_root(branch.clone(), false).unwrap(); + let mut sparse = + RevealedSparseTrie::from_root(branch.clone(), Some(TrieMask::new(0b01)), false) + .unwrap(); // Reveal a branch node and one of its children // // Branch (Mask = 11) // ├── 0 -> Hash (Path = 0) // └── 1 -> Leaf (Path = 1) - sparse.reveal_node(Nibbles::default(), branch).unwrap(); - sparse.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf)).unwrap(); + sparse.reveal_node(Nibbles::default(), branch, Some(TrieMask::new(0b01))).unwrap(); + sparse.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf), None).unwrap(); // Removing a blinded leaf should result in an error assert_matches!( @@ -1768,15 +1832,17 @@ mod tests { TrieMask::new(0b11), )); - let mut sparse = RevealedSparseTrie::from_root(branch.clone(), false).unwrap(); + let mut sparse = + RevealedSparseTrie::from_root(branch.clone(), Some(TrieMask::new(0b01)), false) + .unwrap(); // Reveal a branch node and one of its children // // Branch (Mask = 11) // ├── 0 -> Hash (Path = 0) // └── 1 -> Leaf (Path = 1) - sparse.reveal_node(Nibbles::default(), branch).unwrap(); - sparse.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf)).unwrap(); + sparse.reveal_node(Nibbles::default(), branch, Some(TrieMask::new(0b01))).unwrap(); + sparse.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf), None).unwrap(); // Removing a non-existent leaf should be a noop let sparse_old = sparse.clone(); @@ -1814,7 +1880,7 @@ mod tests { // Insert state updates into the hash builder and calculate the root state.extend(update); - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = run_hash_builder( state.clone(), Default::default(), @@ -1845,7 +1911,7 @@ mod tests { let sparse_root = updated_sparse.root(); let sparse_updates = updated_sparse.take_updates(); - let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes) = + let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _) = run_hash_builder( state.clone(), Default::default(), @@ -1926,22 +1992,24 @@ mod tests { }; // Generate the proof for the root node and initialize the sparse trie with it - let (_, _, hash_builder_proof_nodes) = run_hash_builder( + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder( [(key1(), value()), (key3(), value())], Default::default(), [Nibbles::default()], ); let mut sparse = RevealedSparseTrie::from_root( TrieNode::decode(&mut &hash_builder_proof_nodes.nodes_sorted()[0].1[..]).unwrap(), + branch_node_hash_masks.get(&Nibbles::default()).copied(), false, ) .unwrap(); // Generate the proof for the first key and reveal it in the sparse trie - let (_, _, hash_builder_proof_nodes) = + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder([(key1(), value()), (key3(), value())], Default::default(), [key1()]); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { - sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap()).unwrap(); + let hash_mask = branch_node_hash_masks.get(&path).copied(); + sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap(), hash_mask).unwrap(); } // Check that the branch node exists with only two nibbles set @@ -1960,10 +2028,11 @@ mod tests { ); // Generate the proof for the third key and reveal it in the sparse trie - let (_, _, hash_builder_proof_nodes) = + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder([(key1(), value()), (key3(), value())], Default::default(), [key3()]); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { - sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap()).unwrap(); + let hash_mask = branch_node_hash_masks.get(&path).copied(); + sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap(), hash_mask).unwrap(); } // Check that nothing changed in the branch node @@ -1974,7 +2043,7 @@ mod tests { // Generate the nodes for the full trie with all three key using the hash builder, and // compare them to the sparse trie - let (_, _, hash_builder_proof_nodes) = run_hash_builder( + let (_, _, hash_builder_proof_nodes, _) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], Default::default(), [key1(), key2(), key3()], @@ -2001,26 +2070,28 @@ mod tests { let value = || Account::default(); // Generate the proof for the root node and initialize the sparse trie with it - let (_, _, hash_builder_proof_nodes) = run_hash_builder( + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], Default::default(), [Nibbles::default()], ); let mut sparse = RevealedSparseTrie::from_root( TrieNode::decode(&mut &hash_builder_proof_nodes.nodes_sorted()[0].1[..]).unwrap(), + branch_node_hash_masks.get(&Nibbles::default()).copied(), false, ) .unwrap(); // Generate the proof for the children of the root branch node and reveal it in the sparse // trie - let (_, _, hash_builder_proof_nodes) = run_hash_builder( + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], Default::default(), [key1(), Nibbles::from_nibbles_unchecked([0x01])], ); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { - sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap()).unwrap(); + let hash_mask = branch_node_hash_masks.get(&path).copied(); + sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap(), hash_mask).unwrap(); } // Check that the branch node exists @@ -2039,13 +2110,14 @@ mod tests { ); // Generate the proof for the third key and reveal it in the sparse trie - let (_, _, hash_builder_proof_nodes) = run_hash_builder( + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], Default::default(), [key2()], ); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { - sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap()).unwrap(); + let hash_mask = branch_node_hash_masks.get(&path).copied(); + sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap(), hash_mask).unwrap(); } // Check that nothing changed in the extension node @@ -2076,13 +2148,14 @@ mod tests { }; // Generate the proof for the root node and initialize the sparse trie with it - let (_, _, hash_builder_proof_nodes) = run_hash_builder( + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder( [(key1(), value()), (key2(), value())], Default::default(), [Nibbles::default()], ); let mut sparse = RevealedSparseTrie::from_root( TrieNode::decode(&mut &hash_builder_proof_nodes.nodes_sorted()[0].1[..]).unwrap(), + branch_node_hash_masks.get(&Nibbles::default()).copied(), false, ) .unwrap(); @@ -2103,10 +2176,11 @@ mod tests { ); // Generate the proof for the first key and reveal it in the sparse trie - let (_, _, hash_builder_proof_nodes) = + let (_, _, hash_builder_proof_nodes, branch_node_hash_masks) = run_hash_builder([(key1(), value()), (key2(), value())], Default::default(), [key1()]); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { - sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap()).unwrap(); + let hash_mask = branch_node_hash_masks.get(&path).copied(); + sparse.reveal_node(path, TrieNode::decode(&mut &node[..]).unwrap(), hash_mask).unwrap(); } // Check that the branch node wasn't overwritten by the extension node in the proof @@ -2200,7 +2274,7 @@ mod tests { account_rlp }; - let (hash_builder_root, hash_builder_updates, _) = run_hash_builder( + let (hash_builder_root, hash_builder_updates, _, _) = run_hash_builder( [(key1(), value()), (key2(), value())], Default::default(), [Nibbles::default()],