mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(trie): use branch node hash masks in sparse trie (#13135)
This commit is contained in:
@ -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));
|
||||
|
||||
|
||||
@ -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()],
|
||||
|
||||
Reference in New Issue
Block a user