reth_e2e_test_utils/
trie.rs1use alloy_consensus::BlockHeader;
4use eyre::{ensure, eyre, Result};
5use reth_provider::{
6 BlockNumReader, DBProvider, DatabaseProviderFactory, HeaderProvider, StorageSettingsCache,
7};
8use reth_trie::{
9 prefix_set::{PrefixSetMut, TriePrefixSets},
10 verify::{Output, Verifier},
11 StateRoot,
12};
13use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
14
15pub fn assert_trie_consistency<F>(factory: &F) -> Result<()>
29where
30 F: DatabaseProviderFactory<
31 Provider: DBProvider + StorageSettingsCache + BlockNumReader + HeaderProvider,
32 >,
33{
34 let provider = factory.database_provider_ro()?;
35 let tip = provider.best_block_number()?;
36 let header = provider
37 .header_by_number(tip)?
38 .ok_or_else(|| eyre!("missing persisted header for block {tip}"))?;
39 let tx = provider.tx_ref();
40
41 reth_trie_db::with_adapter!(provider, |A| {
42 let recomputed = StateRoot::new(
46 DatabaseTrieCursorFactory::<_, A>::new(tx),
47 DatabaseHashedCursorFactory::new(tx),
48 )
49 .with_prefix_sets(TriePrefixSets {
50 account_prefix_set: PrefixSetMut::all().freeze(),
51 ..Default::default()
52 })
53 .root()?;
54 ensure!(
55 recomputed == header.state_root(),
56 "state root {recomputed} recomputed from the persisted state does not match the state root of persisted block {tip}: {}",
57 header.state_root()
58 );
59
60 let trie_cursor_factory = DatabaseTrieCursorFactory::<_, A>::new(tx);
61 let verifier = Verifier::new(&trie_cursor_factory, DatabaseHashedCursorFactory::new(tx))?;
62 let mut inconsistencies = Vec::new();
63 for output in verifier {
64 match output? {
65 Output::Progress(_) => {}
66 inconsistency => inconsistencies.push(inconsistency),
67 }
68 }
69 ensure!(
70 inconsistencies.is_empty(),
71 "persisted trie is inconsistent with the persisted hashed state: {inconsistencies:?}"
72 );
73
74 Ok(())
75 })
76}