1use crate::trie_cursor::TrieCursorIter;
22use alloy_primitives::{map::B256Map, B256};
23use itertools::{merge_join_by, EitherOrBoth};
24use reth_storage_errors::db::DatabaseError;
25use reth_trie_common::{
26 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
27 BranchNodeCompact, Nibbles,
28};
29use std::cmp::Ordering;
30
31use crate::trie_cursor::{TrieCursor, TrieCursorFactory, TrieStorageCursor};
32
33pub type ChangesetResult<T> = Result<T, DatabaseError>;
35
36pub fn compute_trie_changesets<Factory>(
51 factory: &Factory,
52 trie_updates: &TrieUpdatesSorted,
53) -> ChangesetResult<TrieUpdatesSorted>
54where
55 Factory: TrieCursorFactory,
56{
57 let account_nodes = compute_account_changesets(factory, trie_updates)?;
59
60 let mut storage_tries = B256Map::default();
62
63 let mut storage_cursor = factory.storage_trie_cursor(B256::default())?;
65
66 for (hashed_address, storage_updates) in trie_updates.storage_tries_ref() {
67 storage_cursor.set_hashed_address(*hashed_address);
68
69 let storage_changesets = if storage_updates.is_deleted() {
70 compute_wiped_storage_changesets(&mut storage_cursor, storage_updates)?
72 } else {
73 compute_storage_changesets(&mut storage_cursor, storage_updates)?
75 };
76
77 if !storage_changesets.is_empty() {
78 storage_tries.insert(
79 *hashed_address,
80 StorageTrieUpdatesSorted {
81 is_deleted: storage_updates.is_deleted(),
82 storage_nodes: storage_changesets,
83 },
84 );
85 }
86 }
87
88 Ok(TrieUpdatesSorted::new(account_nodes, storage_tries))
90}
91
92fn compute_account_changesets<Factory>(
98 factory: &Factory,
99 trie_updates: &TrieUpdatesSorted,
100) -> ChangesetResult<Vec<(Nibbles, Option<BranchNodeCompact>)>>
101where
102 Factory: TrieCursorFactory,
103{
104 let mut cursor = factory.account_trie_cursor()?;
105 let mut account_changesets = Vec::with_capacity(trie_updates.account_nodes_ref().len());
106
107 for (path, _new_node) in trie_updates.account_nodes_ref() {
110 let old_node = cursor.seek_exact(*path)?.map(|(_path, node)| node);
111 account_changesets.push((*path, old_node));
112 }
113
114 Ok(account_changesets)
115}
116
117fn compute_storage_changesets(
129 cursor: &mut impl TrieStorageCursor,
130 storage_updates: &StorageTrieUpdatesSorted,
131) -> ChangesetResult<Vec<(Nibbles, Option<BranchNodeCompact>)>> {
132 let mut storage_changesets = Vec::with_capacity(storage_updates.storage_nodes.len());
133
134 for (path, _new_node) in &storage_updates.storage_nodes {
137 let old_node = cursor.seek_exact(*path)?.map(|(_path, node)| node);
138 storage_changesets.push((*path, old_node));
139 }
140
141 Ok(storage_changesets)
142}
143
144fn compute_wiped_storage_changesets(
162 cursor: &mut impl TrieStorageCursor,
163 storage_updates: &StorageTrieUpdatesSorted,
164) -> ChangesetResult<Vec<(Nibbles, Option<BranchNodeCompact>)>> {
165 let all_nodes = TrieCursorIter::new(cursor);
168
169 let merged = storage_trie_wiped_changeset_iter(
171 storage_updates.storage_nodes.iter().map(|e| e.0),
172 all_nodes,
173 )?;
174
175 let mut storage_changesets = Vec::new();
177 for result in merged {
178 storage_changesets.push(result?);
179 }
180
181 Ok(storage_changesets)
182}
183
184pub fn storage_trie_wiped_changeset_iter(
199 changed_paths: impl Iterator<Item = Nibbles>,
200 all_nodes: impl Iterator<Item = Result<(Nibbles, BranchNodeCompact), DatabaseError>>,
201) -> Result<
202 impl Iterator<Item = Result<(Nibbles, Option<BranchNodeCompact>), DatabaseError>>,
203 DatabaseError,
204> {
205 let all_nodes = all_nodes.map(|e| e.map(|(nibbles, node)| (nibbles, Some(node))));
206
207 let merged = merge_join_by(changed_paths, all_nodes, |a, b| match (a, b) {
208 (_, Err(_)) => Ordering::Greater,
209 (a, Ok(b)) => a.cmp(&b.0),
210 });
211
212 Ok(merged.map(|either_or| match either_or {
213 EitherOrBoth::Left(changed) => {
214 Ok((changed, None))
218 }
219 EitherOrBoth::Right(wiped) => {
220 wiped
223 }
224 EitherOrBoth::Both(_changed, wiped) => {
225 debug_assert!(wiped.is_ok(), "unreachable error condition: {wiped:?}");
232 wiped
233 }
234 }))
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::trie_cursor::mock::MockTrieCursorFactory;
241 use alloy_primitives::map::B256Map;
242 use reth_trie_common::updates::StorageTrieUpdatesSorted;
243 use std::collections::BTreeMap;
244
245 #[test]
246 fn test_empty_updates() {
247 let mut storage_tries = B256Map::default();
251 storage_tries.insert(B256::default(), BTreeMap::new());
252 let factory = MockTrieCursorFactory::new(BTreeMap::new(), storage_tries);
253
254 let updates = TrieUpdatesSorted::new(vec![], B256Map::default());
256
257 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
259
260 assert!(changesets.account_nodes_ref().is_empty());
262 assert!(changesets.storage_tries_ref().is_empty());
263 }
264
265 #[test]
266 fn test_account_changesets() {
267 let path1 = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
269 let path2 = Nibbles::from_nibbles([0x4, 0x5, 0x6]);
270 let node1 = BranchNodeCompact::new(0b1111, 0b1010, 0, vec![], None);
272 let node2 = BranchNodeCompact::new(0b1111, 0b1100, 0, vec![], None);
273
274 let mut account_nodes = BTreeMap::new();
275 account_nodes.insert(path1, node1.clone());
276 account_nodes.insert(path2, node2);
277
278 let mut storage_tries = B256Map::default();
280 storage_tries.insert(B256::default(), BTreeMap::new());
281 let factory = MockTrieCursorFactory::new(account_nodes, storage_tries);
282
283 let path3 = Nibbles::from_nibbles([0x7, 0x8, 0x9]);
285 let new_node1 = BranchNodeCompact::new(0b1111, 0b0001, 0, vec![], None);
286 let new_node3 = BranchNodeCompact::new(0b1111, 0b0000, 0, vec![], None);
287
288 let updates = TrieUpdatesSorted::new(
289 vec![(path1, Some(new_node1)), (path3, Some(new_node3))],
290 B256Map::default(),
291 );
292
293 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
295
296 assert_eq!(changesets.account_nodes_ref().len(), 2);
298
299 assert_eq!(changesets.account_nodes_ref()[0].0, path1);
301 assert_eq!(changesets.account_nodes_ref()[0].1, Some(node1));
302
303 assert_eq!(changesets.account_nodes_ref()[1].0, path3);
305 assert_eq!(changesets.account_nodes_ref()[1].1, None);
306 }
307
308 #[test]
309 fn test_storage_changesets() {
310 let hashed_address = B256::from([1u8; 32]);
311
312 let path1 = Nibbles::from_nibbles([0x1, 0x2]);
314 let path2 = Nibbles::from_nibbles([0x3, 0x4]);
315 let node1 = BranchNodeCompact::new(0b1111, 0b0011, 0, vec![], None);
316 let node2 = BranchNodeCompact::new(0b1111, 0b0101, 0, vec![], None);
317
318 let mut storage_nodes = BTreeMap::new();
319 storage_nodes.insert(path1, node1.clone());
320 storage_nodes.insert(path2, node2);
321
322 let mut storage_tries = B256Map::default();
323 storage_tries.insert(B256::default(), BTreeMap::new()); storage_tries.insert(hashed_address, storage_nodes);
325
326 let factory = MockTrieCursorFactory::new(BTreeMap::new(), storage_tries);
327
328 let path3 = Nibbles::from_nibbles([0x5, 0x6]);
330 let new_node1 = BranchNodeCompact::new(0b1111, 0b1000, 0, vec![], None);
331 let new_node3 = BranchNodeCompact::new(0b1111, 0b0000, 0, vec![], None);
332
333 let mut storage_updates = B256Map::default();
334 storage_updates.insert(
335 hashed_address,
336 StorageTrieUpdatesSorted {
337 is_deleted: false,
338 storage_nodes: vec![(path1, Some(new_node1)), (path3, Some(new_node3))],
339 },
340 );
341
342 let updates = TrieUpdatesSorted::new(vec![], storage_updates);
343
344 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
346
347 assert_eq!(changesets.storage_tries_ref().len(), 1);
349 let storage_changesets = changesets.storage_tries_ref().get(&hashed_address).unwrap();
350 assert!(!storage_changesets.is_deleted);
351 assert_eq!(storage_changesets.storage_nodes.len(), 2);
352
353 assert_eq!(storage_changesets.storage_nodes[0].0, path1);
355 assert_eq!(storage_changesets.storage_nodes[0].1, Some(node1));
356
357 assert_eq!(storage_changesets.storage_nodes[1].0, path3);
359 assert_eq!(storage_changesets.storage_nodes[1].1, None);
360 }
361
362 #[test]
363 fn test_wiped_storage() {
364 let hashed_address = B256::from([2u8; 32]);
365
366 let path1 = Nibbles::from_nibbles([0x1, 0x2]);
368 let path2 = Nibbles::from_nibbles([0x3, 0x4]);
369 let path3 = Nibbles::from_nibbles([0x5, 0x6]);
370 let node1 = BranchNodeCompact::new(0b1111, 0b0011, 0, vec![], None);
371 let node2 = BranchNodeCompact::new(0b1111, 0b0101, 0, vec![], None);
372 let node3 = BranchNodeCompact::new(0b1111, 0b1001, 0, vec![], None);
373
374 let mut storage_nodes = BTreeMap::new();
375 storage_nodes.insert(path1, node1.clone());
376 storage_nodes.insert(path2, node2.clone());
377 storage_nodes.insert(path3, node3.clone());
378
379 let mut storage_tries = B256Map::default();
380 storage_tries.insert(B256::default(), BTreeMap::new()); storage_tries.insert(hashed_address, storage_nodes);
382
383 let factory = MockTrieCursorFactory::new(BTreeMap::new(), storage_tries);
384
385 let new_node1 = BranchNodeCompact::new(0b1111, 0b1000, 0, vec![], None);
387
388 let mut storage_updates = B256Map::default();
389 storage_updates.insert(
390 hashed_address,
391 StorageTrieUpdatesSorted {
392 is_deleted: true,
393 storage_nodes: vec![(path1, Some(new_node1))],
394 },
395 );
396
397 let updates = TrieUpdatesSorted::new(vec![], storage_updates);
398
399 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
401
402 assert_eq!(changesets.storage_tries_ref().len(), 1);
404 let storage_changesets = changesets.storage_tries_ref().get(&hashed_address).unwrap();
405 assert!(storage_changesets.is_deleted);
406
407 assert_eq!(storage_changesets.storage_nodes.len(), 3);
409
410 assert_eq!(storage_changesets.storage_nodes[0].0, path1);
412 assert_eq!(storage_changesets.storage_nodes[1].0, path2);
413 assert_eq!(storage_changesets.storage_nodes[2].0, path3);
414
415 assert_eq!(storage_changesets.storage_nodes[0].1, Some(node1));
417 assert_eq!(storage_changesets.storage_nodes[1].1, Some(node2));
418 assert_eq!(storage_changesets.storage_nodes[2].1, Some(node3));
419 }
420
421 #[test]
422 fn test_wiped_storage_with_new_path() {
423 let hashed_address = B256::from([3u8; 32]);
424
425 let path1 = Nibbles::from_nibbles([0x1, 0x2]);
427 let path3 = Nibbles::from_nibbles([0x5, 0x6]);
428 let node1 = BranchNodeCompact::new(0b1111, 0b0011, 0, vec![], None);
429 let node3 = BranchNodeCompact::new(0b1111, 0b1001, 0, vec![], None);
430
431 let mut storage_nodes = BTreeMap::new();
432 storage_nodes.insert(path1, node1.clone());
433 storage_nodes.insert(path3, node3.clone());
434
435 let mut storage_tries = B256Map::default();
436 storage_tries.insert(B256::default(), BTreeMap::new()); storage_tries.insert(hashed_address, storage_nodes);
438
439 let factory = MockTrieCursorFactory::new(BTreeMap::new(), storage_tries);
440
441 let path2 = Nibbles::from_nibbles([0x3, 0x4]);
443 let new_node2 = BranchNodeCompact::new(0b1111, 0b0101, 0, vec![], None);
444
445 let mut storage_updates = B256Map::default();
446 storage_updates.insert(
447 hashed_address,
448 StorageTrieUpdatesSorted {
449 is_deleted: true,
450 storage_nodes: vec![(path2, Some(new_node2))],
451 },
452 );
453
454 let updates = TrieUpdatesSorted::new(vec![], storage_updates);
455
456 let changesets = compute_trie_changesets(&factory, &updates).unwrap();
458
459 let storage_changesets = changesets.storage_tries_ref().get(&hashed_address).unwrap();
461 assert!(storage_changesets.is_deleted);
462
463 assert_eq!(storage_changesets.storage_nodes.len(), 3);
465
466 assert_eq!(storage_changesets.storage_nodes[0].0, path1);
468 assert_eq!(storage_changesets.storage_nodes[1].0, path2);
469 assert_eq!(storage_changesets.storage_nodes[2].0, path3);
470
471 assert_eq!(storage_changesets.storage_nodes[0].1, Some(node1));
473 assert_eq!(storage_changesets.storage_nodes[1].1, None);
474 assert_eq!(storage_changesets.storage_nodes[2].1, Some(node3));
475 }
476}