mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
test(trie): use DB for hash builder in sparse trie fuzz (#13754)
Co-authored-by: Roman Krasiuk <rokrassyuk@gmail.com>
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -9441,10 +9441,13 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"reth-execution-errors",
|
||||
"reth-primitives-traits",
|
||||
"reth-provider",
|
||||
"reth-storage-api",
|
||||
"reth-testing-utils",
|
||||
"reth-tracing",
|
||||
"reth-trie",
|
||||
"reth-trie-common",
|
||||
"reth-trie-db",
|
||||
"smallvec",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
@ -28,9 +28,12 @@ thiserror.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
reth-primitives-traits = { workspace = true, features = ["arbitrary"] }
|
||||
reth-provider = { workspace = true, features = ["test-utils"] }
|
||||
reth-storage-api.workspace = true
|
||||
reth-testing-utils.workspace = true
|
||||
reth-trie = { workspace = true, features = ["test-utils"] }
|
||||
reth-trie-common = { workspace = true, features = ["test-utils", "arbitrary"] }
|
||||
reth-testing-utils.workspace = true
|
||||
reth-trie-db = { workspace = true, features = ["test-utils"] }
|
||||
|
||||
arbitrary.workspace = true
|
||||
assert_matches.workspace = true
|
||||
@ -44,8 +47,10 @@ rand.workspace = true
|
||||
[features]
|
||||
test-utils = [
|
||||
"reth-primitives-traits/test-utils",
|
||||
"reth-trie/test-utils",
|
||||
"reth-provider/test-utils",
|
||||
"reth-trie-common/test-utils",
|
||||
"reth-trie-db/test-utils",
|
||||
"reth-trie/test-utils",
|
||||
]
|
||||
arbitrary = [
|
||||
"reth-primitives-traits/arbitrary",
|
||||
|
||||
@ -1401,10 +1401,7 @@ impl SparseTrieUpdates {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloy_primitives::{
|
||||
map::{B256HashSet, HashSet},
|
||||
U256,
|
||||
};
|
||||
use alloy_primitives::{map::B256HashSet, U256};
|
||||
use alloy_rlp::Encodable;
|
||||
use assert_matches::assert_matches;
|
||||
use itertools::Itertools;
|
||||
@ -1413,19 +1410,21 @@ mod tests {
|
||||
use proptest_arbitrary_interop::arb;
|
||||
use rand::seq::IteratorRandom;
|
||||
use reth_primitives_traits::Account;
|
||||
use reth_provider::{test_utils::create_test_provider_factory, TrieWriter};
|
||||
use reth_trie::{
|
||||
hashed_cursor::{noop::NoopHashedAccountCursor, HashedPostStateAccountCursor},
|
||||
node_iter::{TrieElement, TrieNodeIter},
|
||||
trie_cursor::noop::NoopAccountTrieCursor,
|
||||
updates::TrieUpdates,
|
||||
trie_cursor::{noop::NoopAccountTrieCursor, TrieCursor, TrieCursorFactory},
|
||||
walker::TrieWalker,
|
||||
BranchNode, ExtensionNode, HashedPostState, LeafNode,
|
||||
};
|
||||
use reth_trie_common::{
|
||||
proof::{ProofNodes, ProofRetainer},
|
||||
updates::TrieUpdates,
|
||||
HashBuilder,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use reth_trie_db::DatabaseTrieCursorFactory;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
/// Pad nibbles to the length of a B256 hash with zeros on the left.
|
||||
fn pad_nibbles_left(nibbles: Nibbles) -> Nibbles {
|
||||
@ -1447,6 +1446,7 @@ mod tests {
|
||||
/// Returns the state root and the retained proof nodes.
|
||||
fn run_hash_builder(
|
||||
state: impl IntoIterator<Item = (Nibbles, Account)> + Clone,
|
||||
trie_cursor: impl TrieCursor,
|
||||
destroyed_accounts: B256HashSet,
|
||||
proof_targets: impl IntoIterator<Item = Nibbles>,
|
||||
) -> (B256, TrieUpdates, ProofNodes, HashMap<Nibbles, TrieMask>, HashMap<Nibbles, TrieMask>)
|
||||
@ -1459,8 +1459,9 @@ mod tests {
|
||||
|
||||
let mut prefix_set = PrefixSetMut::default();
|
||||
prefix_set.extend_keys(state.clone().into_iter().map(|(nibbles, _)| nibbles));
|
||||
let walker = TrieWalker::new(NoopAccountTrieCursor::default(), prefix_set.freeze())
|
||||
.with_deletions_retained(true);
|
||||
prefix_set.extend_keys(destroyed_accounts.iter().map(Nibbles::unpack));
|
||||
let walker =
|
||||
TrieWalker::new(trie_cursor, prefix_set.freeze()).with_deletions_retained(true);
|
||||
let hashed_post_state = HashedPostState::default()
|
||||
.with_accounts(state.into_iter().map(|(nibbles, account)| {
|
||||
(nibbles.pack().into_inner().unwrap().into(), Some(account))
|
||||
@ -1551,7 +1552,11 @@ mod tests {
|
||||
(_, SparseNode::Empty | SparseNode::Hash(_)) => continue,
|
||||
_ => false,
|
||||
};
|
||||
assert!(equals, "proof node: {:?}, sparse node: {:?}", proof_node, sparse_node);
|
||||
assert!(
|
||||
equals,
|
||||
"path: {:?}\nproof node: {:?}\nsparse node: {:?}",
|
||||
proof_node_path, proof_node, sparse_node
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1572,7 +1577,12 @@ mod tests {
|
||||
};
|
||||
|
||||
let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
|
||||
run_hash_builder([(key.clone(), value())], Default::default(), [key.clone()]);
|
||||
run_hash_builder(
|
||||
[(key.clone(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[key.clone()],
|
||||
);
|
||||
|
||||
let mut sparse = RevealedSparseTrie::default().with_updates(true);
|
||||
sparse.update_leaf(key, value_encoded()).unwrap();
|
||||
@ -1599,6 +1609,7 @@ mod tests {
|
||||
let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
|
||||
run_hash_builder(
|
||||
paths.iter().cloned().zip(std::iter::repeat_with(value)),
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
paths.clone(),
|
||||
);
|
||||
@ -1628,6 +1639,7 @@ mod tests {
|
||||
let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
|
||||
run_hash_builder(
|
||||
paths.iter().cloned().zip(std::iter::repeat_with(value)),
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
paths.clone(),
|
||||
);
|
||||
@ -1665,6 +1677,7 @@ mod tests {
|
||||
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)),
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
paths.clone(),
|
||||
);
|
||||
@ -1703,6 +1716,7 @@ mod tests {
|
||||
let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
|
||||
run_hash_builder(
|
||||
paths.iter().cloned().zip(std::iter::repeat_with(|| old_value)),
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
paths.clone(),
|
||||
);
|
||||
@ -1721,6 +1735,7 @@ mod tests {
|
||||
let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
|
||||
run_hash_builder(
|
||||
paths.iter().cloned().zip(std::iter::repeat_with(|| new_value)),
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
paths.clone(),
|
||||
);
|
||||
@ -2050,9 +2065,10 @@ mod tests {
|
||||
// to test the sparse trie updates.
|
||||
const KEY_NIBBLES_LEN: usize = 3;
|
||||
|
||||
fn test(updates: Vec<(HashMap<Nibbles, Account>, HashSet<Nibbles>)>) {
|
||||
fn test(updates: Vec<(BTreeMap<Nibbles, Account>, BTreeSet<Nibbles>)>) {
|
||||
{
|
||||
let mut state = BTreeMap::default();
|
||||
let provider_factory = create_test_provider_factory();
|
||||
let mut sparse = RevealedSparseTrie::default().with_updates(true);
|
||||
|
||||
for (update, keys_to_delete) in updates {
|
||||
@ -2072,28 +2088,36 @@ mod tests {
|
||||
|
||||
// Insert state updates into the hash builder and calculate the root
|
||||
state.extend(update);
|
||||
let provider = provider_factory.provider().unwrap();
|
||||
let trie_cursor = DatabaseTrieCursorFactory::new(provider.tx_ref());
|
||||
let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
|
||||
run_hash_builder(
|
||||
state.clone(),
|
||||
trie_cursor.account_trie_cursor().unwrap(),
|
||||
Default::default(),
|
||||
state.keys().cloned().collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// Write trie updates to the database
|
||||
let provider_rw = provider_factory.provider_rw().unwrap();
|
||||
provider_rw.write_trie_updates(&hash_builder_updates).unwrap();
|
||||
provider_rw.commit().unwrap();
|
||||
|
||||
// Assert that the sparse trie root matches the hash builder root
|
||||
assert_eq!(sparse_root, hash_builder_root);
|
||||
// Assert that the sparse trie updates match the hash builder updates
|
||||
pretty_assertions::assert_eq!(
|
||||
sparse_updates.updated_nodes,
|
||||
hash_builder_updates.account_nodes
|
||||
BTreeMap::from_iter(sparse_updates.updated_nodes),
|
||||
BTreeMap::from_iter(hash_builder_updates.account_nodes)
|
||||
);
|
||||
// Assert that the sparse trie nodes match the hash builder proof nodes
|
||||
assert_eq_sparse_trie_proof_nodes(&updated_sparse, hash_builder_proof_nodes);
|
||||
|
||||
// Delete some keys from both the hash builder and the sparse trie and check
|
||||
// that the sparse trie root still matches the hash builder root
|
||||
for key in keys_to_delete {
|
||||
state.remove(&key).unwrap();
|
||||
sparse.remove_leaf(&key).unwrap();
|
||||
for key in &keys_to_delete {
|
||||
state.remove(key).unwrap();
|
||||
sparse.remove_leaf(key).unwrap();
|
||||
}
|
||||
|
||||
// We need to clone the sparse trie, so that all updated branch nodes are
|
||||
@ -2103,19 +2127,30 @@ mod tests {
|
||||
let sparse_root = updated_sparse.root();
|
||||
let sparse_updates = updated_sparse.take_updates();
|
||||
|
||||
let provider = provider_factory.provider().unwrap();
|
||||
let trie_cursor = DatabaseTrieCursorFactory::new(provider.tx_ref());
|
||||
let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
|
||||
run_hash_builder(
|
||||
state.clone(),
|
||||
Default::default(),
|
||||
trie_cursor.account_trie_cursor().unwrap(),
|
||||
keys_to_delete
|
||||
.iter()
|
||||
.map(|nibbles| B256::from_slice(&nibbles.pack()))
|
||||
.collect(),
|
||||
state.keys().cloned().collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// Write trie updates to the database
|
||||
let provider_rw = provider_factory.provider_rw().unwrap();
|
||||
provider_rw.write_trie_updates(&hash_builder_updates).unwrap();
|
||||
provider_rw.commit().unwrap();
|
||||
|
||||
// Assert that the sparse trie root matches the hash builder root
|
||||
assert_eq!(sparse_root, hash_builder_root);
|
||||
// Assert that the sparse trie updates match the hash builder updates
|
||||
pretty_assertions::assert_eq!(
|
||||
sparse_updates.updated_nodes,
|
||||
hash_builder_updates.account_nodes
|
||||
BTreeMap::from_iter(sparse_updates.updated_nodes),
|
||||
BTreeMap::from_iter(hash_builder_updates.account_nodes)
|
||||
);
|
||||
// Assert that the sparse trie nodes match the hash builder proof nodes
|
||||
assert_eq_sparse_trie_proof_nodes(&updated_sparse, hash_builder_proof_nodes);
|
||||
@ -2124,10 +2159,10 @@ mod tests {
|
||||
}
|
||||
|
||||
fn transform_updates(
|
||||
updates: Vec<HashMap<Nibbles, Account>>,
|
||||
updates: Vec<BTreeMap<Nibbles, Account>>,
|
||||
mut rng: impl Rng,
|
||||
) -> Vec<(HashMap<Nibbles, Account>, HashSet<Nibbles>)> {
|
||||
let mut keys = HashSet::new();
|
||||
) -> Vec<(BTreeMap<Nibbles, Account>, BTreeSet<Nibbles>)> {
|
||||
let mut keys = BTreeSet::new();
|
||||
updates
|
||||
.into_iter()
|
||||
.map(|update| {
|
||||
@ -2148,12 +2183,12 @@ mod tests {
|
||||
|
||||
proptest!(ProptestConfig::with_cases(10), |(
|
||||
updates in proptest::collection::vec(
|
||||
proptest::collection::hash_map(
|
||||
proptest::collection::btree_map(
|
||||
any_with::<Nibbles>(SizeRange::new(KEY_NIBBLES_LEN..=KEY_NIBBLES_LEN)).prop_map(pad_nibbles_right),
|
||||
arb::<Account>(),
|
||||
1..100,
|
||||
).prop_map(HashMap::from_iter),
|
||||
1..100,
|
||||
1..50,
|
||||
),
|
||||
1..50,
|
||||
).prop_perturb(transform_updates)
|
||||
)| {
|
||||
test(updates)
|
||||
@ -2187,6 +2222,7 @@ mod tests {
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key3(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[Nibbles::default()],
|
||||
);
|
||||
@ -2200,7 +2236,12 @@ mod tests {
|
||||
|
||||
// Generate the proof for the first key and reveal it in the sparse trie
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder([(key1(), value()), (key3(), value())], Default::default(), [key1()]);
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key3(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[key1()],
|
||||
);
|
||||
for (path, node) in hash_builder_proof_nodes.nodes_sorted() {
|
||||
let hash_mask = branch_node_hash_masks.get(&path).copied();
|
||||
let tree_mask = branch_node_tree_masks.get(&path).copied();
|
||||
@ -2226,7 +2267,12 @@ mod tests {
|
||||
|
||||
// Generate the proof for the third key and reveal it in the sparse trie
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder([(key1(), value()), (key3(), value())], Default::default(), [key3()]);
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key3(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[key3()],
|
||||
);
|
||||
for (path, node) in hash_builder_proof_nodes.nodes_sorted() {
|
||||
let hash_mask = branch_node_hash_masks.get(&path).copied();
|
||||
let tree_mask = branch_node_tree_masks.get(&path).copied();
|
||||
@ -2245,6 +2291,7 @@ mod tests {
|
||||
// compare them to the sparse trie
|
||||
let (_, _, hash_builder_proof_nodes, _, _) = run_hash_builder(
|
||||
[(key1(), value()), (key2(), value()), (key3(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[key1(), key2(), key3()],
|
||||
);
|
||||
@ -2273,6 +2320,7 @@ mod tests {
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key2(), value()), (key3(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[Nibbles::default()],
|
||||
);
|
||||
@ -2289,6 +2337,7 @@ mod tests {
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key2(), value()), (key3(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[key1(), Nibbles::from_nibbles_unchecked([0x01])],
|
||||
);
|
||||
@ -2319,6 +2368,7 @@ mod tests {
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key2(), value()), (key3(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[key2()],
|
||||
);
|
||||
@ -2361,6 +2411,7 @@ mod tests {
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key2(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[Nibbles::default()],
|
||||
);
|
||||
@ -2389,7 +2440,12 @@ mod tests {
|
||||
|
||||
// Generate the proof for the first key and reveal it in the sparse trie
|
||||
let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
|
||||
run_hash_builder([(key1(), value()), (key2(), value())], Default::default(), [key1()]);
|
||||
run_hash_builder(
|
||||
[(key1(), value()), (key2(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[key1()],
|
||||
);
|
||||
for (path, node) in hash_builder_proof_nodes.nodes_sorted() {
|
||||
let hash_mask = branch_node_hash_masks.get(&path).copied();
|
||||
let tree_mask = branch_node_tree_masks.get(&path).copied();
|
||||
@ -2491,6 +2547,7 @@ mod tests {
|
||||
|
||||
let (hash_builder_root, hash_builder_updates, _, _, _) = run_hash_builder(
|
||||
[(key1(), value()), (key2(), value())],
|
||||
NoopAccountTrieCursor::default(),
|
||||
Default::default(),
|
||||
[Nibbles::default()],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user