feat(trie): use branch node hash masks in sparse trie (#13135)

This commit is contained in:
Alexey Shekhirin
2024-12-06 08:45:08 +00:00
committed by GitHub
parent da98285469
commit 6453b62094
2 changed files with 226 additions and 97 deletions

View File

@ -103,12 +103,17 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
}
/// 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<Item = (Nibbles, Bytes)>,
) -> SparseStateTrieResult<()> {
assert!(!self.retain_updates);
if self.is_account_revealed(&account) {
return Ok(());
}
@ -121,13 +126,14 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
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<F: BlindedProviderFactory> SparseStateTrie<F> {
}
/// 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<F: BlindedProviderFactory> SparseStateTrie<F> {
slot: B256,
proof: impl IntoIterator<Item = (Nibbles, Bytes)>,
) -> SparseStateTrieResult<()> {
assert!(!self.retain_updates);
if self.is_storage_slot_revealed(&account, &slot) {
return Ok(());
}
@ -156,13 +167,14 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
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<F: BlindedProviderFactory> SparseStateTrie<F> {
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));

View File

@ -59,9 +59,10 @@ impl SparseTrie {
pub fn reveal_root(
&mut self,
root: TrieNode,
hash_mask: Option<TrieMask>,
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<P> SparseTrie<P> {
&mut self,
provider: P,
root: TrieNode,
hash_mask: Option<TrieMask>,
retain_updates: bool,
) -> SparseTrieResult<&mut RevealedSparseTrie<P>> {
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<P = DefaultBlindedProvider> {
provider: P,
/// All trie nodes.
nodes: HashMap<Nibbles, SparseNode>,
/// All branch node hash masks.
branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
/// All leaf values.
values: HashMap<Nibbles, Vec<u8>>,
/// Prefix set.
@ -167,6 +172,7 @@ impl<P> fmt::Debug for RevealedSparseTrie<P> {
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<Self> {
pub fn from_root(
node: TrieNode,
hash_mask: Option<TrieMask>,
retain_updates: bool,
) -> SparseTrieResult<Self> {
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<P> RevealedSparseTrie<P> {
pub fn from_provider_and_root(
provider: P,
node: TrieNode,
hash_mask: Option<TrieMask>,
retain_updates: bool,
) -> SparseTrieResult<Self> {
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<P> RevealedSparseTrie<P> {
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<P> RevealedSparseTrie<P> {
}
/// 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<TrieMask>,
) -> 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<P> RevealedSparseTrie<P> {
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<P> RevealedSparseTrie<P> {
// 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<Item = (Nibbles, Account)> + Clone,
destroyed_accounts: HashSet<B256>,
proof_targets: impl IntoIterator<Item = Nibbles>,
) -> (B256, TrieUpdates, ProofNodes) {
) -> (B256, TrieUpdates, ProofNodes, HashMap<Nibbles, TrieMask>) {
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()],