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 =
123 HashedStorage::from_iter(false, changeset.iter().map(|(&k, &v)| (k, v)));
124 let overlay = HashedPostStateSorted::new(
125 Vec::new(),
126 once((self.hashed_address(), hashed_storage.into_sorted())).collect(),
127 );
128 let overlay_cursor_factory =
129 HashedPostStateCursorFactory::new(self.hashed_cursor_factory.clone(), &overlay);
130
131 let (root, _, updates) = StorageRoot::new_hashed(
132 self.trie_cursor_factory.clone(),
133 overlay_cursor_factory,
134 self.hashed_address(),
135 prefix_set.freeze(),
136 #[cfg(feature = "metrics")]
137 crate::metrics::TrieRootMetrics::new(crate::TrieType::Storage),
138 )
139 .root_with_updates()
140 .expect("StorageRoot should succeed");
141
142 (root, updates)
143 }
144
145 pub fn apply_changeset(&mut self, changeset: BTreeMap<B256, U256>) {
148 for (k, v) in changeset {
149 if v == U256::ZERO {
150 self.storage.remove(&k);
151 } else {
152 self.storage.insert(k, v);
153 }
154 }
155 self.rebuild();
156 }
157
158 fn rebuild(&mut self) {
160 self.hashed_cursor_factory = MockHashedCursorFactory::new(
161 BTreeMap::new(),
162 once((self.hashed_address(), self.storage.clone())).collect(),
163 );
164
165 let (root, _, updates) = StorageRoot::new_hashed(
166 MockTrieCursorFactory::new(
167 BTreeMap::new(),
168 once((self.hashed_address(), BTreeMap::new())).collect(),
169 ),
170 self.hashed_cursor_factory.clone(),
171 self.hashed_address(),
172 crate::prefix_set::PrefixSet::default(),
173 #[cfg(feature = "metrics")]
174 crate::metrics::TrieRootMetrics::new(crate::TrieType::Storage),
175 )
176 .root_with_updates()
177 .expect("StorageRoot should succeed");
178
179 self.trie_cursor_factory = MockTrieCursorFactory::new(
180 BTreeMap::new(),
181 once((
182 self.hashed_address(),
183 updates.storage_nodes.iter().map(|(k, v)| (*k, v.clone())).collect(),
184 ))
185 .collect(),
186 );
187
188 self.original_root = root;
189 self.storage_trie_updates = updates;
190 }
191
192 pub const fn hashed_address(&self) -> B256 {
194 B256::ZERO
195 }
196
197 pub const fn storage(&self) -> &BTreeMap<B256, U256> {
199 &self.storage
200 }
201
202 pub const fn original_root(&self) -> B256 {
204 self.original_root
205 }
206
207 pub const fn storage_trie_updates(&self) -> &StorageTrieUpdates {
209 &self.storage_trie_updates
210 }
211
212 pub fn set_trie_nodes(&mut self, trie_nodes: BTreeMap<Nibbles, BranchNodeCompact>) {
214 self.trie_cursor_factory = MockTrieCursorFactory::new(
215 BTreeMap::new(),
216 once((self.hashed_address(), trie_nodes)).collect(),
217 );
218 }
219
220 pub fn trie_cursor_factory(&self) -> MockTrieCursorFactory {
222 self.trie_cursor_factory.clone()
223 }
224
225 pub fn hashed_cursor_factory(&self) -> MockHashedCursorFactory {
227 self.hashed_cursor_factory.clone()
228 }
229
230 pub fn root_node(&self) -> ProofTrieNodeV2 {
232 let trie_cursor = self
233 .trie_cursor_factory
234 .storage_trie_cursor(self.hashed_address())
235 .expect("storage trie cursor should succeed");
236 let hashed_cursor = self
237 .hashed_cursor_factory
238 .hashed_storage_cursor(self.hashed_address())
239 .expect("hashed storage cursor should succeed");
240
241 let mut proof_calculator = StorageProofCalculator::new_storage(trie_cursor, hashed_cursor);
242 proof_calculator
243 .storage_root_node(self.hashed_address())
244 .expect("storage_root_node should succeed")
245 }
246
247 pub fn proof_v2(&self, targets: &mut [ProofV2Target]) -> (Vec<ProofTrieNodeV2>, Option<B256>) {
252 let trie_cursor = self
253 .trie_cursor_factory
254 .storage_trie_cursor(self.hashed_address())
255 .expect("storage trie cursor should succeed");
256 let hashed_cursor = self
257 .hashed_cursor_factory
258 .hashed_storage_cursor(self.hashed_address())
259 .expect("hashed storage cursor should succeed");
260
261 let mut proof_calculator = StorageProofCalculator::new_storage(trie_cursor, hashed_cursor);
262 let proofs = proof_calculator
263 .storage_proof(self.hashed_address(), targets)
264 .expect("proof_v2 should succeed");
265 let root_hash =
266 proof_calculator.compute_root_hash(&proofs).expect("compute_root_hash should succeed");
267 (proofs, root_hash)
268 }
269
270 pub fn minimize_trie_updates(&self, updates: &mut StorageTrieUpdates) {
277 if updates.is_deleted == self.storage_trie_updates.is_deleted {
278 updates.is_deleted = false;
279 }
280
281 let paths_with_updates: HashSet<Nibbles> = updates.storage_nodes.keys().copied().collect();
286
287 updates
288 .storage_nodes
289 .retain(|path, node| self.storage_trie_updates.storage_nodes.get(path) != Some(node));
290
291 updates.removed_nodes.retain(|path| {
292 self.storage_trie_updates.storage_nodes.contains_key(path) &&
293 !paths_with_updates.contains(path)
294 });
295 }
296}