fix(trie): short circuit leaf removal if missing (#12988)

Co-authored-by: Alexey Shekhirin <a.shekhirin@gmail.com>
This commit is contained in:
Roman Krasiuk
2024-12-03 14:40:29 +01:00
committed by GitHub
parent a8feec839f
commit 0aa4701d30

View File

@ -823,8 +823,16 @@ where
{ {
/// Remove leaf node from the trie. /// Remove leaf node from the trie.
pub fn remove_leaf(&mut self, path: &Nibbles) -> SparseTrieResult<()> { pub fn remove_leaf(&mut self, path: &Nibbles) -> SparseTrieResult<()> {
if self.values.remove(path).is_none() {
if let Some(SparseNode::Hash(hash)) = self.nodes.get(path) {
// Leaf is present in the trie, but it's blinded.
return Err(SparseTrieError::BlindedNode { path: path.clone(), hash: *hash })
}
// Leaf is not present in the trie.
return Ok(())
}
self.prefix_set.insert(path.clone()); self.prefix_set.insert(path.clone());
self.values.remove(path);
// If the path wasn't present in `values`, we still need to walk the trie and ensure that // If the path wasn't present in `values`, we still need to walk the trie and ensure that
// there is no node at the path. When a leaf node is a blinded `Hash`, it will have an entry // there is no node at the path. When a leaf node is a blinded `Hash`, it will have an entry
@ -1731,6 +1739,36 @@ mod tests {
); );
} }
#[test]
fn sparse_trie_remove_leaf_non_existent() {
let leaf = LeafNode::new(
Nibbles::default(),
alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec(),
);
let branch = TrieNode::Branch(BranchNode::new(
vec![
RlpNode::word_rlp(&B256::repeat_byte(1)),
RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(),
],
TrieMask::new(0b11),
));
let mut sparse = RevealedSparseTrie::from_root(branch.clone(), 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();
// Removing a non-existent leaf should be a noop
let sparse_old = sparse.clone();
assert_matches!(sparse.remove_leaf(&Nibbles::from_nibbles([0x2])), Ok(()));
assert_eq!(sparse, sparse_old);
}
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[test] #[test]
fn sparse_trie_fuzz() { fn sparse_trie_fuzz() {