1use crate::{
2 hashed_cursor::HashedCursorFactory, prefix_set::TriePrefixSetsMut, proof::Proof, proof_v2,
3 trie_cursor::TrieCursorFactory, TRIE_ACCOUNT_RLP_MAX_SIZE,
4};
5use alloy_primitives::{
6 keccak256,
7 map::{B256Map, HashMap},
8 Bytes, B256,
9};
10use alloy_rlp::{Encodable, EMPTY_STRING_CODE};
11use alloy_trie::{nodes::BranchNodeRef, EMPTY_ROOT_HASH};
12use reth_execution_errors::{SparseStateTrieErrorKind, StateProofError, TrieWitnessError};
13use reth_trie_common::{
14 DecodedMultiProofV2, ExecutionWitnessMode, HashedPostState, MultiProofTargetsV2, ProofV2Target,
15 TrieNodeV2,
16};
17use reth_trie_sparse::{LeafUpdate, SparseStateTrie, SparseTrie as _, TrieNodeEpoch};
18
19#[derive(Debug)]
21pub struct TrieWitness<T, H> {
22 trie_cursor_factory: T,
24 hashed_cursor_factory: H,
26 prefix_sets: TriePrefixSetsMut,
28 always_include_root_node: bool,
33 mode: ExecutionWitnessMode,
35 witness: B256Map<Bytes>,
37}
38
39impl<T, H> TrieWitness<T, H> {
40 pub fn new(trie_cursor_factory: T, hashed_cursor_factory: H) -> Self {
42 Self {
43 trie_cursor_factory,
44 hashed_cursor_factory,
45 prefix_sets: TriePrefixSetsMut::default(),
46 always_include_root_node: false,
47 mode: ExecutionWitnessMode::Legacy,
48 witness: HashMap::default(),
49 }
50 }
51
52 pub fn with_trie_cursor_factory<TF>(self, trie_cursor_factory: TF) -> TrieWitness<TF, H> {
54 TrieWitness {
55 trie_cursor_factory,
56 hashed_cursor_factory: self.hashed_cursor_factory,
57 prefix_sets: self.prefix_sets,
58 always_include_root_node: self.always_include_root_node,
59 mode: self.mode,
60 witness: self.witness,
61 }
62 }
63
64 pub fn with_hashed_cursor_factory<HF>(self, hashed_cursor_factory: HF) -> TrieWitness<T, HF> {
66 TrieWitness {
67 trie_cursor_factory: self.trie_cursor_factory,
68 hashed_cursor_factory,
69 prefix_sets: self.prefix_sets,
70 always_include_root_node: self.always_include_root_node,
71 mode: self.mode,
72 witness: self.witness,
73 }
74 }
75
76 pub fn with_prefix_sets_mut(mut self, prefix_sets: TriePrefixSetsMut) -> Self {
78 self.prefix_sets = prefix_sets;
79 self
80 }
81
82 pub const fn always_include_root_node(mut self) -> Self {
86 self.always_include_root_node = true;
87 self
88 }
89
90 pub const fn with_execution_witness_mode(mut self, mode: ExecutionWitnessMode) -> Self {
92 self.mode = mode;
93 self
94 }
95}
96
97impl<T, H> TrieWitness<T, H>
98where
99 T: TrieCursorFactory + Clone,
100 H: HashedCursorFactory + Clone,
101{
102 pub fn compute(mut self, state: HashedPostState) -> Result<B256Map<Bytes>, TrieWitnessError> {
109 let is_state_empty = state.is_empty();
110 if is_state_empty && !self.always_include_root_node {
111 return Ok(Default::default())
112 }
113
114 let proof_targets = if is_state_empty {
115 MultiProofTargetsV2 {
116 account_targets: vec![ProofV2Target::new(B256::ZERO)],
117 ..Default::default()
118 }
119 } else {
120 Self::get_proof_targets(&state)
121 };
122 let multiproof =
123 Proof::new(self.trie_cursor_factory.clone(), self.hashed_cursor_factory.clone())
124 .with_prefix_sets_mut(self.prefix_sets.clone())
125 .multiproof_v2(proof_targets)?;
126
127 if is_state_empty {
130 let (root_hash, root_node) = if let Some(root_node) =
131 multiproof.account_proofs.into_iter().find(|n| n.path.is_empty())
132 {
133 let mut encoded = Vec::new();
134 root_node.node.encode(&mut encoded);
135 let bytes = Bytes::from(encoded);
136 (keccak256(&bytes), bytes)
137 } else {
138 (EMPTY_ROOT_HASH, Bytes::from([EMPTY_STRING_CODE]))
139 };
140 return Ok(B256Map::from_iter([(root_hash, root_node)]))
141 }
142
143 self.record_multiproof_nodes(&multiproof);
145
146 let mut sparse_trie = SparseStateTrie::new();
147 sparse_trie.reveal_decoded_multiproof_v2(multiproof)?;
148
149 let mut storage_removals: B256Map<B256Map<LeafUpdate>> = B256Map::default();
156 let mut storage_upserts: B256Map<B256Map<LeafUpdate>> = B256Map::default();
157 for (hashed_address, storage) in &state.storages {
158 for (&hashed_slot, value) in &storage.storage {
159 if value.is_zero() {
160 storage_removals
161 .entry(*hashed_address)
162 .or_default()
163 .insert(hashed_slot, LeafUpdate::Changed(vec![]));
164 } else {
165 storage_upserts.entry(*hashed_address).or_default().insert(
166 hashed_slot,
167 LeafUpdate::Changed(alloy_rlp::encode_fixed_size(value).to_vec()),
168 );
169 }
170 }
171 }
172
173 let storage_update_sets = if self.mode.is_canonical() {
174 [&mut storage_upserts, &mut storage_removals]
175 } else {
176 [&mut storage_removals, &mut storage_upserts]
177 };
178
179 for storage_updates in storage_update_sets {
181 loop {
182 let mut targets = MultiProofTargetsV2::default();
183
184 for (&hashed_address, slot_updates) in storage_updates.iter_mut() {
185 if slot_updates.is_empty() {
186 continue;
187 }
188 let storage_trie = sparse_trie
189 .storage_trie_mut(&hashed_address)
190 .expect("storage trie was revealed from multiproof");
191 storage_trie
192 .update_leaves(slot_updates, |key, parent| {
193 targets
194 .storage_targets
195 .entry(hashed_address)
196 .or_default()
197 .push(ProofV2Target::new(key).with_parent(parent));
198 })
199 .map_err(|err| {
200 SparseStateTrieErrorKind::SparseStorageTrie(
201 hashed_address,
202 err.into_kind(),
203 )
204 })?;
205 }
206
207 if targets.is_empty() {
208 break;
209 }
210
211 let multiproof = Proof::new(
212 self.trie_cursor_factory.clone(),
213 self.hashed_cursor_factory.clone(),
214 )
215 .with_prefix_sets_mut(self.prefix_sets.clone())
216 .multiproof_v2(targets)?;
217 self.record_multiproof_nodes(&multiproof);
218 sparse_trie.reveal_decoded_multiproof_v2(multiproof)?;
219 }
220 }
221
222 let mut account_removals: B256Map<LeafUpdate> = B256Map::default();
227 let mut account_upserts: B256Map<LeafUpdate> = B256Map::default();
228 for &hashed_address in state.accounts.keys().chain(state.storages.keys()) {
229 if account_removals.contains_key(&hashed_address) ||
230 account_upserts.contains_key(&hashed_address)
231 {
232 continue;
233 }
234
235 let account = state
236 .accounts
237 .get(&hashed_address)
238 .ok_or(TrieWitnessError::MissingAccount(hashed_address))?
239 .unwrap_or_default();
240
241 let storage_root =
242 if let Some(storage_trie) = sparse_trie.storage_trie_mut(&hashed_address) {
243 storage_trie.root(TrieNodeEpoch::UNMODIFIED)
244 } else {
245 let record_root_node = !self.mode.is_canonical() ||
246 state
247 .storages
248 .get(&hashed_address)
249 .is_some_and(|storage| !storage.storage.is_empty());
250 self.account_storage_root(hashed_address, record_root_node)?
251 };
252
253 if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
254 account_removals.insert(hashed_address, LeafUpdate::Changed(vec![]));
255 } else {
256 let mut rlp = Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE);
257 account.into_trie_account(storage_root).encode(&mut rlp);
258 account_upserts.insert(hashed_address, LeafUpdate::Changed(rlp));
259 }
260 }
261
262 let account_update_sets = if self.mode.is_canonical() {
263 [&mut account_upserts, &mut account_removals]
264 } else {
265 [&mut account_removals, &mut account_upserts]
266 };
267
268 for account_updates in account_update_sets {
270 loop {
271 let mut targets = MultiProofTargetsV2::default();
272
273 sparse_trie
274 .trie_mut()
275 .update_leaves(account_updates, |key, parent| {
276 targets.account_targets.push(ProofV2Target::new(key).with_parent(parent));
277 })
278 .map_err(SparseStateTrieErrorKind::from)?;
279
280 if targets.is_empty() {
281 break;
282 }
283
284 let multiproof = Proof::new(
285 self.trie_cursor_factory.clone(),
286 self.hashed_cursor_factory.clone(),
287 )
288 .with_prefix_sets_mut(self.prefix_sets.clone())
289 .multiproof_v2(targets)?;
290 self.record_multiproof_nodes(&multiproof);
291 sparse_trie.reveal_decoded_multiproof_v2(multiproof)?;
292 }
293 }
294
295 if self.mode.is_canonical() {
296 self.witness.retain(|_, value| value.as_ref() != [EMPTY_STRING_CODE]);
299 }
300
301 Ok(self.witness)
302 }
303
304 fn record_multiproof_nodes(&mut self, multiproof: &DecodedMultiProofV2) {
306 let mut encoded = Vec::new();
307 for proof_node in &multiproof.account_proofs {
308 self.record_witness_node(&proof_node.node, &mut encoded);
309 }
310 for proof_nodes in multiproof.storage_proofs.values() {
311 for proof_node in proof_nodes {
312 self.record_witness_node(&proof_node.node, &mut encoded);
313 }
314 }
315 }
316
317 fn record_witness_node(&mut self, node: &TrieNodeV2, encoded: &mut Vec<u8>) {
319 encoded.clear();
320 node.encode(encoded);
321 let hash = keccak256(encoded.as_slice());
322 self.witness.entry(hash).or_insert_with(|| Bytes::copy_from_slice(encoded));
323
324 if let TrieNodeV2::Branch(branch) = node &&
325 !branch.key.is_empty()
326 {
327 encoded.clear();
328 BranchNodeRef::new(&branch.stack, branch.state_mask).encode(encoded);
329 let hash = keccak256(encoded.as_slice());
330 self.witness.entry(hash).or_insert_with(|| Bytes::copy_from_slice(encoded));
331 }
332 }
333
334 fn account_storage_root(
337 &mut self,
338 hashed_address: B256,
339 record_root_node: bool,
340 ) -> Result<B256, TrieWitnessError> {
341 let storage_trie_cursor = self
342 .trie_cursor_factory
343 .storage_trie_cursor(hashed_address)
344 .map_err(StateProofError::from)?;
345 let hashed_storage_cursor = self
346 .hashed_cursor_factory
347 .hashed_storage_cursor(hashed_address)
348 .map_err(StateProofError::from)?;
349 let mut calculator = proof_v2::StorageProofCalculator::new_storage(
350 storage_trie_cursor,
351 hashed_storage_cursor,
352 );
353 if let Some(prefix_set) = self.prefix_sets.storage_prefix_sets.get(&hashed_address) {
354 calculator = calculator.with_prefix_set(prefix_set.clone().freeze());
355 }
356 let root_node = calculator.storage_root_node(hashed_address)?;
357 let root_hash = calculator
358 .compute_root_hash(core::slice::from_ref(&root_node))?
359 .unwrap_or(EMPTY_ROOT_HASH);
360 drop(calculator);
361 if record_root_node {
362 let mut encoded = Vec::new();
363 self.record_witness_node(&root_node.node, &mut encoded);
364 }
365 Ok(root_hash)
366 }
367
368 fn get_proof_targets(state: &HashedPostState) -> MultiProofTargetsV2 {
371 let mut targets = MultiProofTargetsV2::default();
372 for &hashed_address in state.accounts.keys() {
373 targets.account_targets.push(ProofV2Target::new(hashed_address));
374 }
375 for (&hashed_address, storage) in &state.storages {
376 if !state.accounts.contains_key(&hashed_address) {
377 targets.account_targets.push(ProofV2Target::new(hashed_address));
378 }
379 if storage.storage.is_empty() {
382 continue;
383 }
384 let storage_keys = storage.storage.keys().map(|k| ProofV2Target::new(*k)).collect();
385 targets.storage_targets.insert(hashed_address, storage_keys);
386 }
387 targets
388 }
389}