1use alloy_primitives::{Address, B256, U256};
2use alloy_rlp::encode_fixed_size;
3use reth_primitives_traits::Account;
4use reth_trie_common::triehash::KeccakHasher;
5
6pub use triehash;
8
9pub fn state_root<I, S>(accounts: I) -> B256
11where
12 I: IntoIterator<Item = (Address, (Account, S))>,
13 S: IntoIterator<Item = (B256, U256)>,
14{
15 let encoded_accounts = accounts.into_iter().map(|(address, (account, storage))| {
16 let storage_root = storage_root(storage);
17 let account = account.into_trie_account(storage_root);
18 (address, alloy_rlp::encode(account))
19 });
20 triehash::sec_trie_root::<KeccakHasher, _, _, _>(encoded_accounts)
21}
22
23pub fn storage_root<I: IntoIterator<Item = (B256, U256)>>(storage: I) -> B256 {
25 let encoded_storage = storage.into_iter().map(|(k, v)| (k, encode_fixed_size(&v)));
26 triehash::sec_trie_root::<KeccakHasher, _, _, _>(encoded_storage)
27}
28
29pub fn state_root_prehashed<I, S>(accounts: I) -> B256
32where
33 I: IntoIterator<Item = (B256, (Account, S))>,
34 S: IntoIterator<Item = (B256, U256)>,
35{
36 let encoded_accounts = accounts.into_iter().map(|(address, (account, storage))| {
37 let storage_root = storage_root_prehashed(storage);
38 let account = account.into_trie_account(storage_root);
39 (address, alloy_rlp::encode(account))
40 });
41
42 triehash::trie_root::<KeccakHasher, _, _, _>(encoded_accounts)
43}
44
45pub fn storage_root_prehashed<I: IntoIterator<Item = (B256, U256)>>(storage: I) -> B256 {
47 let encoded_storage = storage.into_iter().map(|(k, v)| (k, encode_fixed_size(&v)));
48 triehash::trie_root::<KeccakHasher, _, _, _>(encoded_storage)
49}
50
51use crate::{
56 hashed_cursor::{
57 mock::MockHashedCursorFactory, HashedCursorFactory, HashedPostStateCursorFactory,
58 },
59 proof_v2::StorageProofCalculator,
60 trie_cursor::{mock::MockTrieCursorFactory, TrieCursorFactory},
61 StorageRoot,
62};
63use alloy_primitives::map::HashSet;
64use reth_trie_common::{
65 prefix_set::PrefixSetMut, updates::StorageTrieUpdates, BranchNodeCompact,
66 HashedPostStateSorted, HashedStorage, Nibbles, ProofTrieNodeV2, ProofV2Target,
67};
68use std::{collections::BTreeMap, iter::once};
69
70#[derive(Debug)]
75pub struct TrieTestHarness {
76 storage: BTreeMap<B256, U256>,
78 original_root: B256,
80 storage_trie_updates: StorageTrieUpdates,
82 trie_cursor_factory: MockTrieCursorFactory,
84 hashed_cursor_factory: MockHashedCursorFactory,
86}
87
88impl TrieTestHarness {
89 pub fn new(storage: BTreeMap<B256, U256>) -> Self {
91 let mut harness = Self {
92 storage,
93 original_root: B256::ZERO,
94 storage_trie_updates: StorageTrieUpdates::default(),
95 trie_cursor_factory: MockTrieCursorFactory::new(
96 BTreeMap::new(),
97 once((B256::ZERO, BTreeMap::new())).collect(),
98 ),
99 hashed_cursor_factory: MockHashedCursorFactory::new(
100 BTreeMap::new(),
101 once((B256::ZERO, BTreeMap::new())).collect(),
102 ),
103 };
104 harness.rebuild();
105 harness
106 }
107
108 pub fn get_root_with_updates(
114 &self,
115 changeset: &BTreeMap<B256, U256>,
116 ) -> (B256, StorageTrieUpdates) {
117 let mut prefix_set = PrefixSetMut::with_capacity(changeset.len());
118 for hashed_slot in changeset.keys() {
119 prefix_set.insert(Nibbles::unpack(hashed_slot));
120 }
121
122 let hashed_storage = HashedStorage::from_iter(changeset.iter().map(|(&k, &v)| (k, v)));
123 let overlay = HashedPostStateSorted::new(
124 Vec::new(),
125 once((self.hashed_address(), hashed_storage.into_sorted())).collect(),
126 );
127 let overlay_cursor_factory =
128 HashedPostStateCursorFactory::new(self.hashed_cursor_factory.clone(), &overlay);
129
130 let (root, _, updates) = StorageRoot::new_hashed(
131 self.trie_cursor_factory.clone(),
132 overlay_cursor_factory,
133 self.hashed_address(),
134 prefix_set.freeze(),
135 #[cfg(feature = "metrics")]
136 crate::metrics::TrieRootMetrics::new(crate::TrieType::Storage),
137 )
138 .root_with_updates()
139 .expect("StorageRoot should succeed");
140
141 (root, updates)
142 }
143
144 pub fn apply_changeset(&mut self, changeset: BTreeMap<B256, U256>) {
147 for (k, v) in changeset {
148 if v == U256::ZERO {
149 self.storage.remove(&k);
150 } else {
151 self.storage.insert(k, v);
152 }
153 }
154 self.rebuild();
155 }
156
157 fn rebuild(&mut self) {
159 self.hashed_cursor_factory = MockHashedCursorFactory::new(
160 BTreeMap::new(),
161 once((self.hashed_address(), self.storage.clone())).collect(),
162 );
163
164 let (root, _, updates) = StorageRoot::new_hashed(
165 MockTrieCursorFactory::new(
166 BTreeMap::new(),
167 once((self.hashed_address(), BTreeMap::new())).collect(),
168 ),
169 self.hashed_cursor_factory.clone(),
170 self.hashed_address(),
171 crate::prefix_set::PrefixSet::default(),
172 #[cfg(feature = "metrics")]
173 crate::metrics::TrieRootMetrics::new(crate::TrieType::Storage),
174 )
175 .root_with_updates()
176 .expect("StorageRoot should succeed");
177
178 self.trie_cursor_factory = MockTrieCursorFactory::new(
179 BTreeMap::new(),
180 once((
181 self.hashed_address(),
182 updates.storage_nodes.iter().map(|(k, v)| (*k, v.clone())).collect(),
183 ))
184 .collect(),
185 );
186
187 self.original_root = root;
188 self.storage_trie_updates = updates;
189 }
190
191 pub const fn hashed_address(&self) -> B256 {
193 B256::ZERO
194 }
195
196 pub const fn storage(&self) -> &BTreeMap<B256, U256> {
198 &self.storage
199 }
200
201 pub const fn original_root(&self) -> B256 {
203 self.original_root
204 }
205
206 pub const fn storage_trie_updates(&self) -> &StorageTrieUpdates {
208 &self.storage_trie_updates
209 }
210
211 pub fn set_trie_nodes(&mut self, trie_nodes: BTreeMap<Nibbles, BranchNodeCompact>) {
213 self.trie_cursor_factory = MockTrieCursorFactory::new(
214 BTreeMap::new(),
215 once((self.hashed_address(), trie_nodes)).collect(),
216 );
217 }
218
219 pub fn trie_cursor_factory(&self) -> MockTrieCursorFactory {
221 self.trie_cursor_factory.clone()
222 }
223
224 pub fn hashed_cursor_factory(&self) -> MockHashedCursorFactory {
226 self.hashed_cursor_factory.clone()
227 }
228
229 pub fn root_node(&self) -> ProofTrieNodeV2 {
231 let trie_cursor = self
232 .trie_cursor_factory
233 .storage_trie_cursor(self.hashed_address())
234 .expect("storage trie cursor should succeed");
235 let hashed_cursor = self
236 .hashed_cursor_factory
237 .hashed_storage_cursor(self.hashed_address())
238 .expect("hashed storage cursor should succeed");
239
240 let mut proof_calculator = StorageProofCalculator::new_storage(trie_cursor, hashed_cursor);
241 proof_calculator
242 .storage_root_node(self.hashed_address())
243 .expect("storage_root_node should succeed")
244 }
245
246 pub fn proof_v2(&self, targets: &mut [ProofV2Target]) -> (Vec<ProofTrieNodeV2>, Option<B256>) {
251 let trie_cursor = self
252 .trie_cursor_factory
253 .storage_trie_cursor(self.hashed_address())
254 .expect("storage trie cursor should succeed");
255 let hashed_cursor = self
256 .hashed_cursor_factory
257 .hashed_storage_cursor(self.hashed_address())
258 .expect("hashed storage cursor should succeed");
259
260 let mut proof_calculator = StorageProofCalculator::new_storage(trie_cursor, hashed_cursor);
261 let proofs = proof_calculator
262 .storage_proof(self.hashed_address(), targets)
263 .expect("proof_v2 should succeed");
264 let root_hash =
265 proof_calculator.compute_root_hash(&proofs).expect("compute_root_hash should succeed");
266 (proofs, root_hash)
267 }
268
269 pub fn minimize_trie_updates(&self, updates: &mut StorageTrieUpdates) {
276 if updates.is_deleted == self.storage_trie_updates.is_deleted {
277 updates.is_deleted = false;
278 }
279
280 let paths_with_updates: HashSet<Nibbles> = updates.storage_nodes.keys().copied().collect();
285
286 updates
287 .storage_nodes
288 .retain(|path, node| self.storage_trie_updates.storage_nodes.get(path) != Some(node));
289
290 updates.removed_nodes.retain(|path| {
291 self.storage_trie_updates.storage_nodes.contains_key(path) &&
292 !paths_with_updates.contains(path)
293 });
294 }
295}