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