1mod branch_child_idx;
2mod cursor;
3mod node_arena;
4mod nodes;
5
6use branch_child_idx::{BranchChildIdx, BranchChildIter};
7use cursor::{branch_idx, ArenaCursor, NextResult, SeekResult};
8use node_arena::{BranchChild, Index, NodeArena};
9use nodes::{ArenaSparseNode, ArenaSparseNodeBranch, ArenaSparseNodeState};
10
11use crate::{
12 LeafLookup, LeafLookupError, LeafUpdate, SparseTrie, SparseTrieUpdates, TrieNodeEpoch,
13};
14use alloc::{borrow::Cow, boxed::Box, vec::Vec};
15use alloy_primitives::{keccak256, map::B256Map, B256};
16use alloy_trie::TrieMask;
17use core::{cmp::Reverse, mem};
18use rayon::iter::{IntoParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
19use reth_execution_errors::{SparseTrieErrorKind, SparseTrieResult};
20use reth_trie_common::{
21 BranchNodeMasks, BranchNodeRef, ExtensionNodeRef, LeafNodeRef, Nibbles, ProofTrieNodeV2,
22 ProofV2TargetParent, RlpNode, TrieNodeV2, EMPTY_ROOT_HASH,
23};
24use smallvec::SmallVec;
25use tracing::{instrument, trace};
26
27const TRACE_TARGET: &str = "trie::arena";
28
29const UPPER_TRIE_MAX_DEPTH: usize = 2;
32
33fn branch_data(arena: &NodeArena, idx: Index) -> &ArenaSparseNodeBranch {
35 arena[branch_idx(arena, idx).expect("node must be a branch or extension")].branch_ref()
36}
37
38fn branch_data_mut(arena: &mut NodeArena, idx: Index) -> &mut ArenaSparseNodeBranch {
40 let idx = branch_idx(arena, idx).expect("node must be a branch or extension");
41 arena[idx].branch_mut()
42}
43
44fn node_state_ref(arena: &NodeArena, idx: Index) -> Option<&ArenaSparseNodeState> {
46 match &arena[idx] {
47 ArenaSparseNode::Extension { child, .. } => node_state_ref(arena, *child),
48 ArenaSparseNode::Subtrie(subtrie) => node_state_ref(&subtrie.arena, subtrie.root),
49 node => node.state_ref(),
50 }
51}
52
53fn node_state_mut(arena: &mut NodeArena, idx: Index) -> &mut ArenaSparseNodeState {
55 let idx = arena[idx].extension_child().unwrap_or(idx);
56 arena[idx].state_mut()
57}
58
59fn node_tree_mask_bit(arena: &NodeArena, idx: Index) -> bool {
61 match &arena[idx] {
62 ArenaSparseNode::Branch(branch) => !branch.branch_masks.is_empty(),
63 ArenaSparseNode::Extension { child, .. } => {
64 !arena[*child].branch_ref().branch_masks.is_empty()
65 }
66 ArenaSparseNode::Subtrie(subtrie) => node_tree_mask_bit(&subtrie.arena, subtrie.root),
67 _ => false,
68 }
69}
70
71fn move_node(dst: &mut NodeArena, src: &mut NodeArena, idx: Index) -> Index {
73 let node = src.drain_node(idx);
74 let new_idx = dst.insert(node);
75 if let Some(child_idx) = dst[new_idx].extension_child() {
76 let child = src.drain_node(child_idx);
77 let new_child_idx = dst.insert(child);
78 let ArenaSparseNode::Extension { child, .. } = &mut dst[new_idx] else { unreachable!() };
79 *child = new_child_idx;
80 }
81 new_idx
82}
83
84fn compact_arena(arena: &mut NodeArena, root: &mut Index) {
88 let mut new_arena = NodeArena::with_capacity(arena.len());
89 let mut marks = new_arena.adopt_blinded(arena);
90 let root_node = arena.drain_node(*root);
91 let new_root = new_arena.insert(root_node);
92
93 let mut next = 0;
96 while next < new_arena.len() {
97 let new_idx = Index::new(next);
98 next += 1;
99 if let ArenaSparseNode::Extension { child, .. } = new_arena[new_idx] {
100 let child_node = arena.drain_node(child);
101 let new_child_idx = new_arena.insert(child_node);
102 let ArenaSparseNode::Extension { child, .. } = &mut new_arena[new_idx] else {
103 unreachable!()
104 };
105 *child = new_child_idx;
106 continue;
107 }
108
109 let old_children: SmallVec<[(usize, Index); 16]> = match &new_arena[new_idx] {
110 ArenaSparseNode::Branch(b) => b
111 .children
112 .iter()
113 .enumerate()
114 .filter_map(|(pos, child)| match child.revealed_index() {
115 Some(old_child_idx) => Some((pos, old_child_idx)),
116 None => {
118 marks.mark(*child);
119 None
120 }
121 })
122 .collect(),
123 _ => continue,
124 };
125
126 for (child_pos, old_child_idx) in old_children {
127 let child_node = arena.drain_node(old_child_idx);
128 let new_child_idx = new_arena.insert(child_node);
129 debug_assert_eq!(
130 new_child_idx.get() + 1,
131 new_arena.len(),
132 "compaction must append, the destination doubles as the BFS queue",
133 );
134 new_arena[new_idx].branch_mut().children[child_pos] =
135 BranchChild::revealed(new_child_idx);
136 }
137 }
138
139 debug_assert!(
140 arena.iter().next().is_none(),
141 "compact_arena: {} orphaned nodes remaining after BFS drain",
142 arena.iter().count(),
143 );
144
145 new_arena.sweep_blinded(marks);
146 *arena = new_arena;
147 *root = new_root;
148}
149
150#[derive(Debug, Default, Clone)]
153struct ArenaTrieBuffers {
154 cursor: ArenaCursor,
156 updates: Option<SparseTrieUpdates>,
159 rlp_buf: Vec<u8>,
161 rlp_node_buf: Vec<RlpNode>,
163}
164
165impl ArenaTrieBuffers {
166 fn clear(&mut self) {
167 if let Some(updates) = self.updates.as_mut() {
168 updates.clear();
169 }
170 self.rlp_buf.clear();
171 self.rlp_node_buf.clear();
172 }
173}
174
175#[derive(Debug, Clone)]
179struct ArenaSparseSubtrie {
180 arena: NodeArena,
182 root: Index,
184 path: Nibbles,
186 buffers: ArenaTrieBuffers,
188 required_proofs: Vec<(usize, ArenaRequiredProof)>,
192 num_leaves: u64,
194 num_dirty_leaves: u64,
196}
197
198impl ArenaSparseSubtrie {
199 fn new(record_updates: bool) -> Box<Self> {
203 let mut arena = NodeArena::new();
204 let root =
205 arena.insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
206 let buffers = ArenaTrieBuffers {
207 updates: record_updates.then(SparseTrieUpdates::default),
208 ..Default::default()
209 };
210 Box::new(Self {
211 arena,
212 root,
213 path: Nibbles::default(),
214 buffers,
215 required_proofs: Vec::new(),
216 num_leaves: 0,
217 num_dirty_leaves: 0,
218 })
219 }
220
221 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
223 fn debug_assert_counters(&self) {
224 let (actual_leaves, actual_dirty) =
225 ArenaParallelSparseTrie::count_leaves_and_dirty(&self.arena, self.root);
226 debug_assert_eq!(
227 self.num_leaves, actual_leaves,
228 "subtrie {:?} num_leaves mismatch: stored {} vs actual {}",
229 self.path, self.num_leaves, actual_leaves,
230 );
231 debug_assert_eq!(
232 self.num_dirty_leaves, actual_dirty,
233 "subtrie {:?} num_dirty_leaves mismatch: stored {} vs actual {}",
234 self.path, self.num_dirty_leaves, actual_dirty,
235 );
236 }
237
238 fn prune(&mut self, prune_before: TrieNodeEpoch) -> usize {
243 if branch_idx(&self.arena, self.root).is_none() {
245 return 0;
246 }
247
248 debug_assert_eq!(self.num_dirty_leaves, 0, "prune must run after hashing");
249
250 if prune_before == TrieNodeEpoch::UNMODIFIED {
251 return 0;
252 }
253
254 let old_count = self.arena.len();
255 let mut new_arena = NodeArena::with_capacity(old_count);
258 let mut marks = new_arena.adopt_blinded(&mut self.arena);
259 let mut new_num_leaves = 0u64;
260
261 let new_root = move_node(&mut new_arena, &mut self.arena, self.root);
263 let mut stack = Vec::new();
264 if let Some(frame) =
265 prepare_retained_node(&new_arena, new_root, self.path, &mut new_num_leaves)
266 {
267 stack.push(frame);
268 }
269
270 while let Some(frame) = stack.last_mut() {
271 let Some((child_pos, nibble, child)) = frame.next_child(&new_arena) else {
272 stack.pop();
273 continue;
274 };
275
276 let Some(old_child_idx) = child.revealed_index() else {
277 marks.mark(child);
279 continue;
280 };
281
282 let parent_new_idx = frame.new_idx;
283 let mut child_path = frame.branch_logical_path;
284 child_path.push(nibble);
285
286 let child_epoch = node_state_ref(&self.arena, old_child_idx)
287 .and_then(ArenaSparseNodeState::cached_epoch)
288 .expect("prune must run after hashing");
289
290 if child_epoch.should_prune(prune_before) {
291 let node = &self.arena[old_child_idx];
292 let rlp_node = node_state_ref(&self.arena, old_child_idx)
293 .and_then(ArenaSparseNodeState::cached_rlp_node)
294 .cloned()
295 .expect("prune must run after hashing");
296 trace!(
297 target: TRACE_TARGET,
298 path = ?child_path,
299 variant = %AsRef::<str>::as_ref(node),
300 cached_rlp_node = ?rlp_node,
301 "pruning node",
302 );
303 let new_child = new_arena.insert_blinded(rlp_node);
304 marks.mark(new_child);
305 branch_data_mut(&mut new_arena, parent_new_idx).children[child_pos] = new_child;
306 let node = self.arena.drain_node(old_child_idx);
307 if let Some(child) = node.extension_child() {
308 self.arena.drain_node(child);
309 }
310 } else {
311 let new_child_idx = move_node(&mut new_arena, &mut self.arena, old_child_idx);
312 if let Some(frame) = prepare_retained_node(
313 &new_arena,
314 new_child_idx,
315 child_path,
316 &mut new_num_leaves,
317 ) {
318 stack.push(frame);
319 }
320 branch_data_mut(&mut new_arena, parent_new_idx).children[child_pos] =
321 BranchChild::revealed(new_child_idx);
322 }
323 }
324
325 new_arena.sweep_blinded(marks);
326 new_arena.shrink_nodes_to_fit();
327 let pruned = old_count - new_arena.len();
328 self.num_leaves = new_num_leaves;
329 self.num_dirty_leaves = 0;
330 self.arena = new_arena;
331 self.root = new_root;
332
333 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
334 self.debug_assert_counters();
335 return pruned;
336
337 struct CopyFrame {
338 new_idx: Index,
339 branch_logical_path: Nibbles,
340 state_mask: TrieMask,
341 remaining_child_mask: TrieMask,
342 }
343
344 impl CopyFrame {
345 fn next_child(&mut self, new_arena: &NodeArena) -> Option<(usize, u8, BranchChild)> {
346 let b = branch_data(new_arena, self.new_idx);
347
348 let nibble = self.remaining_child_mask.first_set_bit_index()?;
349 self.remaining_child_mask.unset_bit(nibble);
350 let child_idx = BranchChildIdx::new(self.state_mask, nibble)
351 .expect("remaining_child_mask must be a subset of state_mask");
352
353 Some((child_idx.get(), nibble, b.children[child_idx]))
354 }
355 }
356
357 fn prepare_retained_node(
360 new_arena: &NodeArena,
361 new_idx: Index,
362 node_path: Nibbles,
363 new_num_leaves: &mut u64,
364 ) -> Option<CopyFrame> {
365 if branch_idx(new_arena, new_idx).is_none() {
366 if matches!(&new_arena[new_idx], ArenaSparseNode::Leaf { .. }) {
367 *new_num_leaves += 1;
368 }
369 return None;
370 }
371 let b = branch_data(new_arena, new_idx);
372
373 let mut branch_logical_path = node_path;
374 if let Some(key) = new_arena[new_idx].extension_key() {
375 branch_logical_path.extend(key);
376 }
377
378 Some(CopyFrame {
379 new_idx,
380 branch_logical_path,
381 state_mask: b.state_mask,
382 remaining_child_mask: b.state_mask,
383 })
384 }
385 }
386
387 #[instrument(
395 level = "trace",
396 target = TRACE_TARGET,
397 skip_all,
398 fields(
399 subtrie = ?self.path,
400 num_updates = sorted_updates.len(),
401 ),
402 )]
403 fn update_leaves(&mut self, sorted_updates: &[(B256, Nibbles, LeafUpdate)]) {
404 if sorted_updates.is_empty() {
405 return;
406 }
407 trace!(target: TRACE_TARGET, "Subtrie update_leaves");
408
409 debug_assert!(
410 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
411 "subtrie root must not be EmptyRoot at start of update_leaves"
412 );
413
414 self.buffers.cursor.reset(&self.arena, self.root, self.path);
415
416 for (idx, &(key, ref full_path, ref update)) in sorted_updates.iter().enumerate() {
417 let find_result = self.buffers.cursor.seek(&mut self.arena, full_path);
418
419 if matches!(find_result, SeekResult::Blinded) {
421 let logical_len = self.buffers.cursor.head_logical_branch_path_len(&self.arena);
422 self.required_proofs.push((
423 idx,
424 ArenaRequiredProof { key, parent: ProofV2TargetParent::new(logical_len) },
425 ));
426 continue;
427 }
428
429 match update {
430 LeafUpdate::Changed(value) if !value.is_empty() => {
431 let (_result, deltas) = ArenaParallelSparseTrie::upsert_leaf(
433 &mut self.arena,
434 &mut self.buffers.cursor,
435 &mut self.root,
436 full_path,
437 value,
438 find_result,
439 );
440 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
441 self.num_dirty_leaves =
442 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
443 }
444 LeafUpdate::Changed(_) => {
445 let (result, deltas) = ArenaParallelSparseTrie::remove_leaf(
446 &mut self.arena,
447 &mut self.buffers.cursor,
448 &mut self.root,
449 key,
450 full_path,
451 find_result,
452 &mut self.buffers.updates,
453 );
454 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
455 self.num_dirty_leaves =
456 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
457
458 if let RemoveLeafResult::NeedsProof { proof_key, parent, .. } = result {
461 self.required_proofs
462 .push((idx, ArenaRequiredProof { key: proof_key, parent }));
463 }
464 }
465 LeafUpdate::Touched => {}
466 }
467 }
468
469 self.buffers.cursor.drain(&mut self.arena);
471
472 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
473 self.debug_assert_counters();
474 }
475
476 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
479 if nodes.is_empty() {
480 return Ok(());
481 }
482 trace!(target: TRACE_TARGET, path = ?self.path, num_nodes = nodes.len(), "Subtrie reveal_nodes");
483
484 debug_assert!(
485 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
486 "subtrie root must not be EmptyRoot in reveal_nodes"
487 );
488
489 self.buffers.cursor.reset(&self.arena, self.root, self.path);
490
491 for node in nodes.iter_mut() {
492 let find_result = self.buffers.cursor.seek(&mut self.arena, &node.path);
493 if ArenaParallelSparseTrie::reveal_node(
494 &mut self.arena,
495 &self.buffers.cursor,
496 node,
497 find_result,
498 )
499 .is_some_and(|child_idx| matches!(self.arena[child_idx], ArenaSparseNode::Leaf { .. }))
500 {
501 self.num_leaves += 1;
502 }
503 }
504
505 self.buffers.cursor.drain(&mut self.arena);
507
508 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
509 self.debug_assert_counters();
510
511 Ok(())
512 }
513
514 fn update_cached_rlp(&mut self, new_epoch: TrieNodeEpoch) {
519 ArenaParallelSparseTrie::update_cached_rlp(
520 &mut self.arena,
521 self.root,
522 self.path,
523 &mut self.buffers,
524 new_epoch,
525 );
526 self.num_dirty_leaves = 0;
527 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
528 self.debug_assert_counters();
529 }
530}
531
532#[derive(Debug, Default)]
536struct SubtrieCounterDeltas {
537 num_leaves_delta: i64,
538 num_dirty_leaves_delta: i64,
539}
540
541#[derive(Debug)]
544enum UpsertLeafResult {
545 Updated,
547 NewLeaf,
549 NewChild,
552}
553
554#[derive(Debug)]
557enum RemoveLeafResult {
558 Removed,
560 NotFound,
562 NeedsProof { key: B256, proof_key: B256, parent: ProofV2TargetParent },
565}
566
567#[derive(Debug, Clone)]
569struct ArenaRequiredProof {
570 key: B256,
572 parent: ProofV2TargetParent,
574}
575
576#[derive(Debug, Clone, Copy, PartialEq, Eq)]
581pub struct ArenaParallelismThresholds {
582 pub min_dirty_leaves: u64,
586 pub min_revealed_nodes: usize,
590 pub min_updates: usize,
594 pub min_leaves_for_prune: u64,
598}
599
600impl Default for ArenaParallelismThresholds {
601 fn default() -> Self {
602 Self {
603 min_dirty_leaves: 64,
604 min_revealed_nodes: 16,
605 min_updates: 128,
606 min_leaves_for_prune: 128,
607 }
608 }
609}
610
611#[derive(Debug, Clone)]
668pub struct ArenaParallelSparseTrie {
669 upper_arena: NodeArena,
671 root: Index,
673 buffers: ArenaTrieBuffers,
675 parallelism_thresholds: ArenaParallelismThresholds,
677}
678
679impl ArenaParallelSparseTrie {
680 pub const fn with_parallelism_thresholds(
682 mut self,
683 thresholds: ArenaParallelismThresholds,
684 ) -> Self {
685 self.parallelism_thresholds = thresholds;
686 self
687 }
688
689 const fn should_be_subtrie(path_len: usize) -> bool {
692 path_len == UPPER_TRIE_MAX_DEPTH
693 }
694
695 fn maybe_wrap_in_subtrie(&mut self, child_idx: Index, child_path: &Nibbles) {
700 if !Self::should_be_subtrie(child_path.len()) {
701 return;
702 }
703
704 if !matches!(
706 self.upper_arena[child_idx],
707 ArenaSparseNode::Branch(_) |
708 ArenaSparseNode::Extension { .. } |
709 ArenaSparseNode::Leaf { .. }
710 ) {
711 return;
712 }
713
714 trace!(target: TRACE_TARGET, ?child_path, "Wrapping child into subtrie");
715 let mut subtrie = ArenaSparseSubtrie::new(self.buffers.updates.is_some());
716 subtrie.path = *child_path;
717 let mut root_node =
718 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie);
719
720 Self::migrate_children(&mut subtrie.arena, &mut self.upper_arena, &mut root_node);
722
723 subtrie.arena[subtrie.root] = root_node;
724 let (leaves, dirty) = Self::count_leaves_and_dirty(&subtrie.arena, subtrie.root);
725 subtrie.num_leaves = leaves;
726 subtrie.num_dirty_leaves = dirty;
727 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
728 subtrie.debug_assert_counters();
729 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
730 }
731
732 fn maybe_wrap_branch_children(&mut self, cursor: &ArenaCursor) {
737 let head_idx = cursor.head().expect("cursor is non-empty").index;
738 let head_path = cursor.head_path();
739
740 let Some(branch_idx) = branch_idx(&self.upper_arena, head_idx) else { return };
741 let short_key = self.upper_arena[head_idx].extension_key().copied();
742 let b = self.upper_arena[branch_idx].branch_ref();
743 let children: SmallVec<[_; 4]> = b
744 .child_iter()
745 .filter_map(|(nibble, child)| Some((nibble, child.revealed_index()?)))
746 .collect();
747
748 for (nibble, child_idx) in children {
749 let mut child_path = head_path;
750 if let Some(key) = short_key {
751 child_path.extend(&key);
752 }
753 child_path.push_unchecked(nibble);
754 self.maybe_wrap_in_subtrie(child_idx, &child_path);
755 }
756 }
757
758 #[instrument(
767 level = "trace",
768 target = TRACE_TARGET,
769 skip_all,
770 fields(subtrie_path = ?cursor.head_path()),
771 )]
772 fn maybe_unwrap_subtrie(&mut self, cursor: &mut ArenaCursor) {
773 let subtrie_idx = cursor.head().expect("cursor is non-empty").index;
774
775 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[subtrie_idx] else {
776 return;
777 };
778
779 if !matches!(subtrie.arena[subtrie.root], ArenaSparseNode::EmptyRoot { .. }) {
780 return;
781 }
782
783 let child_nibble =
784 cursor.head_last_nibble().expect("subtrie path must have at least one nibble");
785 let parent_idx = cursor.parent().expect("cursor has parent").index;
786
787 cursor.pop(&mut self.upper_arena);
790
791 self.recycle_subtrie_from_idx(subtrie_idx);
792
793 trace!(target: TRACE_TARGET, "Unwrapping empty subtrie, removing child slot");
794 let parent_branch = branch_data_mut(&mut self.upper_arena, parent_idx);
795 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
796 .expect("child nibble not found in parent state_mask");
797
798 parent_branch.children.remove(child_idx.get());
799 parent_branch.unset_child_bit(child_nibble);
800 parent_branch.state = parent_branch.state.to_dirty();
802
803 self.maybe_collapse_or_remove_branch(cursor);
804 }
805
806 fn recycle_subtrie(&mut self, node: ArenaSparseNode) {
812 let ArenaSparseNode::Subtrie(mut subtrie) = node else {
813 unreachable!("recycle_subtrie called on non-Subtrie node")
814 };
815 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
816 }
817
818 fn recycle_subtrie_from_idx(&mut self, idx: Index) {
820 let node = self.upper_arena.remove(idx).expect("subtrie exists in arena");
821 self.recycle_subtrie(node);
822 }
823
824 fn restore_taken_subtrie(&mut self, idx: Index, subtrie: Box<ArenaSparseSubtrie>) {
830 debug_assert!(
831 matches!(self.upper_arena[idx], ArenaSparseNode::TakenSubtrie),
832 "taken subtrie slot {idx:?} was removed or reused before restoration",
833 );
834 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
835 }
836
837 fn maybe_collapse_or_remove_branch(&mut self, cursor: &mut ArenaCursor) {
848 loop {
849 let branch_node_idx = cursor.head().expect("cursor is non-empty").index;
850 let branch_path = cursor.head_path();
851
852 let Some(branch_child_idx) = branch_idx(&self.upper_arena, branch_node_idx) else {
855 return;
856 };
857 let count = self.upper_arena[branch_child_idx].branch_ref().state_mask.count_bits();
858
859 if count >= 2 {
860 return;
861 }
862
863 if count == 0 {
864 if branch_node_idx == self.root {
865 self.upper_arena[branch_node_idx] =
866 ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Dirty };
867 if branch_child_idx != branch_node_idx {
868 self.upper_arena.remove(branch_child_idx);
869 }
870 return;
871 }
872 let branch_nibble = branch_path.last().expect("non-root branch");
874 cursor.pop(&mut self.upper_arena);
875 self.upper_arena.remove(branch_node_idx);
876 if branch_child_idx != branch_node_idx {
877 self.upper_arena.remove(branch_child_idx);
878 }
879 let parent_idx = cursor.head().expect("cursor is non-empty").index;
880 let parent_branch = branch_data_mut(&mut self.upper_arena, parent_idx);
881 let child_idx = BranchChildIdx::new(parent_branch.state_mask, branch_nibble)
882 .expect("child nibble not found in parent state_mask");
883 parent_branch.children.remove(child_idx.get());
884 parent_branch.unset_child_bit(branch_nibble);
885 parent_branch.state = parent_branch.state.to_dirty();
886 continue; }
888
889 let (remaining_nibble, remaining_child_idx) = {
891 let b = self.upper_arena[branch_child_idx].branch_ref();
892 let nibble = b.state_mask.iter().next().expect("branch has at least one child");
893 (nibble, b.children[0].revealed_index())
894 };
895
896 let Some(child_idx) = remaining_child_idx else {
897 debug_assert!(false, "single remaining child is blinded — should have been caught by check_subtrie_collapse_needs_proof");
898 return;
899 };
900
901 if matches!(self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie) {
902 return;
905 }
906
907 let is_empty_subtrie = matches!(
909 &self.upper_arena[child_idx],
910 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot { .. })
911 );
912
913 if is_empty_subtrie {
914 self.recycle_subtrie_from_idx(child_idx);
915 let branch = branch_data_mut(&mut self.upper_arena, branch_node_idx);
916 branch.children.remove(0);
917 branch.unset_child_bit(remaining_nibble);
918 branch.state = branch.state.to_dirty();
919 continue; }
921
922 Self::collapse_branch(
924 &mut self.upper_arena,
925 cursor,
926 &mut self.root,
927 &mut self.buffers.updates,
928 );
929
930 let child_idx = cursor.head().expect("cursor is non-empty").index;
935 if let ArenaSparseNode::Subtrie(_) = &self.upper_arena[child_idx] {
936 let ArenaSparseNode::Subtrie(mut subtrie) =
937 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie)
938 else {
939 unreachable!()
940 };
941 Self::migrate_nodes(
942 &mut self.upper_arena,
943 &mut subtrie.arena,
944 subtrie.root,
945 Some(child_idx),
946 );
947 Self::merge_subtrie_updates(
948 &mut self.buffers.updates,
949 &mut subtrie.buffers.updates,
950 );
951
952 self.maybe_wrap_branch_children(cursor);
956 }
957 return;
958 }
959 }
960
961 fn merge_subtrie_updates(
968 dst: &mut Option<SparseTrieUpdates>,
969 src: &mut Option<SparseTrieUpdates>,
970 ) {
971 if let Some(dst_updates) = dst.as_mut() {
972 let src_updates = src.as_mut().expect("updates are enabled");
973
974 for path in src_updates.updated_nodes.keys() {
976 dst_updates.removed_nodes.remove(path);
977 }
978 dst_updates.updated_nodes.extend(src_updates.updated_nodes.drain());
979
980 for path in &src_updates.removed_nodes {
982 dst_updates.updated_nodes.remove(path);
983 }
984 dst_updates.removed_nodes.extend(src_updates.removed_nodes.drain());
985 }
986 }
987
988 fn nibbles_to_padded_b256(path: &Nibbles) -> B256 {
990 let mut bytes = [0u8; 32];
991 path.pack_to(&mut bytes);
992 B256::from(bytes)
993 }
994
995 fn get_branch_masks(arena: &NodeArena, branch: &ArenaSparseNodeBranch) -> BranchNodeMasks {
997 let mut masks = BranchNodeMasks::default();
998
999 for (nibble, child) in branch.child_iter() {
1000 let (hash_bit, tree_bit) = match child.revealed_index() {
1001 Some(child_idx) => {
1002 let child = &arena[child_idx];
1003 (child.hash_mask_bit(), node_tree_mask_bit(arena, child_idx))
1004 }
1005 None => (
1006 branch.branch_masks.hash_mask.is_bit_set(nibble),
1007 branch.branch_masks.tree_mask.is_bit_set(nibble),
1008 ),
1009 };
1010
1011 masks.set_child_bits(nibble, hash_bit, tree_bit);
1012 }
1013
1014 masks
1015 }
1016
1017 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(base_path = ?base_path), ret)]
1030 fn update_cached_rlp(
1031 arena: &mut NodeArena,
1032 root: Index,
1033 base_path: Nibbles,
1034 buffers: &mut ArenaTrieBuffers,
1035 new_epoch: TrieNodeEpoch,
1036 ) -> RlpNode {
1037 let cursor = &mut buffers.cursor;
1038 let rlp_buf = &mut buffers.rlp_buf;
1039 let rlp_node_buf = &mut buffers.rlp_node_buf;
1040 let updates = &mut buffers.updates;
1041
1042 rlp_node_buf.clear();
1043
1044 match &arena[root] {
1048 ArenaSparseNode::EmptyRoot { state } => {
1049 let node_epoch = match state {
1050 ArenaSparseNodeState::Cached { epoch, .. } => *epoch,
1051 ArenaSparseNodeState::Revealed => TrieNodeEpoch::UNMODIFIED,
1052 ArenaSparseNodeState::Dirty => new_epoch,
1053 };
1054 let rlp_node = RlpNode::word_rlp(&EMPTY_ROOT_HASH);
1055 *arena[root].state_mut() =
1056 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), epoch: node_epoch };
1057 return rlp_node
1058 }
1059 ArenaSparseNode::Leaf { .. } => {
1060 Self::encode_leaf(arena, root, rlp_buf, rlp_node_buf, new_epoch);
1061 return rlp_node_buf.pop().expect("encode_leaf must push an RlpNode");
1062 }
1063 ArenaSparseNode::Branch(_) | ArenaSparseNode::Extension { .. } => {
1064 if let Some(ArenaSparseNodeState::Cached { rlp_node, .. }) =
1065 node_state_ref(arena, root)
1066 {
1067 let rlp_node = rlp_node.clone();
1068 return rlp_node;
1069 }
1070 }
1071 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie | ArenaSparseNode::Free => {
1072 unreachable!("Subtrie/TakenSubtrie/Free cannot be a subtrie's root");
1073 }
1074 }
1075
1076 cursor.reset(arena, root, base_path);
1077
1078 loop {
1082 let result = cursor.next(&mut *arena, |_, node| {
1083 matches!(node, ArenaSparseNode::Branch(b) if !matches!(b.state, ArenaSparseNodeState::Cached { .. })) ||
1084 matches!(node, ArenaSparseNode::Extension { .. })
1085 });
1086
1087 match result {
1088 NextResult::Done => break,
1089 NextResult::NonBranch => {
1090 unreachable!("should_descend only returns true for dirty branches")
1091 }
1092 NextResult::Branch => {}
1093 };
1094
1095 let head_idx = cursor.head().expect("cursor is non-empty").index;
1096 if matches!(node_state_ref(arena, head_idx), Some(ArenaSparseNodeState::Cached { .. }))
1099 {
1100 continue;
1101 }
1102 let head_path = cursor.head_path();
1103
1104 trace!(
1108 target: TRACE_TARGET,
1109 branch_path = ?head_path,
1110 branch_short_key = ?arena[head_idx].extension_key(),
1111 state_mask = ?branch_data(arena, head_idx).state_mask,
1112 "Calculating branch RlpNode",
1113 );
1114
1115 rlp_node_buf.clear();
1116 let mut node_epoch = TrieNodeEpoch::UNMODIFIED;
1117 let state_mask = branch_data(arena, head_idx).state_mask;
1118 for (dense_idx, _nibble) in BranchChildIter::new(state_mask) {
1119 let child = branch_data(arena, head_idx).children[dense_idx];
1120 match child.revealed_index() {
1121 None => {
1122 rlp_node_buf.push(arena.blinded(child).clone());
1123 }
1124 Some(child_idx) => {
1125 match &arena[child_idx] {
1126 ArenaSparseNode::Leaf { .. } => {
1127 Self::encode_leaf(
1128 arena,
1129 child_idx,
1130 rlp_buf,
1131 rlp_node_buf,
1132 new_epoch,
1133 );
1134 }
1135 ArenaSparseNode::Branch(child_b) => {
1136 let ArenaSparseNodeState::Cached { rlp_node, .. } = &child_b.state
1137 else {
1138 panic!("child branch must be cached after DFS");
1139 };
1140 let rlp_node = rlp_node.clone();
1141 rlp_node_buf.push(rlp_node);
1142 }
1143 ArenaSparseNode::Extension { child, .. } => {
1144 let Some(ArenaSparseNodeState::Cached { rlp_node, .. }) =
1145 arena[*child].state_ref()
1146 else {
1147 panic!("extension branch child must be cached after DFS");
1148 };
1149 rlp_node_buf.push(rlp_node.clone());
1150 }
1151 ArenaSparseNode::Subtrie(subtrie) => {
1152 let Some(ArenaSparseNodeState::Cached { rlp_node, .. }) =
1153 node_state_ref(&subtrie.arena, subtrie.root)
1154 else {
1155 panic!("subtrie root must be cached");
1156 };
1157 rlp_node_buf.push(rlp_node.clone());
1158 }
1159 ArenaSparseNode::TakenSubtrie |
1160 ArenaSparseNode::EmptyRoot { .. } |
1161 ArenaSparseNode::Free => {
1162 unreachable!("Unexpected child {:?}", arena[child_idx]);
1163 }
1164 }
1165 let Some(ArenaSparseNodeState::Cached { epoch: child_epoch, .. }) =
1166 node_state_ref(arena, child_idx)
1167 else {
1168 panic!("revealed child must be cached after encoding");
1169 };
1170 node_epoch = node_epoch.max(*child_epoch);
1171 }
1172 }
1173 }
1174
1175 let b = branch_data(arena, head_idx);
1177 let short_key = arena[head_idx].extension_key().copied().unwrap_or_default();
1178 let state_mask = b.state_mask;
1179 let prev_branch_masks = b.branch_masks;
1180 let new_branch_masks = Self::get_branch_masks(arena, b);
1181 let was_dirty = matches!(b.state, ArenaSparseNodeState::Dirty);
1182 if was_dirty {
1183 node_epoch = node_epoch.max(new_epoch);
1184 }
1185
1186 rlp_buf.clear();
1187 let rlp_node = BranchNodeRef::new(rlp_node_buf, state_mask).rlp(rlp_buf);
1188
1189 let rlp_node = if short_key.is_empty() {
1190 rlp_node
1191 } else {
1192 rlp_buf.clear();
1193 ExtensionNodeRef::new(&short_key, &rlp_node).rlp(rlp_buf)
1194 };
1195
1196 trace!(
1197 target: TRACE_TARGET,
1198 path = ?head_path,
1199 short_key = ?arena[head_idx].short_key(),
1200 children = ?state_mask.iter().zip(rlp_node_buf.iter()).collect::<Vec<_>>(),
1201 rlp_node = ?rlp_node,
1202 "Calculated branch RlpNode",
1203 );
1204
1205 let branch = branch_data_mut(arena, head_idx);
1206 branch.state = ArenaSparseNodeState::Cached { rlp_node, epoch: node_epoch };
1207 branch.branch_masks = new_branch_masks;
1208
1209 if let Some(trie_updates) = updates.as_mut().filter(|_| was_dirty) {
1212 let mut logical_path = head_path;
1213 logical_path.extend(&short_key);
1214
1215 if !logical_path.is_empty() {
1216 if !prev_branch_masks.is_empty() && new_branch_masks.is_empty() {
1217 trie_updates.updated_nodes.remove(&logical_path);
1218 trie_updates.removed_nodes.insert(logical_path);
1219 } else if !new_branch_masks.is_empty() {
1220 let compact = branch_data(arena, head_idx).branch_node_compact(arena);
1221 trie_updates.updated_nodes.insert(logical_path, compact);
1222 trie_updates.removed_nodes.remove(&logical_path);
1223 }
1224 }
1225 }
1226 }
1227
1228 let Some(ArenaSparseNodeState::Cached { rlp_node, .. }) = node_state_ref(arena, root)
1229 else {
1230 panic!("root must be cached after update_cached_rlp");
1231 };
1232 rlp_node.clone()
1233 }
1234
1235 fn get_leaf_value_in_arena<'a>(
1238 arena: &'a NodeArena,
1239 mut current: Index,
1240 full_path: &Nibbles,
1241 mut path_offset: usize,
1242 ) -> Option<&'a Vec<u8>> {
1243 loop {
1244 match &arena[current] {
1245 ArenaSparseNode::EmptyRoot { .. } |
1246 ArenaSparseNode::TakenSubtrie |
1247 ArenaSparseNode::Free => return None,
1248 ArenaSparseNode::Leaf { key, value, .. } => {
1249 let remaining = full_path.slice_unchecked(path_offset, full_path.len());
1250 return (remaining == *key).then_some(value);
1251 }
1252 ArenaSparseNode::Extension { key, child } => {
1253 let logical_end = path_offset + key.len();
1254 if full_path.len() <= logical_end ||
1255 full_path.slice_unchecked(path_offset, logical_end) != *key
1256 {
1257 return None;
1258 }
1259 current = *child;
1260 path_offset = logical_end;
1261 }
1262 ArenaSparseNode::Branch(b) => {
1263 if full_path.len() <= path_offset {
1264 return None;
1265 }
1266
1267 let child_nibble = full_path.get_unchecked(path_offset);
1268 let child_idx = BranchChildIdx::new(b.state_mask, child_nibble)?;
1269 current = b.children[child_idx].revealed_index()?;
1270 path_offset += 1;
1271 }
1272 ArenaSparseNode::Subtrie(subtrie) => {
1273 return Self::get_leaf_value_in_arena(
1274 &subtrie.arena,
1275 subtrie.root,
1276 full_path,
1277 path_offset,
1278 );
1279 }
1280 }
1281 }
1282 }
1283
1284 fn find_leaf_in_arena(
1288 arena: &NodeArena,
1289 mut current: Index,
1290 full_path: &Nibbles,
1291 mut path_offset: usize,
1292 expected_value: Option<&Vec<u8>>,
1293 ) -> Result<LeafLookup, LeafLookupError> {
1294 loop {
1295 match &arena[current] {
1296 ArenaSparseNode::EmptyRoot { .. } |
1297 ArenaSparseNode::TakenSubtrie |
1298 ArenaSparseNode::Free => {
1299 return Ok(LeafLookup::NonExistent);
1300 }
1301 ArenaSparseNode::Leaf { key, value, .. } => {
1302 let remaining = full_path.slice(path_offset..);
1303 if remaining != *key {
1304 return Ok(LeafLookup::NonExistent);
1305 }
1306 if let Some(expected) = expected_value &&
1307 *expected != *value
1308 {
1309 return Err(LeafLookupError::ValueMismatch {
1310 path: *full_path,
1311 expected: Some(expected.clone()),
1312 actual: value.clone(),
1313 });
1314 }
1315 return Ok(LeafLookup::Exists);
1316 }
1317 ArenaSparseNode::Extension { key, child } => {
1318 let logical_end = path_offset + key.len();
1319 if full_path.len() <= logical_end ||
1320 full_path.slice(path_offset..logical_end) != *key
1321 {
1322 return Ok(LeafLookup::NonExistent);
1323 }
1324 current = *child;
1325 path_offset = logical_end;
1326 }
1327 ArenaSparseNode::Branch(b) => {
1328 if full_path.len() <= path_offset {
1329 return Ok(LeafLookup::NonExistent);
1330 }
1331
1332 let child_nibble = full_path.get_unchecked(path_offset);
1333 let Some(child_idx) = BranchChildIdx::new(b.state_mask, child_nibble) else {
1334 return Ok(LeafLookup::NonExistent);
1335 };
1336
1337 let child = b.children[child_idx];
1338 match child.revealed_index() {
1339 None => {
1340 let rlp_node = arena.blinded(child);
1341 let hash = rlp_node
1342 .as_hash()
1343 .unwrap_or_else(|| keccak256(rlp_node.as_slice()));
1344 let mut blinded_path = full_path.slice(..path_offset);
1345 blinded_path.push_unchecked(child_nibble);
1346 return Err(LeafLookupError::BlindedNode { path: blinded_path, hash });
1347 }
1348 Some(child_idx) => {
1349 current = child_idx;
1350 path_offset += 1;
1351 }
1352 }
1353 }
1354 ArenaSparseNode::Subtrie(subtrie) => {
1355 return Self::find_leaf_in_arena(
1356 &subtrie.arena,
1357 subtrie.root,
1358 full_path,
1359 path_offset,
1360 expected_value,
1361 );
1362 }
1363 }
1364 }
1365 }
1366
1367 fn encode_leaf(
1371 arena: &mut NodeArena,
1372 idx: Index,
1373 rlp_buf: &mut Vec<u8>,
1374 rlp_node_buf: &mut Vec<RlpNode>,
1375 new_epoch: TrieNodeEpoch,
1376 ) {
1377 let (key, value, state) = match &arena[idx] {
1378 ArenaSparseNode::Leaf { key, value, state } => (key, value, state),
1379 _ => unreachable!("encode_leaf called on non-Leaf node"),
1380 };
1381
1382 let epoch = match state {
1383 ArenaSparseNodeState::Cached { rlp_node, .. } => {
1384 rlp_node_buf.push(rlp_node.clone());
1385 return;
1386 }
1387 ArenaSparseNodeState::Revealed => TrieNodeEpoch::UNMODIFIED,
1388 ArenaSparseNodeState::Dirty => new_epoch,
1389 };
1390
1391 rlp_buf.clear();
1392 let rlp_node = LeafNodeRef { key, value }.rlp(rlp_buf);
1393
1394 *arena[idx].state_mut() =
1395 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), epoch };
1396 rlp_node_buf.push(rlp_node);
1397 }
1398
1399 fn split_and_insert_leaf(
1413 arena: &mut NodeArena,
1414 cursor: &mut ArenaCursor,
1415 root: &mut Index,
1416 new_leaf_path: Nibbles,
1417 value: &[u8],
1418 ) -> bool {
1419 let old_child_idx = cursor.head().expect("cursor must have head").index;
1420 let old_child_short_key = arena[old_child_idx].short_key().expect("top of stack is a leaf");
1421 let diverge_len = new_leaf_path.common_prefix_length(old_child_short_key);
1422
1423 trace!(
1424 target: TRACE_TARGET,
1425 path = ?cursor.head_path(),
1426 ?new_leaf_path,
1427 ?old_child_short_key,
1428 diverge_len,
1429 "Splitting node and inserting new leaf",
1430 );
1431
1432 let old_child_nibble = old_child_short_key.get_unchecked(diverge_len);
1433 let old_child_suffix = old_child_short_key.slice(diverge_len + 1..);
1434
1435 let old_child = mem::replace(&mut arena[old_child_idx], ArenaSparseNode::TakenSubtrie);
1438 let newly_dirtied_existing = match old_child {
1439 ArenaSparseNode::Leaf { value, state, .. } => {
1440 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1441 arena[old_child_idx] = ArenaSparseNode::Leaf {
1442 key: old_child_suffix,
1443 value,
1444 state: ArenaSparseNodeState::Dirty,
1445 };
1446 was_clean
1447 }
1448 ArenaSparseNode::Extension { child, .. } => {
1449 if old_child_suffix.is_empty() {
1450 let branch = arena.remove(child).expect("extension child exists");
1451 arena[old_child_idx] = branch;
1452 *arena[old_child_idx].state_mut() = ArenaSparseNodeState::Dirty;
1453 } else {
1454 arena[old_child_idx] =
1455 ArenaSparseNode::Extension { key: old_child_suffix, child };
1456 *arena[child].state_mut() = ArenaSparseNodeState::Dirty;
1457 }
1458 false
1460 }
1461 _ => unreachable!("split_and_insert_leaf called on non-Leaf/Extension node"),
1462 };
1463
1464 let short_key = new_leaf_path.slice(..diverge_len);
1465 let new_leaf_nibble = new_leaf_path.get_unchecked(diverge_len);
1466 debug_assert_ne!(old_child_nibble, new_leaf_nibble);
1467
1468 let new_leaf_idx = arena.insert(ArenaSparseNode::Leaf {
1469 state: ArenaSparseNodeState::Dirty,
1470 key: new_leaf_path.slice(diverge_len + 1..),
1471 value: value.to_vec(),
1472 });
1473
1474 let (first_nibble, first_child, second_nibble, second_child) =
1475 if old_child_nibble < new_leaf_nibble {
1476 (old_child_nibble, old_child_idx, new_leaf_nibble, new_leaf_idx)
1477 } else {
1478 (new_leaf_nibble, new_leaf_idx, old_child_nibble, old_child_idx)
1479 };
1480
1481 let state_mask = TrieMask::from_nibble(first_nibble) | TrieMask::from_nibble(second_nibble);
1482 let mut children = SmallVec::new();
1483 children.push(BranchChild::revealed(first_child));
1484 children.push(BranchChild::revealed(second_child));
1485
1486 let branch_child_idx = arena.insert(ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1487 state: ArenaSparseNodeState::Dirty,
1488 children,
1489 state_mask,
1490 branch_masks: BranchNodeMasks::default(),
1491 }));
1492 let new_branch_idx = if short_key.is_empty() {
1493 branch_child_idx
1494 } else {
1495 arena.insert(ArenaSparseNode::Extension { key: short_key, child: branch_child_idx })
1496 };
1497
1498 cursor.replace_head_index(arena, root, new_branch_idx);
1499 newly_dirtied_existing
1500 }
1501
1502 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(full_path = ?full_path))]
1517 fn upsert_leaf(
1518 arena: &mut NodeArena,
1519 cursor: &mut ArenaCursor,
1520 root: &mut Index,
1521 full_path: &Nibbles,
1522 value: &[u8],
1523 find_result: SeekResult,
1524 ) -> (UpsertLeafResult, SubtrieCounterDeltas) {
1525 trace!(target: TRACE_TARGET, ?find_result, "Upserting leaf");
1526 let head = cursor.head().expect("cursor is non-empty");
1527
1528 match find_result {
1529 SeekResult::Blinded => {
1530 unreachable!("Blinded case must be handled by caller")
1531 }
1532 SeekResult::EmptyRoot => {
1533 let head_idx = head.index;
1534 arena[head_idx] = ArenaSparseNode::Leaf {
1535 state: ArenaSparseNodeState::Dirty,
1536 key: full_path.slice(cursor.head_path_len()..),
1537 value: value.to_vec(),
1538 };
1539 (
1540 UpsertLeafResult::NewLeaf,
1541 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1542 )
1543 }
1544 SeekResult::RevealedLeaf => {
1545 let head_idx = head.index;
1547 let was_clean =
1548 if let ArenaSparseNode::Leaf { value: v, state, .. } = &mut arena[head_idx] {
1549 v.clear();
1550 v.extend_from_slice(value);
1551 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1552 *state = ArenaSparseNodeState::Dirty;
1553 was_clean
1554 } else {
1555 unreachable!("RevealedLeaf but cursor head is not a leaf")
1556 };
1557 (
1558 UpsertLeafResult::Updated,
1559 SubtrieCounterDeltas {
1560 num_leaves_delta: 0,
1561 num_dirty_leaves_delta: was_clean as i64,
1562 },
1563 )
1564 }
1565 SeekResult::Diverged => {
1566 let full_path_from_head = full_path.slice(cursor.head_path_len()..);
1567
1568 let split_dirtied_existing =
1569 Self::split_and_insert_leaf(arena, cursor, root, full_path_from_head, value);
1570
1571 let result = if cursor.depth() >= 1 {
1572 UpsertLeafResult::NewChild
1573 } else {
1574 UpsertLeafResult::NewLeaf
1575 };
1576 (
1577 result,
1578 SubtrieCounterDeltas {
1579 num_leaves_delta: 1,
1580 num_dirty_leaves_delta: 1 + split_dirtied_existing as i64,
1581 },
1582 )
1583 }
1584 SeekResult::NoChild { child_nibble } => {
1585 let head_idx = head.index;
1586
1587 let leaf_key = full_path.slice(cursor.head_logical_branch_path_len(arena) + 1..);
1588 let new_leaf = arena.insert(ArenaSparseNode::Leaf {
1589 state: ArenaSparseNodeState::Dirty,
1590 key: leaf_key,
1591 value: value.to_vec(),
1592 });
1593
1594 let branch = branch_data_mut(arena, head_idx);
1595 branch.set_child(child_nibble, BranchChild::revealed(new_leaf));
1596
1597 cursor.seek(arena, full_path);
1599
1600 (
1601 UpsertLeafResult::NewChild,
1602 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1603 )
1604 }
1605 SeekResult::RevealedSubtrie => {
1606 unreachable!("RevealedSubtrie must be handled by caller")
1607 }
1608 }
1609 }
1610
1611 fn remove_leaf(
1627 arena: &mut NodeArena,
1628 cursor: &mut ArenaCursor,
1629 root: &mut Index,
1630 key: B256,
1631 full_path: &Nibbles,
1632 find_result: SeekResult,
1633 updates: &mut Option<SparseTrieUpdates>,
1634 ) -> (RemoveLeafResult, SubtrieCounterDeltas) {
1635 match find_result {
1636 SeekResult::Blinded | SeekResult::RevealedSubtrie => {
1637 unreachable!("Blinded/RevealedSubtrie must be handled by caller")
1638 }
1639 SeekResult::EmptyRoot | SeekResult::Diverged | SeekResult::NoChild { .. } => {
1640 (RemoveLeafResult::NotFound, SubtrieCounterDeltas::default())
1641 }
1642 SeekResult::RevealedLeaf => {
1643 let head_idx = cursor.head().expect("cursor is non-empty").index;
1645 let head_path = cursor.head_path();
1646
1647 trace!(
1648 target: TRACE_TARGET,
1649 path = ?head_path,
1650 ?full_path,
1651 "Removing leaf",
1652 );
1653
1654 if let Some(parent_entry) = cursor.parent() {
1657 let parent_idx = parent_entry.index;
1658 let child_nibble = head_path.last().expect("non-root leaf");
1659 let parent_branch = branch_data(arena, parent_idx);
1660
1661 if parent_branch.state_mask.count_bits() == 2 &&
1662 parent_branch.sibling_child(child_nibble).is_blinded()
1663 {
1664 let sibling_nibble = parent_branch
1665 .state_mask
1666 .iter()
1667 .find(|&n| n != child_nibble)
1668 .expect("branch has two children");
1669 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1670 sibling_path.push_unchecked(sibling_nibble);
1671 trace!(target: TRACE_TARGET, ?full_path, ?sibling_path, "Removal would collapse branch onto blinded sibling, requesting proof");
1672 return (
1673 RemoveLeafResult::NeedsProof {
1674 key,
1675 proof_key: Self::nibbles_to_padded_b256(&sibling_path),
1676 parent: ProofV2TargetParent::new(
1677 sibling_path
1678 .len()
1679 .checked_sub(1)
1680 .expect("sibling path has a child nibble"),
1681 ),
1682 },
1683 SubtrieCounterDeltas::default(),
1684 );
1685 }
1686 }
1687
1688 let removed_was_dirty =
1690 matches!(arena[head_idx].state_ref(), Some(ArenaSparseNodeState::Dirty));
1691
1692 if cursor.depth() == 0 {
1693 arena.remove(head_idx);
1696 *root = arena
1697 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Dirty });
1698 cursor.reset(arena, *root, head_path);
1699 return (
1700 RemoveLeafResult::Removed,
1701 SubtrieCounterDeltas {
1702 num_leaves_delta: -1,
1703 num_dirty_leaves_delta: -(removed_was_dirty as i64),
1704 },
1705 );
1706 }
1707
1708 cursor.pop(arena);
1710
1711 let parent_entry = cursor.head().expect("cursor is non-empty");
1713 let parent_idx = parent_entry.index;
1714 let child_nibble = head_path.last().expect("non-root leaf");
1715
1716 arena.remove(head_idx);
1718 let parent_branch = branch_data_mut(arena, parent_idx);
1719 parent_branch.remove_child(child_nibble);
1720
1721 let collapse_dirtied_leaf = if parent_branch.state_mask.count_bits() == 1 {
1724 Self::collapse_branch(arena, cursor, root, updates)
1725 } else {
1726 false
1727 };
1728 (
1729 RemoveLeafResult::Removed,
1730 SubtrieCounterDeltas {
1731 num_leaves_delta: -1,
1732 num_dirty_leaves_delta: (collapse_dirtied_leaf as i64) -
1733 (removed_was_dirty as i64),
1734 },
1735 )
1736 }
1737 }
1738 }
1739
1740 fn check_subtrie_collapse_needs_proof(
1746 arena: &NodeArena,
1747 cursor: &ArenaCursor,
1748 subtrie_updates: &[(B256, Nibbles, LeafUpdate)],
1749 ) -> Option<ArenaRequiredProof> {
1750 let num_removals = subtrie_updates
1751 .iter()
1752 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()))
1753 .count() as u64;
1754
1755 let num_changed = subtrie_updates.iter().filter(|(_, _, u)| u.is_changed()).count() as u64;
1763
1764 if num_removals == 0 || num_removals != num_changed {
1765 return None;
1766 }
1767
1768 let subtrie_entry = cursor.head()?;
1770 let subtrie_num_leaves = match &arena[subtrie_entry.index] {
1771 ArenaSparseNode::Subtrie(s) => s.num_leaves,
1772 _ => return None,
1773 };
1774 if num_removals < subtrie_num_leaves {
1775 return None;
1776 }
1777
1778 let child_nibble =
1779 cursor.head_last_nibble().expect("subtrie path must have at least one nibble");
1780
1781 let parent_entry = cursor.parent()?;
1782 let parent_branch = branch_data(arena, parent_entry.index);
1783 if parent_branch.state_mask.count_bits() != 2 {
1784 return None;
1785 }
1786
1787 if !parent_branch.sibling_child(child_nibble).is_blinded() {
1788 return None;
1789 }
1790
1791 let sibling_nibble = parent_branch
1792 .state_mask
1793 .iter()
1794 .find(|&n| n != child_nibble)
1795 .expect("branch has two children");
1796 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1797 sibling_path.push_unchecked(sibling_nibble);
1798
1799 Some(ArenaRequiredProof {
1800 key: Self::nibbles_to_padded_b256(&sibling_path),
1801 parent: ProofV2TargetParent::new(
1802 sibling_path.len().checked_sub(1).expect("sibling path has a child nibble"),
1803 ),
1804 })
1805 }
1806
1807 fn collapse_branch(
1818 arena: &mut NodeArena,
1819 cursor: &mut ArenaCursor,
1820 root: &mut Index,
1821 updates: &mut Option<SparseTrieUpdates>,
1822 ) -> bool {
1823 let branch_node_idx = cursor.head().expect("cursor is non-empty").index;
1824 let branch_child_idx = branch_idx(arena, branch_node_idx).expect("cursor head is a branch");
1825 let branch_path = cursor.head_path();
1826 let branch = arena[branch_child_idx].branch_ref();
1827 let remaining_nibble =
1828 branch.state_mask.iter().next().expect("branch has at least one child");
1829 let branch_short_key = arena[branch_node_idx].extension_key().copied().unwrap_or_default();
1830
1831 debug_assert_eq!(
1832 branch.state_mask.count_bits(),
1833 1,
1834 "collapse_branch requires exactly 1 child"
1835 );
1836 debug_assert!(
1837 !branch.children[0].is_blinded(),
1838 "collapse_branch called with a blinded remaining child"
1839 );
1840
1841 trace!(
1842 target: TRACE_TARGET,
1843 path = ?branch_path,
1844 short_key = ?branch_short_key,
1845 branch_masks = ?branch.branch_masks,
1846 ?remaining_nibble,
1847 "Collapsing single-child branch",
1848 );
1849
1850 if let Some(trie_updates) = updates.as_mut() &&
1853 !branch.branch_masks.is_empty()
1854 {
1855 let logical_path = cursor.head_logical_branch_path(arena);
1856 if !logical_path.is_empty() {
1857 trie_updates.updated_nodes.remove(&logical_path);
1858 trie_updates.removed_nodes.insert(logical_path);
1859 }
1860 }
1861
1862 let mut prefix = branch_short_key;
1864 prefix.push_unchecked(remaining_nibble);
1865
1866 let child_idx = branch.children[0].revealed_index().expect("remaining child is revealed");
1867
1868 let child = mem::replace(&mut arena[child_idx], ArenaSparseNode::TakenSubtrie);
1871 let (replacement_idx, newly_dirtied_leaf) = match child {
1872 ArenaSparseNode::Leaf { key, value, state } => {
1873 let mut new_key = prefix;
1874 new_key.extend(&key);
1875 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1876 arena[child_idx] = ArenaSparseNode::Leaf {
1877 key: new_key,
1878 state: ArenaSparseNodeState::Dirty,
1879 value,
1880 };
1881 (child_idx, was_clean)
1882 }
1883 ArenaSparseNode::Extension { key, child } => {
1884 let mut new_key = prefix;
1885 new_key.extend(&key);
1886 arena[child_idx] = ArenaSparseNode::Extension { key: new_key, child };
1887 *arena[child].state_mut() = ArenaSparseNodeState::Dirty;
1888 (child_idx, false)
1889 }
1890 ArenaSparseNode::Branch(branch) => {
1891 let new_branch_idx = arena.insert(ArenaSparseNode::Branch(branch));
1892 *arena[new_branch_idx].state_mut() = ArenaSparseNodeState::Dirty;
1893 arena[child_idx] =
1894 ArenaSparseNode::Extension { key: prefix, child: new_branch_idx };
1895 (child_idx, false)
1896 }
1897 ArenaSparseNode::Subtrie(mut subtrie) => {
1898 subtrie.path = branch_path;
1899 let root = subtrie.root;
1900 let root_node =
1901 mem::replace(&mut subtrie.arena[root], ArenaSparseNode::TakenSubtrie);
1902 match root_node {
1903 ArenaSparseNode::Branch(branch) => {
1904 let child = subtrie.arena.insert(ArenaSparseNode::Branch(branch));
1905 subtrie.arena[root] = ArenaSparseNode::Extension { key: prefix, child };
1906 *subtrie.arena[child].state_mut() = ArenaSparseNodeState::Dirty;
1907 }
1908 ArenaSparseNode::Extension { key, child } => {
1909 let mut new_key = prefix;
1910 new_key.extend(&key);
1911 subtrie.arena[root] = ArenaSparseNode::Extension { key: new_key, child };
1912 *subtrie.arena[child].state_mut() = ArenaSparseNodeState::Dirty;
1913 }
1914 ArenaSparseNode::Leaf { key, value, state } => {
1915 let mut new_key = prefix;
1916 new_key.extend(&key);
1917 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1918 subtrie.arena[root] = ArenaSparseNode::Leaf {
1919 key: new_key,
1920 value,
1921 state: ArenaSparseNodeState::Dirty,
1922 };
1923 if was_clean {
1924 subtrie.num_dirty_leaves += 1;
1925 }
1926 }
1927 _ => {
1928 unreachable!("subtrie root must be a Branch or Leaf during collapse_branch")
1929 }
1930 }
1931 arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
1932 (child_idx, false)
1933 }
1934 _ => unreachable!("remaining child must be Leaf, Branch, Extension, or Subtrie"),
1935 };
1936
1937 cursor.replace_head_index(arena, root, replacement_idx);
1939
1940 arena.remove(branch_node_idx);
1942 if branch_node_idx != branch_child_idx {
1943 arena.remove(branch_child_idx);
1944 }
1945 newly_dirtied_leaf
1946 }
1947
1948 fn count_leaves_and_dirty(arena: &NodeArena, idx: Index) -> (u64, u64) {
1950 match &arena[idx] {
1951 ArenaSparseNode::Leaf { state, .. } => {
1952 let dirty = matches!(state, ArenaSparseNodeState::Dirty) as u64;
1953 (1, dirty)
1954 }
1955 ArenaSparseNode::Branch(b) => {
1956 let mut leaves = 0u64;
1957 let mut dirty = 0u64;
1958 for c in &b.children {
1959 if let Some(child_idx) = c.revealed_index() {
1960 let (l, d) = Self::count_leaves_and_dirty(arena, child_idx);
1961 leaves += l;
1962 dirty += d;
1963 }
1964 }
1965 (leaves, dirty)
1966 }
1967 ArenaSparseNode::Extension { child, .. } => Self::count_leaves_and_dirty(arena, *child),
1968 _ => (0, 0),
1969 }
1970 }
1971
1972 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
1978 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
1979 fn debug_assert_subtrie_structure(&mut self) {
1980 let mut cursor = mem::take(&mut self.buffers.cursor);
1981 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
1982
1983 loop {
1984 let result = cursor.next(&mut self.upper_arena, |_, _| true);
1985 match result {
1986 NextResult::Done => break,
1987 NextResult::NonBranch | NextResult::Branch => {
1988 let path_len = cursor.head_path_len();
1989 let node = &self.upper_arena[cursor.head().expect("cursor is non-empty").index];
1990
1991 if Self::should_be_subtrie(path_len) {
1992 debug_assert!(
1993 matches!(
1994 node,
1995 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie
1996 ),
1997 "node at path_len={path_len} should be a Subtrie but is {node:?}",
1998 );
1999 } else {
2000 debug_assert!(
2001 !matches!(node, ArenaSparseNode::Subtrie(_)),
2002 "node at path_len={path_len} should NOT be a Subtrie but is",
2003 );
2004 }
2005 }
2006 }
2007 }
2008
2009 self.buffers.cursor = cursor;
2010 }
2011
2012 fn migrate_nodes(
2020 dst: &mut NodeArena,
2021 src: &mut NodeArena,
2022 src_idx: Index,
2023 dst_slot: Option<Index>,
2024 ) -> Index {
2025 let mut node = src.remove(src_idx).expect("node exists in source arena");
2026
2027 Self::migrate_children(dst, src, &mut node);
2029
2030 if let Some(slot) = dst_slot {
2031 dst[slot] = node;
2032 slot
2033 } else {
2034 dst.insert(node)
2035 }
2036 }
2037
2038 fn migrate_children(dst: &mut NodeArena, src: &mut NodeArena, node: &mut ArenaSparseNode) {
2042 match node {
2043 ArenaSparseNode::Extension { child, .. } => {
2044 *child = Self::migrate_nodes(dst, src, *child, None);
2045 }
2046 ArenaSparseNode::Branch(branch) => {
2047 for child in &mut branch.children {
2048 *child = match child.revealed_index() {
2049 Some(child_idx) => {
2050 BranchChild::revealed(Self::migrate_nodes(dst, src, child_idx, None))
2051 }
2052 None => dst.insert_blinded(src.take_blinded(*child)),
2053 };
2054 }
2055 }
2056 _ => {}
2057 }
2058 }
2059
2060 fn remove_pruned_node(arena: &mut NodeArena, cursor: &mut ArenaCursor) -> ArenaSparseNode {
2063 let path = cursor.head_path();
2065 let idx = cursor.head().expect("cursor is non-empty").index;
2066 let rlp_node = node_state_ref(arena, idx)
2067 .and_then(ArenaSparseNodeState::cached_rlp_node)
2068 .cloned()
2069 .expect("prune must run after hashing");
2070 cursor.pop(arena);
2071 let node = arena.remove(idx).expect("node must exist to be pruned");
2072 if let Some(child) = node.extension_child() {
2073 arena.remove(child).expect("extension child exists");
2074 }
2075 trace!(
2076 target: TRACE_TARGET,
2077 ?path,
2078 variant = %AsRef::<str>::as_ref(&node),
2079 cached_rlp_node = ?rlp_node,
2080 "pruning node",
2081 );
2082
2083 let parent_idx = cursor.head().expect("pruned child has parent").index;
2084 let child_nibble = path.last().expect("non-root child");
2085 let blinded = arena.insert_blinded(rlp_node);
2086 let parent_branch = branch_data_mut(arena, parent_idx);
2087 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
2088 .expect("child nibble not found in parent state_mask");
2089 parent_branch.children[child_idx] = blinded;
2090
2091 node
2092 }
2093
2094 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2103 fn reveal_node(
2104 arena: &mut NodeArena,
2105 cursor: &ArenaCursor,
2106 node: &mut ProofTrieNodeV2,
2107 find_result: SeekResult,
2108 ) -> Option<Index> {
2109 let SeekResult::Blinded = find_result else {
2110 return None;
2112 };
2113
2114 let head = cursor.head().expect("cursor is non-empty");
2115 let head_idx = head.index;
2116 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
2117
2118 debug_assert_eq!(
2119 node.path.len(),
2120 head_branch_logical_path.len() + 1,
2121 "proof node path {:?} is not a direct child of branch at {:?} (expected depth {})",
2122 node.path,
2123 head_branch_logical_path,
2124 head_branch_logical_path.len() + 1,
2125 );
2126
2127 let child_nibble = node.path.get_unchecked(head_branch_logical_path.len());
2128 let head_branch = branch_data(arena, head_idx);
2129 let dense_child_idx = BranchChildIdx::new(head_branch.state_mask, child_nibble)
2130 .expect("Blinded result but child nibble not in state_mask");
2131
2132 let child = head_branch.children[dense_child_idx];
2133 if !child.is_blinded() {
2134 return None;
2135 }
2136 let cached_rlp = arena.blinded(child).clone();
2137
2138 trace!(
2139 target: TRACE_TARGET,
2140 path = ?node.path,
2141 rlp_node = ?cached_rlp,
2142 "Revealing node",
2143 );
2144
2145 let proof_node = mem::replace(node, ProofTrieNodeV2::empty());
2146 let child_idx = Self::insert_proof_node(arena, proof_node);
2147 *node_state_mut(arena, child_idx) =
2148 ArenaSparseNodeState::Cached { rlp_node: cached_rlp, epoch: TrieNodeEpoch::UNMODIFIED };
2149 branch_data_mut(arena, head_idx).children[dense_child_idx] =
2150 BranchChild::revealed(child_idx);
2151 arena.take_blinded(child);
2152
2153 Some(child_idx)
2154 }
2155
2156 fn insert_proof_node(arena: &mut NodeArena, proof_node: ProofTrieNodeV2) -> Index {
2159 let ProofTrieNodeV2 { path, node, masks } = proof_node;
2160 let TrieNodeV2::Branch(branch) = node else {
2161 let node =
2162 ArenaSparseNode::from_proof_node(arena, ProofTrieNodeV2 { path, node, masks });
2163 return arena.insert(node);
2164 };
2165
2166 let children = branch.stack[..branch.state_mask.count_bits() as usize]
2167 .iter()
2168 .map(|rlp| arena.insert_blinded(rlp.clone()))
2169 .collect();
2170 let child = arena.insert(ArenaSparseNode::Branch(ArenaSparseNodeBranch {
2171 state: ArenaSparseNodeState::Revealed,
2172 children,
2173 state_mask: branch.state_mask,
2174 branch_masks: masks.unwrap_or_default(),
2175 }));
2176 if branch.key.is_empty() {
2177 child
2178 } else {
2179 arena.insert(ArenaSparseNode::Extension { key: branch.key, child })
2180 }
2181 }
2182
2183 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
2184 fn collect_reachable_nodes(
2185 arena: &NodeArena,
2186 idx: Index,
2187 reachable: &mut alloy_primitives::map::HashSet<Index>,
2188 ) {
2189 if !reachable.insert(idx) {
2190 return;
2191 }
2192 match &arena[idx] {
2193 ArenaSparseNode::Extension { child, .. } => {
2194 Self::collect_reachable_nodes(arena, *child, reachable);
2195 }
2196 ArenaSparseNode::Branch(b) => {
2197 for child in &b.children {
2198 if let Some(child_idx) = child.revealed_index() {
2199 Self::collect_reachable_nodes(arena, child_idx, reachable);
2200 }
2201 }
2202 }
2203 _ => {}
2204 }
2205 }
2206
2207 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
2208 fn assert_no_orphaned_nodes(arena: &NodeArena, root: Index, label: &str) {
2209 let mut reachable = alloy_primitives::map::HashSet::default();
2210 Self::collect_reachable_nodes(arena, root, &mut reachable);
2211 let all_indices: alloy_primitives::map::HashSet<Index> =
2212 arena.iter().map(|(idx, _)| idx).collect();
2213 let orphaned: Vec<_> = all_indices.difference(&reachable).collect();
2214 debug_assert!(
2215 orphaned.is_empty(),
2216 "{label} has {} orphaned node(s): {orphaned:?}",
2217 orphaned.len(),
2218 );
2219 }
2220}
2221
2222#[cfg(all(debug_assertions, not(feature = "fuzz")))]
2223impl Drop for ArenaParallelSparseTrie {
2224 fn drop(&mut self) {
2225 Self::assert_no_orphaned_nodes(&self.upper_arena, self.root, "upper arena");
2226
2227 for (_, node) in self.upper_arena.iter() {
2228 if let Some(subtrie) = node.as_subtrie() {
2229 Self::assert_no_orphaned_nodes(
2230 &subtrie.arena,
2231 subtrie.root,
2232 &alloc::format!("subtrie {:?}", subtrie.path),
2233 );
2234 }
2235 }
2236 }
2237}
2238
2239impl Default for ArenaParallelSparseTrie {
2240 fn default() -> Self {
2241 let mut upper_arena = NodeArena::new();
2242 let root = upper_arena
2243 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
2244 Self {
2245 upper_arena,
2246 root,
2247 buffers: ArenaTrieBuffers::default(),
2248 parallelism_thresholds: ArenaParallelismThresholds::default(),
2249 }
2250 }
2251}
2252
2253impl ArenaParallelSparseTrie {
2254 fn update_upper_subtrie(&mut self, head_idx: Index, new_epoch: TrieNodeEpoch) {
2256 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx] else {
2257 unreachable!()
2258 };
2259
2260 if !node_state_ref(&subtrie.arena, subtrie.root)
2261 .is_some_and(|state| matches!(state, ArenaSparseNodeState::Cached { .. }))
2262 {
2263 subtrie.update_cached_rlp(new_epoch);
2264 }
2265
2266 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
2267 }
2268}
2269
2270impl SparseTrie for ArenaParallelSparseTrie {
2271 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2272 fn set_root(
2273 &mut self,
2274 root: TrieNodeV2,
2275 masks: Option<BranchNodeMasks>,
2276 retain_updates: bool,
2277 ) -> SparseTrieResult<()> {
2278 debug_assert!(
2279 matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
2280 "set_root called on a trie that already has revealed nodes"
2281 );
2282
2283 match root {
2284 TrieNodeV2::EmptyRoot => {
2285 trace!(target: TRACE_TARGET, "Setting empty root");
2286 self.upper_arena[self.root] =
2287 ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed };
2288 }
2289 TrieNodeV2::Leaf(leaf) => {
2290 trace!(target: TRACE_TARGET, key = ?leaf.key, "Setting leaf root");
2291 self.upper_arena[self.root] = ArenaSparseNode::Leaf {
2292 state: ArenaSparseNodeState::Revealed,
2293 key: leaf.key,
2294 value: leaf.value,
2295 };
2296 }
2297 TrieNodeV2::Branch(branch) => {
2298 trace!(target: TRACE_TARGET, state_mask = ?branch.state_mask, num_children = branch.state_mask.count_bits(), "Setting branch root");
2299 let mut children = SmallVec::with_capacity(branch.state_mask.count_bits() as usize);
2300 for (stack_ptr, _nibble) in branch.state_mask.iter().enumerate() {
2301 let child = self.upper_arena.insert_blinded(branch.stack[stack_ptr].clone());
2302 children.push(child);
2303 }
2304
2305 let branch_node = ArenaSparseNode::Branch(ArenaSparseNodeBranch {
2306 state: ArenaSparseNodeState::Revealed,
2307 children,
2308 state_mask: branch.state_mask,
2309 branch_masks: masks.unwrap_or_default(),
2310 });
2311 if branch.key.is_empty() {
2312 self.upper_arena[self.root] = branch_node;
2313 } else {
2314 let child = self.upper_arena.insert(branch_node);
2315 self.upper_arena[self.root] =
2316 ArenaSparseNode::Extension { key: branch.key, child };
2317 }
2318 }
2319 TrieNodeV2::Extension(node) => {
2320 return Err(SparseTrieErrorKind::Reveal {
2321 path: Nibbles::new(),
2322 node: Box::new(node),
2323 }
2324 .into());
2325 }
2326 }
2327
2328 self.set_updates(retain_updates);
2329 Ok(())
2330 }
2331
2332 fn set_updates(&mut self, retain_updates: bool) {
2333 if retain_updates {
2334 self.buffers.updates.get_or_insert_with(SparseTrieUpdates::default).clear();
2335 } else {
2336 self.buffers.updates = None;
2337 }
2338 }
2339
2340 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(num_nodes = nodes.len()))]
2341 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
2342 if nodes.is_empty() {
2343 return Ok(());
2344 }
2345
2346 if matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot { .. }) {
2347 trace!(target: TRACE_TARGET, "Skipping reveal_nodes on empty root");
2348 return Ok(());
2349 }
2350
2351 nodes.sort_unstable_by_key(|n| n.path);
2353
2354 let threshold = self.parallelism_thresholds.min_revealed_nodes;
2355
2356 let mut cursor = mem::take(&mut self.buffers.cursor);
2358 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2359
2360 let mut node_idx = if nodes[0].path.is_empty() { 1 } else { 0 };
2362
2363 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, Vec<ProofTrieNodeV2>)> = Vec::new();
2367
2368 while node_idx < nodes.len() {
2369 let find_result = cursor.seek(&mut self.upper_arena, &nodes[node_idx].path);
2370
2371 match find_result {
2372 SeekResult::RevealedLeaf => {
2373 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, "Skipping reveal: leaf head");
2374 node_idx += 1;
2375 }
2376 SeekResult::Blinded => {
2377 let child_path = nodes[node_idx].path;
2379 let child_idx = Self::reveal_node(
2380 &mut self.upper_arena,
2381 &cursor,
2382 &mut nodes[node_idx],
2383 SeekResult::Blinded,
2384 );
2385 node_idx += 1;
2386
2387 if let Some(child_idx) = child_idx {
2388 self.maybe_wrap_in_subtrie(child_idx, &child_path);
2389 }
2390 }
2391 SeekResult::RevealedSubtrie => {
2392 let child_idx = cursor.head().expect("cursor is non-empty").index;
2393 let prefix = cursor.head_path();
2394
2395 let subtrie_start = node_idx;
2396 while node_idx < nodes.len() && nodes[node_idx].path.starts_with(&prefix) {
2397 node_idx += 1;
2398 }
2399 let num_subtrie_nodes = node_idx - subtrie_start;
2400
2401 if num_subtrie_nodes >= threshold {
2402 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Taking subtrie for parallel reveal");
2404 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2405 &mut self.upper_arena[child_idx],
2406 ArenaSparseNode::TakenSubtrie,
2407 ) else {
2408 unreachable!("RevealedSubtrie must point to a Subtrie node")
2409 };
2410 let node_vec: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2411 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2412 .collect();
2413 taken.push((child_idx, subtrie, node_vec));
2414 } else {
2415 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Revealing subtrie inline");
2417 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2418 else {
2419 unreachable!("RevealedSubtrie must point to a Subtrie node")
2420 };
2421 let mut subtrie_nodes: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2422 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2423 .collect();
2424 subtrie.reveal_nodes(&mut subtrie_nodes)?;
2425 }
2426 }
2427 _ => {
2428 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, ?find_result, "Skipping reveal: no blinded child");
2429 node_idx += 1;
2430 }
2431 }
2432 }
2433
2434 cursor.drain(&mut self.upper_arena);
2436 self.buffers.cursor = cursor;
2437
2438 if taken.is_empty() {
2439 return Ok(());
2440 }
2441
2442 if taken.len() == 1 {
2444 let (_, subtrie, node_vec) = &mut taken[0];
2445 subtrie.reveal_nodes(node_vec)?;
2446 } else {
2447 let parent_span = tracing::Span::current();
2448 let results: Vec<SparseTrieResult<()>> = taken
2449 .par_iter_mut()
2450 .map(|(_, subtrie, node_vec)| {
2451 let _guard = parent_span.enter();
2452 subtrie.reveal_nodes(node_vec)
2453 })
2454 .collect();
2455
2456 if let Some(err) = results.into_iter().find(|r| r.is_err()) {
2457 for (idx, subtrie, _) in taken {
2459 self.restore_taken_subtrie(idx, subtrie);
2460 }
2461 return err;
2462 }
2463 }
2464
2465 for (idx, subtrie, _) in taken {
2467 self.restore_taken_subtrie(idx, subtrie);
2468 }
2469
2470 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
2471 self.debug_assert_subtrie_structure();
2472
2473 Ok(())
2474 }
2475
2476 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, ret)]
2477 fn root(&mut self, new_epoch: TrieNodeEpoch) -> B256 {
2478 self.update_subtrie_hashes(new_epoch);
2479
2480 let rlp_node = Self::update_cached_rlp(
2481 &mut self.upper_arena,
2482 self.root,
2483 Nibbles::default(),
2484 &mut self.buffers,
2485 new_epoch,
2486 );
2487
2488 rlp_node.as_hash().expect("root RlpNode must be a hash")
2489 }
2490
2491 fn is_root_cached(&self) -> bool {
2492 matches!(
2493 node_state_ref(&self.upper_arena, self.root),
2494 Some(ArenaSparseNodeState::Cached { .. })
2495 )
2496 }
2497
2498 fn root_epoch(&self) -> Option<TrieNodeEpoch> {
2499 match node_state_ref(&self.upper_arena, self.root)? {
2500 ArenaSparseNodeState::Revealed => Some(TrieNodeEpoch::UNMODIFIED),
2501 ArenaSparseNodeState::Cached { epoch, .. } => Some(*epoch),
2502 ArenaSparseNodeState::Dirty => None,
2503 }
2504 }
2505
2506 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2507 fn update_subtrie_hashes(&mut self, new_epoch: TrieNodeEpoch) {
2508 trace!(target: TRACE_TARGET, "Updating subtrie hashes");
2509
2510 if branch_idx(&self.upper_arena, self.root).is_none() {
2512 return;
2513 }
2514
2515 let mut total_dirty_leaves: u64 = 0;
2517 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2518 for (idx, node) in self.upper_arena.iter_mut() {
2519 let ArenaSparseNode::Subtrie(s) = node else { continue };
2520 if s.num_dirty_leaves == 0 {
2521 continue;
2522 }
2523 total_dirty_leaves += s.num_dirty_leaves;
2524 let ArenaSparseNode::Subtrie(subtrie) =
2525 mem::replace(node, ArenaSparseNode::TakenSubtrie)
2526 else {
2527 unreachable!()
2528 };
2529 taken.push((idx, subtrie));
2530 }
2531
2532 if !taken.is_empty() {
2534 if taken.len() == 1 || total_dirty_leaves < self.parallelism_thresholds.min_dirty_leaves
2535 {
2536 for (_, subtrie) in &mut taken {
2537 subtrie.update_cached_rlp(new_epoch);
2538 }
2539 } else {
2540 let parent_span = tracing::Span::current();
2541 taken = taken
2542 .into_par_iter()
2543 .map(|(idx, mut subtrie)| {
2544 let _guard = parent_span.enter();
2545 subtrie.update_cached_rlp(new_epoch);
2546 (idx, subtrie)
2547 })
2548 .collect();
2549 }
2550 }
2551
2552 if taken.is_empty() && self.is_root_cached() {
2555 return;
2556 }
2557
2558 taken.sort_unstable_by_key(|(_, b)| Reverse(b.path));
2562
2563 self.buffers.cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2564
2565 loop {
2566 let result = self.buffers.cursor.next(&mut self.upper_arena, |_, child| match child {
2567 ArenaSparseNode::Branch(_) | ArenaSparseNode::Subtrie(_) => !child.is_cached(),
2568 ArenaSparseNode::Extension { .. } | ArenaSparseNode::TakenSubtrie => true,
2569 _ => false,
2570 });
2571
2572 match result {
2573 NextResult::Done => break,
2574 NextResult::Branch => continue,
2575 NextResult::NonBranch => {}
2576 }
2577
2578 let head_idx = self.buffers.cursor.head().expect("cursor is non-empty").index;
2580
2581 if matches!(&self.upper_arena[head_idx], ArenaSparseNode::TakenSubtrie) {
2582 let (_, subtrie) = taken.pop().expect("taken subtries must not be exhausted");
2583 debug_assert_eq!(
2584 subtrie.path,
2585 self.buffers.cursor.head_path(),
2586 "taken subtrie path mismatch",
2587 );
2588 self.upper_arena[head_idx] = ArenaSparseNode::Subtrie(subtrie);
2589 }
2590
2591 self.update_upper_subtrie(head_idx, new_epoch);
2592 }
2593 }
2594
2595 fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>> {
2596 Self::get_leaf_value_in_arena(&self.upper_arena, self.root, full_path, 0)
2597 }
2598
2599 fn find_leaf(
2600 &self,
2601 full_path: &Nibbles,
2602 expected_value: Option<&Vec<u8>>,
2603 ) -> Result<LeafLookup, LeafLookupError> {
2604 Self::find_leaf_in_arena(&self.upper_arena, self.root, full_path, 0, expected_value)
2605 }
2606
2607 fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates> {
2608 self.buffers
2609 .updates
2610 .as_ref()
2611 .map_or(Cow::Owned(SparseTrieUpdates::default()), Cow::Borrowed)
2612 }
2613
2614 fn take_updates(&mut self) -> SparseTrieUpdates {
2615 match self.buffers.updates.take() {
2616 Some(updates) => {
2617 self.buffers.updates = Some(SparseTrieUpdates::with_capacity(
2618 updates.updated_nodes.len(),
2619 updates.removed_nodes.len(),
2620 ));
2621 updates
2622 }
2623 None => SparseTrieUpdates::default(),
2624 }
2625 }
2626
2627 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2628 fn clear(&mut self) {
2629 self.upper_arena = NodeArena::new();
2630 self.root = self
2631 .upper_arena
2632 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
2633 self.buffers.clear();
2634 }
2635
2636 #[instrument(
2637 level = "trace",
2638 target = TRACE_TARGET,
2639 skip_all,
2640 fields(prune_before = prune_before.get()),
2641 )]
2642 fn prune(&mut self, prune_before: TrieNodeEpoch) -> usize {
2643 assert!(self.root_epoch().is_some(), "prune cannot run on a dirty trie");
2644
2645 if branch_idx(&self.upper_arena, self.root).is_none() {
2647 return 0;
2648 }
2649
2650 let threshold = self.parallelism_thresholds.min_leaves_for_prune;
2651
2652 let mut cursor = mem::take(&mut self.buffers.cursor);
2653 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2654
2655 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2657
2658 let mut pruned = 0;
2659
2660 loop {
2661 let result = cursor.next(&mut self.upper_arena, |_, child| {
2662 matches!(
2663 child,
2664 ArenaSparseNode::Branch(_) |
2665 ArenaSparseNode::Extension { .. } |
2666 ArenaSparseNode::Subtrie(_) |
2667 ArenaSparseNode::Leaf { .. }
2668 )
2669 });
2670
2671 if matches!(result, NextResult::Done) {
2672 break
2673 }
2674
2675 let head_idx = cursor.head().expect("cursor is non-empty").index;
2676
2677 match &self.upper_arena[head_idx] {
2678 ArenaSparseNode::Branch(_) |
2679 ArenaSparseNode::Extension { .. } |
2680 ArenaSparseNode::Leaf { .. } => {
2681 if cursor.depth() == 0 {
2683 continue;
2684 }
2685
2686 let node_epoch = node_state_ref(&self.upper_arena, head_idx)
2687 .and_then(ArenaSparseNodeState::cached_epoch)
2688 .expect("prune must run after hashing");
2689 if !node_epoch.should_prune(prune_before) {
2690 continue;
2691 }
2692
2693 let removed = Self::remove_pruned_node(&mut self.upper_arena, &mut cursor);
2694 pruned += 1 + usize::from(removed.extension_child().is_some());
2695 }
2696 ArenaSparseNode::Subtrie(_) => {
2697 let root_epoch = self.upper_arena[head_idx]
2698 .state_ref()
2699 .and_then(ArenaSparseNodeState::cached_epoch)
2700 .expect("prune must run after hashing");
2701 if root_epoch.should_prune(prune_before) {
2702 let removed = Self::remove_pruned_node(&mut self.upper_arena, &mut cursor);
2703 let ArenaSparseNode::Subtrie(s) = &removed else { unreachable!() };
2704 pruned += s.arena.len();
2705 self.recycle_subtrie(removed);
2706 continue;
2707 }
2708
2709 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[head_idx] else {
2710 unreachable!()
2711 };
2712 if subtrie.num_leaves >= threshold {
2713 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2714 &mut self.upper_arena[head_idx],
2715 ArenaSparseNode::TakenSubtrie,
2716 ) else {
2717 unreachable!()
2718 };
2719 taken.push((head_idx, subtrie));
2720 } else {
2721 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx]
2722 else {
2723 unreachable!()
2724 };
2725 pruned += subtrie.prune(prune_before);
2726 }
2727 }
2728 _ => unreachable!("prune walk must visit a subtrie, leaf, branch, or extension"),
2729 }
2730 }
2731
2732 self.buffers.cursor = cursor;
2733
2734 if !taken.is_empty() {
2735 if taken.len() == 1 {
2737 let (_, ref mut subtrie) = taken[0];
2738 pruned += subtrie.prune(prune_before);
2739 } else {
2740 let parent_span = tracing::Span::current();
2741 pruned += taken
2742 .par_iter_mut()
2743 .map(|(_, subtrie)| {
2744 let _guard = parent_span.enter();
2745 let _span = tracing::trace_span!(
2746 target: TRACE_TARGET,
2747 "subtrie_prune",
2748 subtrie = ?subtrie.path,
2749 )
2750 .entered();
2751
2752 subtrie.prune(prune_before)
2753 })
2754 .sum::<usize>();
2755 }
2756
2757 for (child_idx, subtrie) in taken {
2759 self.restore_taken_subtrie(child_idx, subtrie);
2760 }
2761 }
2762
2763 if pruned > 0 {
2764 compact_arena(&mut self.upper_arena, &mut self.root);
2765 }
2766
2767 pruned
2768 }
2769
2770 #[instrument(
2771 level = "trace",
2772 target = TRACE_TARGET,
2773 skip_all,
2774 fields(num_updates = updates.len()),
2775 )]
2776 fn update_leaves(
2777 &mut self,
2778 updates: &mut B256Map<LeafUpdate>,
2779 mut proof_required_fn: impl FnMut(B256, ProofV2TargetParent),
2780 ) -> SparseTrieResult<()> {
2781 if updates.is_empty() {
2782 return Ok(());
2783 }
2784
2785 let mut sorted: Vec<_> =
2787 updates.drain().map(|(key, update)| (key, Nibbles::unpack(key), update)).collect();
2788 sorted.sort_unstable_by_key(|entry| entry.1);
2789
2790 let threshold = self.parallelism_thresholds.min_updates;
2791 let parallelize_distributed_updates = sorted.len() >= threshold.saturating_mul(4);
2792
2793 let mut cursor = mem::take(&mut self.buffers.cursor);
2794 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2795
2796 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, core::ops::Range<usize>)> = Vec::new();
2798
2799 let mut update_idx = 0;
2800 while update_idx < sorted.len() {
2801 let (key, ref full_path, ref update) = sorted[update_idx];
2802
2803 let find_result = cursor.seek(&mut self.upper_arena, full_path);
2804
2805 match find_result {
2806 SeekResult::Blinded => {
2808 let logical_len = cursor.head_logical_branch_path_len(&self.upper_arena);
2809 let parent = ProofV2TargetParent::new(logical_len);
2810 trace!(target: TRACE_TARGET, ?key, ?parent, "Update hit blinded node, requesting proof");
2811 proof_required_fn(key, parent);
2812 updates.insert(key, update.clone());
2813 }
2814 SeekResult::RevealedSubtrie => {
2816 let child_idx = cursor.head().expect("cursor is non-empty").index;
2817 let subtrie_root_path = cursor.head_path();
2818
2819 let subtrie_start = update_idx;
2820 while update_idx < sorted.len() &&
2821 sorted[update_idx].1.starts_with(&subtrie_root_path)
2822 {
2823 update_idx += 1;
2824 }
2825
2826 let subtrie_updates = &sorted[subtrie_start..update_idx];
2827
2828 if let Some(proof) = Self::check_subtrie_collapse_needs_proof(
2832 &self.upper_arena,
2833 &cursor,
2834 subtrie_updates,
2835 ) {
2836 trace!(target: TRACE_TARGET, proof_key = ?proof.key, proof_parent = ?proof.parent, "Subtrie collapse would need blinded sibling, requesting proof");
2837 proof_required_fn(proof.key, proof.parent);
2838 for &(key, _, ref update) in subtrie_updates {
2839 updates.insert(key, update.clone());
2840 }
2841 continue;
2843 }
2844
2845 let num_subtrie_updates = update_idx - subtrie_start;
2846
2847 let all_removals = subtrie_updates
2851 .iter()
2852 .filter(|(_, _, u)| u.is_changed())
2856 .all(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()));
2857 let subtrie_num_leaves = match &self.upper_arena[child_idx] {
2858 ArenaSparseNode::Subtrie(s) => s.num_leaves,
2859 _ => 0,
2860 };
2861 let might_empty_subtrie =
2862 all_removals && num_subtrie_updates as u64 >= subtrie_num_leaves;
2863
2864 if (num_subtrie_updates >= threshold || parallelize_distributed_updates) &&
2865 !might_empty_subtrie
2866 {
2867 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Taking subtrie for parallel update");
2869 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2870 &mut self.upper_arena[child_idx],
2871 ArenaSparseNode::TakenSubtrie,
2872 ) else {
2873 unreachable!()
2874 };
2875 taken.push((child_idx, subtrie, subtrie_start..update_idx));
2876 } else {
2877 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Updating subtrie inline");
2879 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2880 else {
2881 unreachable!()
2882 };
2883
2884 subtrie.update_leaves(subtrie_updates);
2885
2886 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
2887 proof_required_fn(proof.key, proof.parent);
2888 let (key, _, ref update) = subtrie_updates[target_idx];
2889 updates.insert(key, update.clone());
2890 }
2891
2892 self.maybe_unwrap_subtrie(&mut cursor);
2894 }
2895
2896 continue;
2898 }
2899 find_result @ (SeekResult::EmptyRoot |
2901 SeekResult::RevealedLeaf |
2902 SeekResult::Diverged |
2903 SeekResult::NoChild { .. }) => match update {
2904 LeafUpdate::Changed(v) if !v.is_empty() => {
2905 let (result, _deltas) = Self::upsert_leaf(
2906 &mut self.upper_arena,
2907 &mut cursor,
2908 &mut self.root,
2909 full_path,
2910 v,
2911 find_result,
2912 );
2913 match result {
2914 UpsertLeafResult::NewChild => {
2915 if Self::should_be_subtrie(cursor.head_path_len()) {
2916 let head_idx =
2919 cursor.head().expect("cursor is non-empty").index;
2920 self.maybe_wrap_in_subtrie(head_idx, &cursor.head_path());
2921 } else {
2922 self.maybe_wrap_branch_children(&cursor);
2926 }
2927 }
2928 UpsertLeafResult::NewLeaf => {
2929 self.maybe_wrap_branch_children(&cursor);
2932 }
2933 UpsertLeafResult::Updated => {}
2934 }
2935 }
2936 LeafUpdate::Changed(_) => {
2937 let (result, _deltas) = Self::remove_leaf(
2938 &mut self.upper_arena,
2939 &mut cursor,
2940 &mut self.root,
2941 key,
2942 full_path,
2943 find_result,
2944 &mut self.buffers.updates,
2945 );
2946 match result {
2947 RemoveLeafResult::NeedsProof { key, proof_key, parent } => {
2948 proof_required_fn(proof_key, parent);
2949 let update =
2950 mem::replace(&mut sorted[update_idx].2, LeafUpdate::Touched);
2951 updates.insert(key, update);
2952 }
2953 RemoveLeafResult::Removed => {
2954 self.maybe_collapse_or_remove_branch(&mut cursor);
2963 let head_idx = cursor
2964 .head()
2965 .expect("cursor always has root after collapse")
2966 .index;
2967 self.maybe_wrap_in_subtrie(head_idx, &cursor.head_path());
2968 }
2969 RemoveLeafResult::NotFound => {}
2970 }
2971 }
2972 LeafUpdate::Touched => {}
2973 },
2974 }
2975
2976 update_idx += 1;
2977 }
2978
2979 cursor.drain(&mut self.upper_arena);
2981 self.buffers.cursor = cursor;
2982
2983 if taken.is_empty() {
2984 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
2985 self.debug_assert_subtrie_structure();
2986
2987 return Ok(());
2988 }
2989
2990 if taken.len() == 1 {
2992 let (_, ref mut subtrie, ref range) = taken[0];
2993 subtrie.update_leaves(&sorted[range.clone()]);
2994 } else {
2995 let parent_span = tracing::Span::current();
2996 taken.par_iter_mut().for_each(|(_, subtrie, range)| {
2997 let _guard = parent_span.enter();
2998 subtrie.update_leaves(&sorted[range.clone()]);
2999 });
3000 }
3001
3002 let taken_paths: Vec<Nibbles> = taken.iter().map(|(_, s, _)| s.path).collect();
3005 for (child_idx, mut subtrie, range) in taken {
3006 let subtrie_updates = &sorted[range];
3007 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
3008 proof_required_fn(proof.key, proof.parent);
3009 let (key, _, ref update) = subtrie_updates[target_idx];
3010 updates.insert(key, update.clone());
3011 }
3012
3013 self.restore_taken_subtrie(child_idx, subtrie);
3015 }
3016
3017 {
3023 let mut cursor = mem::take(&mut self.buffers.cursor);
3024 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
3025
3026 for path in &taken_paths {
3027 let find_result = cursor.seek(&mut self.upper_arena, path);
3028 match find_result {
3029 SeekResult::RevealedSubtrie => {
3030 debug_assert!(
3031 {
3032 let head_idx = cursor.head().expect("cursor is non-empty").index;
3033 !matches!(
3034 &self.upper_arena[head_idx],
3035 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot { .. })
3036 )
3037 },
3038 "taken subtrie became EmptyRoot — should have been forced inline"
3039 );
3040
3041 cursor.pop(&mut self.upper_arena);
3042
3043 self.maybe_collapse_or_remove_branch(&mut cursor);
3047 }
3048 _ => {
3049 }
3052 }
3053 }
3054
3055 cursor.drain(&mut self.upper_arena);
3056 self.buffers.cursor = cursor;
3057 }
3058
3059 #[cfg(all(debug_assertions, not(feature = "fuzz")))]
3060 self.debug_assert_subtrie_structure();
3061
3062 Ok(())
3063 }
3064}
3065
3066#[cfg(test)]
3067mod tests {
3068 use super::*;
3069 use crate::{
3070 ArenaParallelSparseTrie, ArenaParallelismThresholds, LeafUpdate, SparseTrie, TrieNodeEpoch,
3071 };
3072 use alloy_primitives::{map::B256Map, B256, U256};
3073 use rand::{seq::SliceRandom, Rng, SeedableRng};
3074 use reth_trie::test_utils::TrieTestHarness;
3075 use reth_trie_common::ProofV2Target;
3076 use std::collections::BTreeMap;
3077 use tracing::{info, trace};
3078
3079 const fn epoch(value: u64) -> TrieNodeEpoch {
3080 TrieNodeEpoch::new(value)
3081 }
3082
3083 #[test]
3084 fn unsupported_root_preserves_update_retention_and_allows_valid_root() {
3085 let mut trie = ArenaParallelSparseTrie::default();
3086 let root = TrieNodeV2::Extension(reth_trie_common::ExtensionNode::new(
3087 Nibbles::from_nibbles([1]),
3088 RlpNode::word_rlp(&B256::ZERO),
3089 ));
3090 let error = trie.set_root(root, None, true).unwrap_err();
3091 assert!(
3092 matches!(error.kind(), SparseTrieErrorKind::Reveal { path, .. } if path.is_empty())
3093 );
3094 assert!(trie.buffers.updates.is_none());
3095
3096 trie.set_root(TrieNodeV2::EmptyRoot, None, true).unwrap();
3097 assert_eq!(trie.root(epoch(1)), EMPTY_ROOT_HASH);
3098 assert!(trie.buffers.updates.is_some());
3099 }
3100
3101 #[test]
3102 fn pruning_pops_cursor_before_reusing_node_slot() {
3103 use super::{
3104 ArenaCursor, ArenaSparseNode, ArenaSparseNodeBranch, ArenaSparseNodeState, BranchChild,
3105 NextResult, NodeArena,
3106 };
3107 use reth_trie_common::RlpNode;
3108
3109 let mut arena = NodeArena::new();
3110 let rlp = RlpNode::word_rlp(&B256::repeat_byte(1));
3111 let state = ArenaSparseNodeState::Cached { rlp_node: rlp.clone(), epoch: epoch(0) };
3112 let leaf =
3113 ArenaSparseNode::Leaf { state: state.clone(), key: Default::default(), value: vec![1] };
3114 let first = arena.insert(leaf.clone());
3115 let second = arena.insert(leaf);
3116 let root = arena.insert(ArenaSparseNode::Branch(ArenaSparseNodeBranch {
3117 state: state.clone(),
3118 children: [BranchChild::revealed(first), BranchChild::revealed(second)]
3119 .into_iter()
3120 .collect(),
3121 state_mask: alloy_trie::TrieMask::new(3),
3122 branch_masks: Default::default(),
3123 }));
3124 let mut cursor = ArenaCursor::default();
3125 cursor.reset(&arena, root, Default::default());
3126 assert!(matches!(cursor.next(&mut arena, |_, _| true), NextResult::NonBranch));
3127 assert_eq!(cursor.head().unwrap().index, first);
3128
3129 ArenaParallelSparseTrie::remove_pruned_node(&mut arena, &mut cursor);
3130 assert_eq!(cursor.head().unwrap().index, root);
3131 let reused = arena.insert(ArenaSparseNode::Leaf {
3132 state: ArenaSparseNodeState::Dirty,
3133 key: Default::default(),
3134 value: vec![2],
3135 });
3136 assert_eq!(reused, first);
3137
3138 assert!(matches!(cursor.next(&mut arena, |_, _| true), NextResult::NonBranch));
3139 assert_eq!(cursor.head().unwrap().index, second);
3140 assert!(matches!(cursor.next(&mut arena, |_, _| true), NextResult::Branch));
3141 assert_eq!(arena[root].state_ref(), Some(&state));
3142 assert_eq!(arena.blinded(arena[root].branch_ref().children[0]), &rlp);
3143 assert!(matches!(cursor.next(&mut arena, |_, _| true), NextResult::Done));
3144 }
3145
3146 struct ArenaTrieTestHarness {
3151 inner: TrieTestHarness,
3153 }
3154
3155 impl std::ops::Deref for ArenaTrieTestHarness {
3156 type Target = TrieTestHarness;
3157 fn deref(&self) -> &Self::Target {
3158 &self.inner
3159 }
3160 }
3161
3162 impl std::ops::DerefMut for ArenaTrieTestHarness {
3163 fn deref_mut(&mut self) -> &mut Self::Target {
3164 &mut self.inner
3165 }
3166 }
3167
3168 impl ArenaTrieTestHarness {
3169 fn new(storage: BTreeMap<B256, U256>) -> Self {
3171 Self { inner: TrieTestHarness::new(storage) }
3172 }
3173
3174 fn assert_changes(
3178 &self,
3179 apst: &mut ArenaParallelSparseTrie,
3180 changes: BTreeMap<B256, U256>,
3181 ) {
3182 let (expected_root, mut expected_trie_updates) = if changes.is_empty() {
3184 (self.original_root(), Default::default())
3185 } else {
3186 self.get_root_with_updates(&changes)
3187 };
3188
3189 self.minimize_trie_updates(&mut expected_trie_updates);
3190
3191 let mut leaf_updates: B256Map<LeafUpdate> = changes
3194 .iter()
3195 .map(|(&slot, &value)| {
3196 let rlp_value = if value.is_zero() {
3197 Vec::new()
3198 } else {
3199 alloy_rlp::encode_fixed_size(&value).to_vec()
3200 };
3201 (slot, LeafUpdate::Changed(rlp_value))
3202 })
3203 .collect();
3204
3205 loop {
3208 let mut targets: Vec<ProofV2Target> = Vec::new();
3209 apst.update_leaves(&mut leaf_updates, |key, parent| {
3210 targets.push(ProofV2Target::new(key).with_parent(parent));
3211 })
3212 .expect("update_leaves should succeed");
3213
3214 if targets.is_empty() {
3215 break;
3216 }
3217
3218 let (mut proof_nodes, _) = self.proof_v2(&mut targets);
3219 apst.reveal_nodes(&mut proof_nodes).expect("reveal_nodes should succeed");
3220 }
3221
3222 let actual_root = apst.root(epoch(0));
3224 let mut actual_updates = apst.take_updates();
3225
3226 actual_updates.updated_nodes.retain(|path, node| {
3229 self.storage_trie_updates().storage_nodes.get(path) != Some(node)
3230 });
3231 actual_updates
3232 .removed_nodes
3233 .retain(|path| self.storage_trie_updates().storage_nodes.contains_key(path));
3234
3235 let mut expected_updated_nodes =
3236 expected_trie_updates.storage_nodes.into_iter().collect::<Vec<_>>();
3237 let mut actual_updated_nodes =
3238 actual_updates.updated_nodes.into_iter().collect::<Vec<_>>();
3239 expected_updated_nodes.sort();
3240 actual_updated_nodes.sort();
3241 pretty_assertions::assert_eq!(
3242 expected_updated_nodes,
3243 actual_updated_nodes,
3244 "updated nodes mismatch"
3245 );
3246
3247 let mut expected_removed_nodes =
3248 expected_trie_updates.removed_nodes.into_iter().collect::<Vec<_>>();
3249 let mut actual_removed_nodes =
3250 actual_updates.removed_nodes.into_iter().collect::<Vec<_>>();
3251 expected_removed_nodes.sort();
3252 actual_removed_nodes.sort();
3253 pretty_assertions::assert_eq!(
3254 expected_removed_nodes,
3255 actual_removed_nodes,
3256 "removed nodes mismatch"
3257 );
3258 assert_eq!(expected_root, actual_root, "storage root mismatch");
3259 }
3260 }
3261
3262 use proptest::prelude::*;
3263 use proptest_arbitrary_interop::arb;
3264
3265 fn build_changeset(
3272 base: &BTreeMap<B256, U256>,
3273 new_keys: BTreeMap<B256, U256>,
3274 overlap_pct: f64,
3275 delete_pct: f64,
3276 rng: &mut rand::rngs::StdRng,
3277 ) -> BTreeMap<B256, U256> {
3278 let num_overlap = (base.len() as f64 * overlap_pct) as usize;
3279 let num_delete = (num_overlap as f64 * delete_pct) as usize;
3280
3281 let mut all_keys: Vec<B256> = base.keys().copied().collect();
3282 all_keys.shuffle(rng);
3283 let overlap_keys = &all_keys[..num_overlap];
3284
3285 let mut changeset = new_keys;
3286 for (i, &key) in overlap_keys.iter().enumerate() {
3287 let value =
3288 if i < num_delete { U256::ZERO } else { U256::from(rng.random::<u64>() | 1) };
3289 changeset.entry(key).or_insert(value);
3290 }
3291 changeset
3292 }
3293
3294 proptest! {
3295 #![proptest_config(ProptestConfig::with_cases(1000))]
3296 #[test]
3297 fn arena_trie_proptest(
3298 initial in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=100usize),
3299 changeset1_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3300 changeset2_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3301 overlap_pct in 0.0..=0.5f64,
3302 delete_pct in 0.0..=0.33f64, shuffle_seed in arb::<u64>(),
3304 ) {
3305 reth_tracing::init_test_tracing();
3306 info!(target: TRACE_TARGET, ?shuffle_seed, "PROPTEST START");
3307
3308 let initial: BTreeMap<B256, U256> = initial.into_iter()
3310 .filter(|(_, v)| !v.is_zero())
3311 .collect();
3312
3313 let mut rng = rand::rngs::StdRng::seed_from_u64(shuffle_seed);
3314
3315 let changeset1 = build_changeset(&initial, changeset1_new_keys, overlap_pct, delete_pct, &mut rng);
3316 for (i, (k, v)) in changeset1.iter().enumerate() {
3317 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 1 entry");
3318 }
3319
3320 let mut harness = ArenaTrieTestHarness::new(initial);
3321
3322 let root_node = harness.root_node();
3324 let mut apst = ArenaParallelSparseTrie::default().with_parallelism_thresholds(
3325 ArenaParallelismThresholds {
3326 min_dirty_leaves: 3,
3327 min_revealed_nodes: 3,
3328 min_updates: 3,
3329 min_leaves_for_prune: 3,
3330 },
3331 );
3332 apst.set_root(root_node.node, root_node.masks, true).expect("set_root should succeed");
3333
3334 harness.assert_changes(&mut apst, changeset1.clone());
3335
3336 harness.apply_changeset(changeset1);
3338
3339 apst.prune(epoch(1));
3342
3343 let changeset2 = build_changeset(harness.storage(), changeset2_new_keys, overlap_pct, delete_pct, &mut rng);
3344 for (i, (k, v)) in changeset2.iter().enumerate() {
3345 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 2 entry");
3346 }
3347
3348 harness.assert_changes(&mut apst, changeset2);
3349 }
3350 }
3351}