1mod branch_child_idx;
2mod cursor;
3mod nodes;
4
5use branch_child_idx::{BranchChildIdx, BranchChildIter};
6use cursor::{ArenaCursor, NextResult, SeekResult};
7use nodes::{
8 ArenaSparseNode, ArenaSparseNodeBranch, ArenaSparseNodeBranchChild, ArenaSparseNodeState,
9};
10
11use crate::{
12 LeafLookup, LeafLookupError, LeafUpdate, SparseTrie, SparseTrieUpdates, TrieNodeEpoch,
13};
14use alloc::{borrow::Cow, boxed::Box, collections::VecDeque, vec::Vec};
15use alloy_primitives::{keccak256, map::B256Map, B256};
16use alloy_trie::TrieMask;
17use core::{cmp::Reverse, mem};
18use reth_execution_errors::SparseTrieResult;
19use reth_trie_common::{
20 BranchNodeMasks, BranchNodeRef, ExtensionNodeRef, LeafNodeRef, Nibbles, ProofTrieNodeV2,
21 ProofV2TargetParent, RlpNode, TrieNodeV2, EMPTY_ROOT_HASH,
22};
23use slotmap::{DefaultKey, SlotMap};
24use smallvec::SmallVec;
25use tracing::{instrument, trace};
26
27#[cfg(feature = "trie-debug")]
28use crate::debug_recorder::{LeafUpdateRecord, ProofTrieNodeRecord, RecordedOp, TrieDebugRecorder};
29
30type Index = DefaultKey;
32type NodeArena = SlotMap<Index, ArenaSparseNode>;
34
35const TRACE_TARGET: &str = "trie::arena";
36
37const UPPER_TRIE_MAX_DEPTH: usize = 2;
40
41fn compact_arena(arena: &mut NodeArena, root: &mut Index) {
45 let mut new_arena = SlotMap::with_capacity(arena.len());
46 let mut queue = VecDeque::new();
47
48 let root_node = arena.remove(*root).expect("root exists");
49 let new_root = new_arena.insert(root_node);
50 queue.push_back(new_root);
51
52 while let Some(new_idx) = queue.pop_front() {
53 let old_children: SmallVec<[(usize, Index); 16]> = match &new_arena[new_idx] {
58 ArenaSparseNode::Branch(b) => b
59 .children
60 .iter()
61 .enumerate()
62 .filter_map(|(i, c)| match c {
63 ArenaSparseNodeBranchChild::Revealed(old_idx) => Some((i, *old_idx)),
64 _ => None,
65 })
66 .collect(),
67 _ => continue,
68 };
69
70 for (child_pos, old_child_idx) in old_children {
71 let child_node = arena.remove(old_child_idx).expect("child exists");
72 let new_child_idx = new_arena.insert(child_node);
73 let ArenaSparseNode::Branch(b) = &mut new_arena[new_idx] else { unreachable!() };
74 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
75 queue.push_back(new_child_idx);
76 }
77 }
78
79 debug_assert!(
80 arena.is_empty(),
81 "compact_arena: {} orphaned nodes remaining after BFS drain",
82 arena.len(),
83 );
84
85 *arena = new_arena;
86 *root = new_root;
87}
88
89#[derive(Debug, Default, Clone)]
92struct ArenaTrieBuffers {
93 cursor: ArenaCursor,
95 updates: Option<SparseTrieUpdates>,
98 rlp_buf: Vec<u8>,
100 rlp_node_buf: Vec<RlpNode>,
102}
103
104impl ArenaTrieBuffers {
105 fn clear(&mut self) {
106 if let Some(updates) = self.updates.as_mut() {
107 updates.clear();
108 }
109 self.rlp_buf.clear();
110 self.rlp_node_buf.clear();
111 }
112}
113
114#[derive(Debug, Clone)]
118struct ArenaSparseSubtrie {
119 arena: NodeArena,
121 root: Index,
123 path: Nibbles,
125 buffers: ArenaTrieBuffers,
127 required_proofs: Vec<(usize, ArenaRequiredProof)>,
131 num_leaves: u64,
133 num_dirty_leaves: u64,
135}
136
137impl ArenaSparseSubtrie {
138 fn new(record_updates: bool) -> Box<Self> {
142 let mut arena = SlotMap::new();
143 let root =
144 arena.insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
145 let buffers = ArenaTrieBuffers {
146 updates: record_updates.then(SparseTrieUpdates::default),
147 ..Default::default()
148 };
149 Box::new(Self {
150 arena,
151 root,
152 path: Nibbles::default(),
153 buffers,
154 required_proofs: Vec::new(),
155 num_leaves: 0,
156 num_dirty_leaves: 0,
157 })
158 }
159
160 #[cfg(debug_assertions)]
162 fn debug_assert_counters(&self) {
163 let (actual_leaves, actual_dirty) =
164 ArenaParallelSparseTrie::count_leaves_and_dirty(&self.arena, self.root);
165 debug_assert_eq!(
166 self.num_leaves, actual_leaves,
167 "subtrie {:?} num_leaves mismatch: stored {} vs actual {}",
168 self.path, self.num_leaves, actual_leaves,
169 );
170 debug_assert_eq!(
171 self.num_dirty_leaves, actual_dirty,
172 "subtrie {:?} num_dirty_leaves mismatch: stored {} vs actual {}",
173 self.path, self.num_dirty_leaves, actual_dirty,
174 );
175 }
176
177 fn prune(&mut self, prune_before: TrieNodeEpoch) -> usize {
182 if !matches!(&self.arena[self.root], ArenaSparseNode::Branch(_)) {
184 return 0;
185 }
186
187 debug_assert_eq!(self.num_dirty_leaves, 0, "prune must run after hashing");
188
189 if prune_before == TrieNodeEpoch::UNMODIFIED {
190 return 0;
191 }
192
193 let old_count = self.arena.len();
194 let mut new_arena = SlotMap::new();
196 let mut new_num_leaves = 0u64;
197
198 let root_node = self.arena.remove(self.root).expect("root exists");
200 let new_root = new_arena.insert(root_node);
201 let mut stack = Vec::new();
202 if let Some(frame) =
203 prepare_retained_node(&new_arena, new_root, self.path, &mut new_num_leaves)
204 {
205 stack.push(frame);
206 }
207
208 while let Some(frame) = stack.last_mut() {
209 let Some((child_pos, nibble, old_child_idx)) = frame.next_revealed_child(&new_arena)
210 else {
211 stack.pop();
212 continue;
213 };
214
215 let parent_new_idx = frame.new_idx;
216 let mut child_path = frame.branch_logical_path;
217 child_path.push(nibble);
218
219 let child_epoch = self.arena[old_child_idx]
220 .state_ref()
221 .and_then(ArenaSparseNodeState::cached_epoch)
222 .expect("prune must run after hashing");
223
224 if child_epoch.should_prune(prune_before) {
225 let node = &self.arena[old_child_idx];
226 let rlp_node = node
227 .state_ref()
228 .and_then(ArenaSparseNodeState::cached_rlp_node)
229 .cloned()
230 .expect("prune must run after hashing");
231 trace!(
232 target: TRACE_TARGET,
233 path = ?child_path,
234 variant = %AsRef::<str>::as_ref(node),
235 cached_rlp_node = ?rlp_node,
236 "pruning node",
237 );
238 let ArenaSparseNode::Branch(b) = &mut new_arena[parent_new_idx] else {
239 unreachable!()
240 };
241 b.children[child_pos] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
242 } else {
243 let child_node = self.arena.remove(old_child_idx).expect("child exists");
244 let new_child_idx = new_arena.insert(child_node);
245 if let Some(frame) = prepare_retained_node(
246 &new_arena,
247 new_child_idx,
248 child_path,
249 &mut new_num_leaves,
250 ) {
251 stack.push(frame);
252 }
253 let ArenaSparseNode::Branch(b) = &mut new_arena[parent_new_idx] else {
254 unreachable!()
255 };
256 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
257 }
258 }
259
260 let pruned = old_count - new_arena.len();
261 self.num_leaves = new_num_leaves;
262 self.num_dirty_leaves = 0;
263 self.arena = new_arena;
264 self.root = new_root;
265
266 #[cfg(debug_assertions)]
267 self.debug_assert_counters();
268 return pruned;
269
270 struct CopyFrame {
271 new_idx: Index,
272 branch_logical_path: Nibbles,
273 state_mask: TrieMask,
274 remaining_child_mask: TrieMask,
275 }
276
277 impl CopyFrame {
278 fn next_revealed_child(&mut self, new_arena: &NodeArena) -> Option<(usize, u8, Index)> {
279 let ArenaSparseNode::Branch(b) = &new_arena[self.new_idx] else { unreachable!() };
280
281 loop {
282 let nibble = self.remaining_child_mask.first_set_bit_index()?;
283 self.remaining_child_mask.unset_bit(nibble);
284 let child_idx = BranchChildIdx::new(self.state_mask, nibble)
285 .expect("remaining_child_mask must be a subset of state_mask");
286
287 if let ArenaSparseNodeBranchChild::Revealed(old_idx) = b.children[child_idx] {
288 return Some((child_idx.get(), nibble, old_idx))
289 }
290 }
291 }
292 }
293
294 fn prepare_retained_node(
297 new_arena: &NodeArena,
298 new_idx: Index,
299 node_path: Nibbles,
300 new_num_leaves: &mut u64,
301 ) -> Option<CopyFrame> {
302 let ArenaSparseNode::Branch(b) = &new_arena[new_idx] else {
303 if matches!(&new_arena[new_idx], ArenaSparseNode::Leaf { .. }) {
304 *new_num_leaves += 1;
305 }
306 return None;
307 };
308
309 let mut branch_logical_path = node_path;
310 branch_logical_path.extend(&b.short_key);
311
312 Some(CopyFrame {
313 new_idx,
314 branch_logical_path,
315 state_mask: b.state_mask,
316 remaining_child_mask: b.state_mask,
317 })
318 }
319 }
320
321 #[instrument(
329 level = "trace",
330 target = TRACE_TARGET,
331 skip_all,
332 fields(
333 subtrie = ?self.path,
334 num_updates = sorted_updates.len(),
335 ),
336 )]
337 fn update_leaves(&mut self, sorted_updates: &[(B256, Nibbles, LeafUpdate)]) {
338 if sorted_updates.is_empty() {
339 return;
340 }
341 trace!(target: TRACE_TARGET, "Subtrie update_leaves");
342
343 debug_assert!(
344 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
345 "subtrie root must not be EmptyRoot at start of update_leaves"
346 );
347
348 self.buffers.cursor.reset(&self.arena, self.root, self.path);
349
350 for (idx, &(key, ref full_path, ref update)) in sorted_updates.iter().enumerate() {
351 let find_result = self.buffers.cursor.seek(&mut self.arena, full_path);
352
353 if matches!(find_result, SeekResult::Blinded) {
355 let logical_len = self.buffers.cursor.head_logical_branch_path_len(&self.arena);
356 self.required_proofs.push((
357 idx,
358 ArenaRequiredProof { key, parent: ProofV2TargetParent::new(logical_len) },
359 ));
360 continue;
361 }
362
363 match update {
364 LeafUpdate::Changed(value) if !value.is_empty() => {
365 let (_result, deltas) = ArenaParallelSparseTrie::upsert_leaf(
367 &mut self.arena,
368 &mut self.buffers.cursor,
369 &mut self.root,
370 full_path,
371 value,
372 find_result,
373 );
374 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
375 self.num_dirty_leaves =
376 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
377 }
378 LeafUpdate::Changed(_) => {
379 let (result, deltas) = ArenaParallelSparseTrie::remove_leaf(
380 &mut self.arena,
381 &mut self.buffers.cursor,
382 &mut self.root,
383 key,
384 full_path,
385 find_result,
386 &mut self.buffers.updates,
387 );
388 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
389 self.num_dirty_leaves =
390 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
391
392 if let RemoveLeafResult::NeedsProof { key, proof_key, parent } = result {
393 self.required_proofs
394 .push((idx, ArenaRequiredProof { key: proof_key, parent }));
395 self.required_proofs.push((idx, ArenaRequiredProof { key, parent }));
396 }
397 }
398 LeafUpdate::Touched => {}
399 }
400 }
401
402 self.buffers.cursor.drain(&mut self.arena);
404
405 #[cfg(debug_assertions)]
406 self.debug_assert_counters();
407 }
408
409 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
412 if nodes.is_empty() {
413 return Ok(());
414 }
415 trace!(target: TRACE_TARGET, path = ?self.path, num_nodes = nodes.len(), "Subtrie reveal_nodes");
416
417 debug_assert!(
418 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
419 "subtrie root must not be EmptyRoot in reveal_nodes"
420 );
421
422 self.buffers.cursor.reset(&self.arena, self.root, self.path);
423
424 for node in nodes.iter_mut() {
425 let find_result = self.buffers.cursor.seek(&mut self.arena, &node.path);
426 if ArenaParallelSparseTrie::reveal_node(
427 &mut self.arena,
428 &self.buffers.cursor,
429 node,
430 find_result,
431 )
432 .is_some_and(|child_idx| matches!(self.arena[child_idx], ArenaSparseNode::Leaf { .. }))
433 {
434 self.num_leaves += 1;
435 }
436 }
437
438 self.buffers.cursor.drain(&mut self.arena);
440
441 #[cfg(debug_assertions)]
442 self.debug_assert_counters();
443
444 Ok(())
445 }
446
447 fn update_cached_rlp(&mut self, new_epoch: TrieNodeEpoch) {
452 ArenaParallelSparseTrie::update_cached_rlp(
453 &mut self.arena,
454 self.root,
455 self.path,
456 &mut self.buffers,
457 new_epoch,
458 );
459 self.num_dirty_leaves = 0;
460 #[cfg(debug_assertions)]
461 self.debug_assert_counters();
462 }
463}
464
465#[derive(Debug, Default)]
469struct SubtrieCounterDeltas {
470 num_leaves_delta: i64,
471 num_dirty_leaves_delta: i64,
472}
473
474#[derive(Debug)]
477enum UpsertLeafResult {
478 Updated,
480 NewLeaf,
482 NewChild,
485}
486
487#[derive(Debug)]
490enum RemoveLeafResult {
491 Removed,
493 NotFound,
495 NeedsProof { key: B256, proof_key: B256, parent: ProofV2TargetParent },
498}
499
500#[derive(Debug, Clone)]
502struct ArenaRequiredProof {
503 key: B256,
505 parent: ProofV2TargetParent,
507}
508
509#[derive(Debug, Clone, Copy, PartialEq, Eq)]
514pub struct ArenaParallelismThresholds {
515 pub min_dirty_leaves: u64,
519 pub min_revealed_nodes: usize,
523 pub min_updates: usize,
527 pub min_leaves_for_prune: u64,
531}
532
533impl Default for ArenaParallelismThresholds {
534 fn default() -> Self {
535 Self {
536 min_dirty_leaves: 64,
537 min_revealed_nodes: 16,
538 min_updates: 128,
539 min_leaves_for_prune: 128,
540 }
541 }
542}
543
544#[derive(Debug, Clone)]
601pub struct ArenaParallelSparseTrie {
602 upper_arena: NodeArena,
604 root: Index,
606 buffers: ArenaTrieBuffers,
608 parallelism_thresholds: ArenaParallelismThresholds,
610 #[cfg(feature = "trie-debug")]
612 debug_recorder: TrieDebugRecorder,
613}
614
615impl ArenaParallelSparseTrie {
616 pub const fn with_parallelism_thresholds(
618 mut self,
619 thresholds: ArenaParallelismThresholds,
620 ) -> Self {
621 self.parallelism_thresholds = thresholds;
622 self
623 }
624
625 #[cfg(feature = "trie-debug")]
631 fn record_initial_state(&mut self) {
632 use crate::debug_recorder::{NodeStateRecord, TrieNodeRecord};
633 use alloy_primitives::hex;
634 use alloy_trie::nodes::{BranchNode, TrieNode};
635
636 fn state_to_record(state: &ArenaSparseNodeState) -> NodeStateRecord {
637 match state {
638 ArenaSparseNodeState::Revealed => NodeStateRecord::Revealed,
639 ArenaSparseNodeState::Cached { rlp_node, .. } => {
640 NodeStateRecord::Cached { rlp_node: hex::encode(rlp_node.as_ref()) }
641 }
642 ArenaSparseNodeState::Dirty => NodeStateRecord::Dirty,
643 }
644 }
645
646 fn node_to_record(
650 arena: &NodeArena,
651 idx: Index,
652 path: Nibbles,
653 ) -> Option<ProofTrieNodeRecord> {
654 match &arena[idx] {
655 ArenaSparseNode::EmptyRoot { .. } => Some(ProofTrieNodeRecord {
656 path,
657 node: TrieNodeRecord(TrieNode::EmptyRoot),
658 masks: None,
659 short_key: None,
660 state: None,
661 }),
662 ArenaSparseNode::Branch(b) => {
663 let stack = b
664 .children
665 .iter()
666 .map(|child| match child {
667 ArenaSparseNodeBranchChild::Blinded(rlp) => rlp.clone(),
668 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
669 arena[*child_idx]
671 .state_ref()
672 .and_then(|s| s.cached_rlp_node())
673 .cloned()
674 .unwrap_or_default()
675 }
676 })
677 .collect();
678 Some(ProofTrieNodeRecord {
679 path,
680 node: TrieNodeRecord(TrieNode::Branch(BranchNode::new(
681 stack,
682 b.state_mask,
683 ))),
684 masks: Some((
685 b.branch_masks.hash_mask.get(),
686 b.branch_masks.tree_mask.get(),
687 )),
688 short_key: (!b.short_key.is_empty()).then_some(b.short_key),
689 state: Some(state_to_record(&b.state)),
690 })
691 }
692 ArenaSparseNode::Leaf { key, value, state, .. } => Some(ProofTrieNodeRecord {
693 path,
694 node: TrieNodeRecord(TrieNode::Leaf(alloy_trie::nodes::LeafNode::new(
695 *key,
696 value.clone(),
697 ))),
698 masks: None,
699 short_key: None,
700 state: Some(state_to_record(state)),
701 }),
702 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie => None,
703 }
704 }
705
706 fn collect_records(
708 arena: &mut NodeArena,
709 root: Index,
710 root_path: Nibbles,
711 cursor: &mut ArenaCursor,
712 result: &mut Vec<ProofTrieNodeRecord>,
713 ) {
714 cursor.reset(arena, root, root_path);
715
716 if let Some(record) = node_to_record(arena, root, root_path) {
718 result.push(record);
719 }
720
721 loop {
722 match cursor.next(arena, |_, node| {
723 matches!(node, ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. })
724 }) {
725 NextResult::Done => break,
726 NextResult::Branch | NextResult::NonBranch => {
727 let head = cursor.head().expect("cursor is non-empty");
728 if let Some(record) = node_to_record(arena, head.index, head.path) {
729 result.push(record);
730 }
731 }
732 }
733 }
734 }
735
736 let mut nodes = Vec::new();
737
738 collect_records(
740 &mut self.upper_arena,
741 self.root,
742 Nibbles::default(),
743 &mut self.buffers.cursor,
744 &mut nodes,
745 );
746
747 for (_, node) in &mut self.upper_arena {
749 if let ArenaSparseNode::Subtrie(subtrie) = node {
750 collect_records(
751 &mut subtrie.arena,
752 subtrie.root,
753 subtrie.path,
754 &mut self.buffers.cursor,
755 &mut nodes,
756 );
757 }
758 }
759
760 self.debug_recorder.reset();
762 self.debug_recorder.record(RecordedOp::Prune);
763
764 if let Some(root_record) = nodes.first() {
766 self.debug_recorder.record(RecordedOp::SetRoot { node: root_record.clone() });
767 }
768 if nodes.len() > 1 {
769 self.debug_recorder.record(RecordedOp::RevealNodes { nodes: nodes[1..].to_vec() });
770 }
771 }
772
773 const fn should_be_subtrie(path_len: usize) -> bool {
776 path_len == UPPER_TRIE_MAX_DEPTH
777 }
778
779 fn maybe_wrap_in_subtrie(&mut self, child_idx: Index, child_path: &Nibbles) {
784 if !Self::should_be_subtrie(child_path.len()) {
785 return;
786 }
787
788 if !matches!(
790 self.upper_arena[child_idx],
791 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. }
792 ) {
793 return;
794 }
795
796 trace!(target: TRACE_TARGET, ?child_path, "Wrapping child into subtrie");
797 let mut subtrie = ArenaSparseSubtrie::new(self.buffers.updates.is_some());
798 subtrie.path = *child_path;
799 let mut root_node =
800 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie);
801
802 if let ArenaSparseNode::Branch(b) = &mut root_node {
804 for child in &mut b.children {
805 if let ArenaSparseNodeBranchChild::Revealed(idx) = child {
806 *idx =
807 Self::migrate_nodes(&mut subtrie.arena, &mut self.upper_arena, *idx, None);
808 }
809 }
810 }
811
812 subtrie.arena[subtrie.root] = root_node;
813 let (leaves, dirty) = Self::count_leaves_and_dirty(&subtrie.arena, subtrie.root);
814 subtrie.num_leaves = leaves;
815 subtrie.num_dirty_leaves = dirty;
816 #[cfg(debug_assertions)]
817 subtrie.debug_assert_counters();
818 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
819 }
820
821 fn maybe_wrap_branch_children(&mut self, cursor: &ArenaCursor) {
826 let head = cursor.head().expect("cursor is non-empty");
827 let head_idx = head.index;
828 let head_path = head.path;
829
830 let ArenaSparseNode::Branch(b) = &self.upper_arena[head_idx] else { return };
831 let short_key = b.short_key;
832 let children: SmallVec<[_; 4]> = b
833 .child_iter()
834 .filter_map(|(nibble, child)| match child {
835 ArenaSparseNodeBranchChild::Revealed(idx) => Some((nibble, *idx)),
836 ArenaSparseNodeBranchChild::Blinded(_) => None,
837 })
838 .collect();
839
840 for (nibble, child_idx) in children {
841 let mut child_path = head_path;
842 child_path.extend(&short_key);
843 child_path.push_unchecked(nibble);
844 self.maybe_wrap_in_subtrie(child_idx, &child_path);
845 }
846 }
847
848 #[instrument(
857 level = "trace",
858 target = TRACE_TARGET,
859 skip_all,
860 fields(subtrie_path = ?cursor.head().expect("cursor is non-empty").path),
861 )]
862 fn maybe_unwrap_subtrie(&mut self, cursor: &mut ArenaCursor) {
863 let subtrie_idx = cursor.head().expect("cursor is non-empty").index;
864
865 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[subtrie_idx] else {
866 return;
867 };
868
869 if !matches!(subtrie.arena[subtrie.root], ArenaSparseNode::EmptyRoot { .. }) {
870 return;
871 }
872
873 let child_nibble = cursor
874 .head()
875 .expect("cursor is non-empty")
876 .path
877 .last()
878 .expect("subtrie path must have at least one nibble");
879 let parent_idx = cursor.parent().expect("cursor has parent").index;
880
881 cursor.pop(&mut self.upper_arena);
884
885 self.recycle_subtrie_from_idx(subtrie_idx);
886
887 trace!(target: TRACE_TARGET, "Unwrapping empty subtrie, removing child slot");
888 let parent_branch = self.upper_arena[parent_idx].branch_mut();
889 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
890 .expect("child nibble not found in parent state_mask");
891
892 parent_branch.children.remove(child_idx.get());
893 parent_branch.unset_child_bit(child_nibble);
894 parent_branch.state = parent_branch.state.to_dirty();
896
897 self.maybe_collapse_or_remove_branch(cursor);
898 }
899
900 fn recycle_subtrie(&mut self, node: ArenaSparseNode) {
906 let ArenaSparseNode::Subtrie(mut subtrie) = node else {
907 unreachable!("recycle_subtrie called on non-Subtrie node")
908 };
909 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
910 }
911
912 fn recycle_subtrie_from_idx(&mut self, idx: Index) {
914 let node = self.upper_arena.remove(idx).expect("subtrie exists in arena");
915 self.recycle_subtrie(node);
916 }
917
918 fn maybe_collapse_or_remove_branch(&mut self, cursor: &mut ArenaCursor) {
929 loop {
930 let branch_entry = cursor.head().expect("cursor is non-empty");
931 let branch_idx = branch_entry.index;
932 let branch_path = branch_entry.path;
933
934 let count = {
937 let ArenaSparseNode::Branch(b) = &self.upper_arena[branch_idx] else {
938 return;
939 };
940 b.state_mask.count_bits()
941 };
942
943 if count >= 2 {
944 return;
945 }
946
947 if count == 0 {
948 if branch_idx == self.root {
949 self.upper_arena[branch_idx] =
950 ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Dirty };
951 return;
952 }
953 let branch_nibble = branch_path.last().expect("non-root branch");
955 cursor.pop(&mut self.upper_arena);
956 self.upper_arena.remove(branch_idx);
957 let parent_idx = cursor.head().expect("cursor is non-empty").index;
958 let parent_branch = self.upper_arena[parent_idx].branch_mut();
959 let child_idx = BranchChildIdx::new(parent_branch.state_mask, branch_nibble)
960 .expect("child nibble not found in parent state_mask");
961 parent_branch.children.remove(child_idx.get());
962 parent_branch.unset_child_bit(branch_nibble);
963 parent_branch.state = parent_branch.state.to_dirty();
964 continue; }
966
967 let (remaining_nibble, remaining_child_idx) = {
969 let b = self.upper_arena[branch_idx].branch_ref();
970 let nibble = b.state_mask.iter().next().expect("branch has at least one child");
971 let child_idx = match &b.children[0] {
972 ArenaSparseNodeBranchChild::Revealed(idx) => Some(*idx),
973 ArenaSparseNodeBranchChild::Blinded(_) => None,
974 };
975 (nibble, child_idx)
976 };
977
978 let Some(child_idx) = remaining_child_idx else {
979 debug_assert!(false, "single remaining child is blinded — should have been caught by check_subtrie_collapse_needs_proof");
980 return;
981 };
982
983 if matches!(self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie) {
984 return;
987 }
988
989 let is_empty_subtrie = matches!(
991 &self.upper_arena[child_idx],
992 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot { .. })
993 );
994
995 if is_empty_subtrie {
996 self.recycle_subtrie_from_idx(child_idx);
997 let branch = self.upper_arena[branch_idx].branch_mut();
998 branch.children.remove(0);
999 branch.unset_child_bit(remaining_nibble);
1000 branch.state = branch.state.to_dirty();
1001 continue; }
1003
1004 Self::collapse_branch(
1006 &mut self.upper_arena,
1007 cursor,
1008 &mut self.root,
1009 &mut self.buffers.updates,
1010 );
1011
1012 let child_idx = cursor.head().expect("cursor is non-empty").index;
1017 if let ArenaSparseNode::Subtrie(_) = &self.upper_arena[child_idx] {
1018 let ArenaSparseNode::Subtrie(mut subtrie) =
1019 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie)
1020 else {
1021 unreachable!()
1022 };
1023 Self::migrate_nodes(
1024 &mut self.upper_arena,
1025 &mut subtrie.arena,
1026 subtrie.root,
1027 Some(child_idx),
1028 );
1029 Self::merge_subtrie_updates(
1030 &mut self.buffers.updates,
1031 &mut subtrie.buffers.updates,
1032 );
1033
1034 self.maybe_wrap_branch_children(cursor);
1038 }
1039 return;
1040 }
1041 }
1042
1043 fn merge_subtrie_updates(
1050 dst: &mut Option<SparseTrieUpdates>,
1051 src: &mut Option<SparseTrieUpdates>,
1052 ) {
1053 if let Some(dst_updates) = dst.as_mut() {
1054 let src_updates = src.as_mut().expect("updates are enabled");
1055 debug_assert!(!src_updates.wiped, "subtrie updates should never have wiped=true");
1056
1057 for path in src_updates.updated_nodes.keys() {
1059 dst_updates.removed_nodes.remove(path);
1060 }
1061 dst_updates.updated_nodes.extend(src_updates.updated_nodes.drain());
1062
1063 for path in &src_updates.removed_nodes {
1065 dst_updates.updated_nodes.remove(path);
1066 }
1067 dst_updates.removed_nodes.extend(src_updates.removed_nodes.drain());
1068 }
1069 }
1070
1071 fn nibbles_to_padded_b256(path: &Nibbles) -> B256 {
1073 let mut bytes = [0u8; 32];
1074 path.pack_to(&mut bytes);
1075 B256::from(bytes)
1076 }
1077
1078 fn get_branch_masks(arena: &NodeArena, branch: &ArenaSparseNodeBranch) -> BranchNodeMasks {
1080 let mut masks = BranchNodeMasks::default();
1081
1082 for (nibble, child) in branch.child_iter() {
1083 let (hash_bit, tree_bit) = match child {
1084 ArenaSparseNodeBranchChild::Blinded(_) => (
1085 branch.branch_masks.hash_mask.is_bit_set(nibble),
1086 branch.branch_masks.tree_mask.is_bit_set(nibble),
1087 ),
1088 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1089 let child = &arena[*child_idx];
1090 (child.hash_mask_bit(), child.tree_mask_bit())
1091 }
1092 };
1093
1094 masks.set_child_bits(nibble, hash_bit, tree_bit);
1095 }
1096
1097 masks
1098 }
1099
1100 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(base_path = ?base_path), ret)]
1113 fn update_cached_rlp(
1114 arena: &mut NodeArena,
1115 root: Index,
1116 base_path: Nibbles,
1117 buffers: &mut ArenaTrieBuffers,
1118 new_epoch: TrieNodeEpoch,
1119 ) -> RlpNode {
1120 let cursor = &mut buffers.cursor;
1121 let rlp_buf = &mut buffers.rlp_buf;
1122 let rlp_node_buf = &mut buffers.rlp_node_buf;
1123 let updates = &mut buffers.updates;
1124
1125 rlp_node_buf.clear();
1126
1127 match &arena[root] {
1131 ArenaSparseNode::EmptyRoot { state } => {
1132 let node_epoch = match state {
1133 ArenaSparseNodeState::Cached { epoch, .. } => *epoch,
1134 ArenaSparseNodeState::Revealed => TrieNodeEpoch::UNMODIFIED,
1135 ArenaSparseNodeState::Dirty => new_epoch,
1136 };
1137 let rlp_node = RlpNode::word_rlp(&EMPTY_ROOT_HASH);
1138 *arena[root].state_mut() =
1139 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), epoch: node_epoch };
1140 return rlp_node
1141 }
1142 ArenaSparseNode::Leaf { .. } => {
1143 Self::encode_leaf(arena, root, rlp_buf, rlp_node_buf, new_epoch);
1144 return rlp_node_buf.pop().expect("encode_leaf must push an RlpNode");
1145 }
1146 ArenaSparseNode::Branch(b) => {
1147 if let ArenaSparseNodeState::Cached { rlp_node, .. } = &b.state {
1148 let rlp_node = rlp_node.clone();
1149 return rlp_node;
1150 }
1151 }
1152 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie => {
1153 unreachable!("Subtrie/TakenSubtrie should not appear inside a subtrie's own arena");
1154 }
1155 }
1156
1157 cursor.reset(arena, root, base_path);
1158
1159 loop {
1163 let result = cursor.next(&mut *arena, |_, node| {
1164 matches!(
1165 node,
1166 ArenaSparseNode::Branch(b) if matches!(b.state, ArenaSparseNodeState::Dirty)
1167 )
1168 });
1169
1170 match result {
1171 NextResult::Done => break,
1172 NextResult::NonBranch => {
1173 unreachable!("should_descend only returns true for dirty branches")
1174 }
1175 NextResult::Branch => {}
1176 };
1177
1178 let head = cursor.head().expect("cursor is non-empty");
1179 let head_idx = head.index;
1180 let head_path = head.path;
1181
1182 trace!(
1186 target: TRACE_TARGET,
1187 branch_path = ?head_path,
1188 branch_short_key = ?arena[head_idx].short_key().expect("head is a branch"),
1189 state_mask = ?arena[head_idx].branch_ref().state_mask,
1190 "Calculating branch RlpNode",
1191 );
1192
1193 rlp_node_buf.clear();
1194 let mut node_epoch = TrieNodeEpoch::UNMODIFIED;
1195 let state_mask = arena[head_idx].branch_ref().state_mask;
1196 for (child_idx, _nibble) in BranchChildIter::new(state_mask) {
1197 match &arena[head_idx].branch_ref().children[child_idx] {
1198 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1199 rlp_node_buf.push(rlp_node.clone());
1200 }
1201 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1202 let child_idx = *child_idx;
1203 match &arena[child_idx] {
1204 ArenaSparseNode::Leaf { .. } => {
1205 Self::encode_leaf(
1206 arena,
1207 child_idx,
1208 rlp_buf,
1209 rlp_node_buf,
1210 new_epoch,
1211 );
1212 }
1213 ArenaSparseNode::Branch(child_b) => {
1214 let ArenaSparseNodeState::Cached { rlp_node, .. } = &child_b.state
1215 else {
1216 panic!("child branch must be cached after DFS");
1217 };
1218 let rlp_node = rlp_node.clone();
1219 rlp_node_buf.push(rlp_node);
1220 }
1221 ArenaSparseNode::Subtrie(subtrie) => {
1222 let subtrie_root = &subtrie.arena[subtrie.root];
1223 match subtrie_root {
1224 ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1225 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1226 ..
1227 }) |
1228 ArenaSparseNode::Leaf {
1229 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1230 ..
1231 } => {
1232 rlp_node_buf.push(rlp_node.clone());
1233 }
1234 _ => panic!("subtrie root must be a cached Branch or Leaf"),
1235 }
1236 }
1237 ArenaSparseNode::TakenSubtrie | ArenaSparseNode::EmptyRoot { .. } => {
1238 unreachable!("Unexpected child {:?}", arena[child_idx]);
1239 }
1240 }
1241 let Some(ArenaSparseNodeState::Cached { epoch: child_epoch, .. }) =
1242 arena[child_idx].state_ref()
1243 else {
1244 panic!("revealed child must be cached after encoding");
1245 };
1246 node_epoch = node_epoch.max(*child_epoch);
1247 }
1248 }
1249 }
1250
1251 let b = arena[head_idx].branch_ref();
1253 let short_key = b.short_key;
1254 let state_mask = b.state_mask;
1255 let prev_branch_masks = b.branch_masks;
1256 let new_branch_masks = Self::get_branch_masks(arena, b);
1257 let was_dirty = matches!(b.state, ArenaSparseNodeState::Dirty);
1258 if was_dirty {
1259 node_epoch = node_epoch.max(new_epoch);
1260 }
1261
1262 rlp_buf.clear();
1263 let rlp_node = BranchNodeRef::new(rlp_node_buf, state_mask).rlp(rlp_buf);
1264
1265 let rlp_node = if short_key.is_empty() {
1266 rlp_node
1267 } else {
1268 rlp_buf.clear();
1269 ExtensionNodeRef::new(&short_key, &rlp_node).rlp(rlp_buf)
1270 };
1271
1272 trace!(
1273 target: TRACE_TARGET,
1274 path = ?head_path,
1275 short_key = ?arena[head_idx].short_key(),
1276 children = ?state_mask.iter().zip(rlp_node_buf.iter()).collect::<Vec<_>>(),
1277 rlp_node = ?rlp_node,
1278 "Calculated branch RlpNode",
1279 );
1280
1281 let branch = arena[head_idx].branch_mut();
1282 branch.state =
1283 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), epoch: node_epoch };
1284 branch.branch_masks = new_branch_masks;
1285
1286 if let Some(trie_updates) = updates.as_mut().filter(|_| was_dirty) {
1289 let mut logical_path = head_path;
1290 logical_path.extend(&short_key);
1291
1292 if !logical_path.is_empty() {
1293 if !prev_branch_masks.is_empty() && new_branch_masks.is_empty() {
1294 trie_updates.updated_nodes.remove(&logical_path);
1295 trie_updates.removed_nodes.insert(logical_path);
1296 } else if !new_branch_masks.is_empty() {
1297 let compact = arena[head_idx].branch_ref().branch_node_compact(arena);
1298 trie_updates.updated_nodes.insert(logical_path, compact);
1299 trie_updates.removed_nodes.remove(&logical_path);
1300 }
1301 }
1302 }
1303 }
1304
1305 let ArenaSparseNodeState::Cached { rlp_node, .. } = &arena[root].branch_ref().state else {
1306 panic!("root must be cached after update_cached_rlp");
1307 };
1308 rlp_node.clone()
1309 }
1310
1311 fn get_leaf_value_in_arena<'a>(
1314 arena: &'a NodeArena,
1315 mut current: Index,
1316 full_path: &Nibbles,
1317 mut path_offset: usize,
1318 ) -> Option<&'a Vec<u8>> {
1319 loop {
1320 match &arena[current] {
1321 ArenaSparseNode::EmptyRoot { .. } | ArenaSparseNode::TakenSubtrie => return None,
1322 ArenaSparseNode::Leaf { key, value, .. } => {
1323 let remaining = full_path.slice(path_offset..);
1324 return (remaining == *key).then_some(value);
1325 }
1326 ArenaSparseNode::Branch(b) => {
1327 let short_key = &b.short_key;
1328 let logical_end = path_offset + short_key.len();
1329 if full_path.len() <= logical_end ||
1330 full_path.slice(path_offset..logical_end) != *short_key
1331 {
1332 return None;
1333 }
1334
1335 let child_nibble = full_path.get_unchecked(logical_end);
1336 let child_idx = BranchChildIdx::new(b.state_mask, child_nibble)?;
1337 match &b.children[child_idx] {
1338 ArenaSparseNodeBranchChild::Blinded(_) => return None,
1339 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1340 current = *child_idx;
1341 path_offset = logical_end + 1;
1342 }
1343 }
1344 }
1345 ArenaSparseNode::Subtrie(subtrie) => {
1346 return Self::get_leaf_value_in_arena(
1347 &subtrie.arena,
1348 subtrie.root,
1349 full_path,
1350 path_offset,
1351 );
1352 }
1353 }
1354 }
1355 }
1356
1357 fn find_leaf_in_arena(
1361 arena: &NodeArena,
1362 mut current: Index,
1363 full_path: &Nibbles,
1364 mut path_offset: usize,
1365 expected_value: Option<&Vec<u8>>,
1366 ) -> Result<LeafLookup, LeafLookupError> {
1367 loop {
1368 match &arena[current] {
1369 ArenaSparseNode::EmptyRoot { .. } | ArenaSparseNode::TakenSubtrie => {
1370 return Ok(LeafLookup::NonExistent);
1371 }
1372 ArenaSparseNode::Leaf { key, value, .. } => {
1373 let remaining = full_path.slice(path_offset..);
1374 if remaining != *key {
1375 return Ok(LeafLookup::NonExistent);
1376 }
1377 if let Some(expected) = expected_value &&
1378 *expected != *value
1379 {
1380 return Err(LeafLookupError::ValueMismatch {
1381 path: *full_path,
1382 expected: Some(expected.clone()),
1383 actual: value.clone(),
1384 });
1385 }
1386 return Ok(LeafLookup::Exists);
1387 }
1388 ArenaSparseNode::Branch(b) => {
1389 let short_key = &b.short_key;
1390 let logical_end = path_offset + short_key.len();
1391
1392 if full_path.len() <= logical_end {
1393 return Ok(LeafLookup::NonExistent);
1394 }
1395
1396 if full_path.slice(path_offset..logical_end) != *short_key {
1397 return Ok(LeafLookup::NonExistent);
1398 }
1399
1400 let child_nibble = full_path.get_unchecked(logical_end);
1401 let Some(child_idx) = BranchChildIdx::new(b.state_mask, child_nibble) else {
1402 return Ok(LeafLookup::NonExistent);
1403 };
1404
1405 match &b.children[child_idx] {
1406 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1407 let hash = rlp_node
1408 .as_hash()
1409 .unwrap_or_else(|| keccak256(rlp_node.as_slice()));
1410 let mut blinded_path = full_path.slice(..logical_end);
1411 blinded_path.push_unchecked(child_nibble);
1412 return Err(LeafLookupError::BlindedNode { path: blinded_path, hash });
1413 }
1414 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1415 current = *child_idx;
1416 path_offset = logical_end + 1;
1417 }
1418 }
1419 }
1420 ArenaSparseNode::Subtrie(subtrie) => {
1421 return Self::find_leaf_in_arena(
1422 &subtrie.arena,
1423 subtrie.root,
1424 full_path,
1425 path_offset,
1426 expected_value,
1427 );
1428 }
1429 }
1430 }
1431 }
1432
1433 fn encode_leaf(
1437 arena: &mut NodeArena,
1438 idx: Index,
1439 rlp_buf: &mut Vec<u8>,
1440 rlp_node_buf: &mut Vec<RlpNode>,
1441 new_epoch: TrieNodeEpoch,
1442 ) {
1443 let (key, value, state) = match &arena[idx] {
1444 ArenaSparseNode::Leaf { key, value, state } => (key, value, state),
1445 _ => unreachable!("encode_leaf called on non-Leaf node"),
1446 };
1447
1448 let epoch = match state {
1449 ArenaSparseNodeState::Cached { rlp_node, .. } => {
1450 rlp_node_buf.push(rlp_node.clone());
1451 return;
1452 }
1453 ArenaSparseNodeState::Revealed => TrieNodeEpoch::UNMODIFIED,
1454 ArenaSparseNodeState::Dirty => new_epoch,
1455 };
1456
1457 rlp_buf.clear();
1458 let rlp_node = LeafNodeRef { key, value }.rlp(rlp_buf);
1459
1460 *arena[idx].state_mut() =
1461 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), epoch };
1462 rlp_node_buf.push(rlp_node);
1463 }
1464
1465 fn split_and_insert_leaf(
1479 arena: &mut NodeArena,
1480 cursor: &mut ArenaCursor,
1481 root: &mut Index,
1482 new_leaf_path: Nibbles,
1483 value: &[u8],
1484 ) -> bool {
1485 let old_child_entry = cursor.head().expect("cursor must have head");
1486 let old_child_idx = old_child_entry.index;
1487 let old_child_short_key = arena[old_child_idx].short_key().expect("top of stack is a leaf");
1488 let diverge_len = new_leaf_path.common_prefix_length(old_child_short_key);
1489
1490 trace!(
1491 target: TRACE_TARGET,
1492 path = ?old_child_entry.path,
1493 ?new_leaf_path,
1494 ?old_child_short_key,
1495 diverge_len,
1496 "Splitting node and inserting new leaf",
1497 );
1498
1499 let old_child_nibble = old_child_short_key.get_unchecked(diverge_len);
1500 let old_child_suffix = old_child_short_key.slice(diverge_len + 1..);
1501
1502 let newly_dirtied_existing = match &mut arena[old_child_idx] {
1505 ArenaSparseNode::Leaf { key, state, .. } => {
1506 *key = old_child_suffix;
1507 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1508 *state = ArenaSparseNodeState::Dirty;
1509 was_clean
1510 }
1511 ArenaSparseNode::Branch(b) => {
1512 b.short_key = old_child_suffix;
1513 b.state = b.state.to_dirty();
1514 false
1516 }
1517 _ => unreachable!("split_and_insert_leaf called on non-Leaf/Branch node"),
1518 };
1519
1520 let short_key = new_leaf_path.slice(..diverge_len);
1521 let new_leaf_nibble = new_leaf_path.get_unchecked(diverge_len);
1522 debug_assert_ne!(old_child_nibble, new_leaf_nibble);
1523
1524 let new_leaf_idx = arena.insert(ArenaSparseNode::Leaf {
1525 state: ArenaSparseNodeState::Dirty,
1526 key: new_leaf_path.slice(diverge_len + 1..),
1527 value: value.to_vec(),
1528 });
1529
1530 let (first_nibble, first_child, second_nibble, second_child) =
1531 if old_child_nibble < new_leaf_nibble {
1532 (old_child_nibble, old_child_idx, new_leaf_nibble, new_leaf_idx)
1533 } else {
1534 (new_leaf_nibble, new_leaf_idx, old_child_nibble, old_child_idx)
1535 };
1536
1537 let state_mask = TrieMask::from(1u16 << first_nibble | 1u16 << second_nibble);
1538 let mut children = SmallVec::with_capacity(2);
1539 children.push(ArenaSparseNodeBranchChild::Revealed(first_child));
1540 children.push(ArenaSparseNodeBranchChild::Revealed(second_child));
1541
1542 let new_branch_idx = arena.insert(ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1543 state: ArenaSparseNodeState::Dirty,
1544 children,
1545 state_mask,
1546 short_key,
1547 branch_masks: BranchNodeMasks::default(),
1548 }));
1549
1550 cursor.replace_head_index(arena, root, new_branch_idx);
1551 newly_dirtied_existing
1552 }
1553
1554 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(full_path = ?full_path))]
1569 fn upsert_leaf(
1570 arena: &mut NodeArena,
1571 cursor: &mut ArenaCursor,
1572 root: &mut Index,
1573 full_path: &Nibbles,
1574 value: &[u8],
1575 find_result: SeekResult,
1576 ) -> (UpsertLeafResult, SubtrieCounterDeltas) {
1577 trace!(target: TRACE_TARGET, ?find_result, "Upserting leaf");
1578 let head = cursor.head().expect("cursor is non-empty");
1579
1580 match find_result {
1581 SeekResult::Blinded => {
1582 unreachable!("Blinded case must be handled by caller")
1583 }
1584 SeekResult::EmptyRoot => {
1585 let head_idx = head.index;
1586 let head_path = head.path;
1587 arena[head_idx] = ArenaSparseNode::Leaf {
1588 state: ArenaSparseNodeState::Dirty,
1589 key: full_path.slice(head_path.len()..),
1590 value: value.to_vec(),
1591 };
1592 (
1593 UpsertLeafResult::NewLeaf,
1594 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1595 )
1596 }
1597 SeekResult::RevealedLeaf => {
1598 let head_idx = head.index;
1600 let was_clean =
1601 if let ArenaSparseNode::Leaf { value: v, state, .. } = &mut arena[head_idx] {
1602 v.clear();
1603 v.extend_from_slice(value);
1604 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1605 *state = ArenaSparseNodeState::Dirty;
1606 was_clean
1607 } else {
1608 unreachable!("RevealedLeaf but cursor head is not a leaf")
1609 };
1610 (
1611 UpsertLeafResult::Updated,
1612 SubtrieCounterDeltas {
1613 num_leaves_delta: 0,
1614 num_dirty_leaves_delta: was_clean as i64,
1615 },
1616 )
1617 }
1618 SeekResult::Diverged => {
1619 let head_path = head.path;
1620 let full_path_from_head = full_path.slice(head_path.len()..);
1621
1622 let split_dirtied_existing =
1623 Self::split_and_insert_leaf(arena, cursor, root, full_path_from_head, value);
1624
1625 let result = if cursor.depth() >= 1 {
1626 UpsertLeafResult::NewChild
1627 } else {
1628 UpsertLeafResult::NewLeaf
1629 };
1630 (
1631 result,
1632 SubtrieCounterDeltas {
1633 num_leaves_delta: 1,
1634 num_dirty_leaves_delta: 1 + split_dirtied_existing as i64,
1635 },
1636 )
1637 }
1638 SeekResult::NoChild { child_nibble } => {
1639 let head_idx = head.index;
1640
1641 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
1642 let leaf_key = full_path.slice(head_branch_logical_path.len() + 1..);
1643 let new_leaf = arena.insert(ArenaSparseNode::Leaf {
1644 state: ArenaSparseNodeState::Dirty,
1645 key: leaf_key,
1646 value: value.to_vec(),
1647 });
1648
1649 let branch = arena[head_idx].branch_mut();
1650 branch.set_child(child_nibble, ArenaSparseNodeBranchChild::Revealed(new_leaf));
1651
1652 cursor.seek(arena, full_path);
1654
1655 (
1656 UpsertLeafResult::NewChild,
1657 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1658 )
1659 }
1660 SeekResult::RevealedSubtrie => {
1661 unreachable!("RevealedSubtrie must be handled by caller")
1662 }
1663 }
1664 }
1665
1666 fn remove_leaf(
1682 arena: &mut NodeArena,
1683 cursor: &mut ArenaCursor,
1684 root: &mut Index,
1685 key: B256,
1686 full_path: &Nibbles,
1687 find_result: SeekResult,
1688 updates: &mut Option<SparseTrieUpdates>,
1689 ) -> (RemoveLeafResult, SubtrieCounterDeltas) {
1690 match find_result {
1691 SeekResult::Blinded | SeekResult::RevealedSubtrie => {
1692 unreachable!("Blinded/RevealedSubtrie must be handled by caller")
1693 }
1694 SeekResult::EmptyRoot | SeekResult::Diverged | SeekResult::NoChild { .. } => {
1695 (RemoveLeafResult::NotFound, SubtrieCounterDeltas::default())
1696 }
1697 SeekResult::RevealedLeaf => {
1698 let head = cursor.head().expect("cursor is non-empty");
1700 let head_idx = head.index;
1701 let head_path = head.path;
1702
1703 trace!(
1704 target: TRACE_TARGET,
1705 path = ?head_path,
1706 ?full_path,
1707 "Removing leaf",
1708 );
1709
1710 if let Some(parent_entry) = cursor.parent() {
1713 let parent_idx = parent_entry.index;
1714 let child_nibble = head_path.last().expect("non-root leaf");
1715 let parent_branch = arena[parent_idx].branch_ref();
1716
1717 if parent_branch.state_mask.count_bits() == 2 &&
1718 parent_branch.sibling_child(child_nibble).is_blinded()
1719 {
1720 let sibling_nibble = parent_branch
1721 .state_mask
1722 .iter()
1723 .find(|&n| n != child_nibble)
1724 .expect("branch has two children");
1725 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1726 sibling_path.push_unchecked(sibling_nibble);
1727 trace!(target: TRACE_TARGET, ?full_path, ?sibling_path, "Removal would collapse branch onto blinded sibling, requesting proof");
1728 return (
1729 RemoveLeafResult::NeedsProof {
1730 key,
1731 proof_key: Self::nibbles_to_padded_b256(&sibling_path),
1732 parent: ProofV2TargetParent::new(
1733 sibling_path
1734 .len()
1735 .checked_sub(1)
1736 .expect("sibling path has a child nibble"),
1737 ),
1738 },
1739 SubtrieCounterDeltas::default(),
1740 );
1741 }
1742 }
1743
1744 let removed_was_dirty =
1746 matches!(arena[head_idx].state_ref(), Some(ArenaSparseNodeState::Dirty));
1747
1748 if cursor.depth() == 0 {
1749 arena.remove(head_idx);
1752 *root = arena
1753 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Dirty });
1754 cursor.reset(arena, *root, head_path);
1755 return (
1756 RemoveLeafResult::Removed,
1757 SubtrieCounterDeltas {
1758 num_leaves_delta: -1,
1759 num_dirty_leaves_delta: -(removed_was_dirty as i64),
1760 },
1761 );
1762 }
1763
1764 cursor.pop(arena);
1766
1767 let parent_entry = cursor.head().expect("cursor is non-empty");
1769 let parent_idx = parent_entry.index;
1770 let child_nibble = head_path.last().expect("non-root leaf");
1771
1772 arena.remove(head_idx);
1774 let parent_branch = arena[parent_idx].branch_mut();
1775 parent_branch.remove_child(child_nibble);
1776
1777 let collapse_dirtied_leaf = if parent_branch.state_mask.count_bits() == 1 {
1780 Self::collapse_branch(arena, cursor, root, updates)
1781 } else {
1782 false
1783 };
1784 (
1785 RemoveLeafResult::Removed,
1786 SubtrieCounterDeltas {
1787 num_leaves_delta: -1,
1788 num_dirty_leaves_delta: (collapse_dirtied_leaf as i64) -
1789 (removed_was_dirty as i64),
1790 },
1791 )
1792 }
1793 }
1794 }
1795
1796 fn check_subtrie_collapse_needs_proof(
1802 arena: &NodeArena,
1803 cursor: &ArenaCursor,
1804 subtrie_updates: &[(B256, Nibbles, LeafUpdate)],
1805 ) -> Option<ArenaRequiredProof> {
1806 let num_removals = subtrie_updates
1807 .iter()
1808 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()))
1809 .count() as u64;
1810
1811 let num_changed =
1819 subtrie_updates.iter().filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_))).count()
1820 as u64;
1821
1822 if num_removals == 0 || num_removals != num_changed {
1823 return None;
1824 }
1825
1826 let subtrie_entry = cursor.head()?;
1828 let subtrie_num_leaves = match &arena[subtrie_entry.index] {
1829 ArenaSparseNode::Subtrie(s) => s.num_leaves,
1830 _ => return None,
1831 };
1832 if num_removals < subtrie_num_leaves {
1833 return None;
1834 }
1835
1836 let child_nibble =
1837 subtrie_entry.path.last().expect("subtrie path must have at least one nibble");
1838
1839 let parent_entry = cursor.parent()?;
1840 let parent_branch = arena[parent_entry.index].branch_ref();
1841 if parent_branch.state_mask.count_bits() != 2 {
1842 return None;
1843 }
1844
1845 if !parent_branch.sibling_child(child_nibble).is_blinded() {
1846 return None;
1847 }
1848
1849 let sibling_nibble = parent_branch
1850 .state_mask
1851 .iter()
1852 .find(|&n| n != child_nibble)
1853 .expect("branch has two children");
1854 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1855 sibling_path.push_unchecked(sibling_nibble);
1856
1857 Some(ArenaRequiredProof {
1858 key: Self::nibbles_to_padded_b256(&sibling_path),
1859 parent: ProofV2TargetParent::new(
1860 sibling_path.len().checked_sub(1).expect("sibling path has a child nibble"),
1861 ),
1862 })
1863 }
1864
1865 fn collapse_branch(
1876 arena: &mut NodeArena,
1877 cursor: &mut ArenaCursor,
1878 root: &mut Index,
1879 updates: &mut Option<SparseTrieUpdates>,
1880 ) -> bool {
1881 let branch_entry = cursor.head().expect("cursor is non-empty");
1882 let branch_idx = branch_entry.index;
1883 let branch = arena[branch_idx].branch_ref();
1884 let remaining_nibble =
1885 branch.state_mask.iter().next().expect("branch has at least one child");
1886 let branch_short_key = branch.short_key;
1887
1888 debug_assert_eq!(
1889 branch.state_mask.count_bits(),
1890 1,
1891 "collapse_branch requires exactly 1 child"
1892 );
1893 debug_assert!(
1894 !branch.children[0].is_blinded(),
1895 "collapse_branch called with a blinded remaining child"
1896 );
1897
1898 trace!(
1899 target: TRACE_TARGET,
1900 path = ?branch_entry.path,
1901 short_key = ?branch_short_key,
1902 branch_masks = ?branch.branch_masks,
1903 ?remaining_nibble,
1904 "Collapsing single-child branch",
1905 );
1906
1907 if let Some(trie_updates) = updates.as_mut() &&
1910 !branch.branch_masks.is_empty()
1911 {
1912 let logical_path = cursor.head_logical_branch_path(arena);
1913 if !logical_path.is_empty() {
1914 trie_updates.updated_nodes.remove(&logical_path);
1915 trie_updates.removed_nodes.insert(logical_path);
1916 }
1917 }
1918
1919 let mut prefix = branch_short_key;
1921 prefix.push_unchecked(remaining_nibble);
1922
1923 let ArenaSparseNodeBranchChild::Revealed(child_idx) = branch.children[0] else {
1924 unreachable!()
1925 };
1926
1927 let newly_dirtied_leaf = match &mut arena[child_idx] {
1930 ArenaSparseNode::Leaf { key, state, .. } => {
1931 let mut new_key = prefix;
1932 new_key.extend(key);
1933 *key = new_key;
1934 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1935 *state = ArenaSparseNodeState::Dirty;
1936 was_clean
1937 }
1938 ArenaSparseNode::Branch(b) => {
1939 let mut new_short_key = prefix;
1940 new_short_key.extend(&b.short_key);
1941 b.short_key = new_short_key;
1942 b.state = b.state.to_dirty();
1943 false
1944 }
1945 ArenaSparseNode::Subtrie(subtrie) => {
1946 subtrie.path = branch_entry.path;
1947 match &mut subtrie.arena[subtrie.root] {
1948 ArenaSparseNode::Branch(b) => {
1949 let mut new_short_key = prefix;
1950 new_short_key.extend(&b.short_key);
1951 b.short_key = new_short_key;
1952 b.state = b.state.to_dirty();
1953 }
1954 ArenaSparseNode::Leaf { key, state, .. } => {
1955 let mut new_key = prefix;
1956 new_key.extend(key);
1957 *key = new_key;
1958 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1959 *state = ArenaSparseNodeState::Dirty;
1960 if was_clean {
1961 subtrie.num_dirty_leaves += 1;
1962 }
1963 }
1964 _ => {
1965 unreachable!("subtrie root must be a Branch or Leaf during collapse_branch")
1966 }
1967 }
1968 false
1969 }
1970 _ => unreachable!("remaining child must be Leaf, Branch, or Subtrie"),
1971 };
1972
1973 cursor.replace_head_index(arena, root, child_idx);
1975
1976 arena.remove(branch_idx);
1978 newly_dirtied_leaf
1979 }
1980
1981 fn count_leaves_and_dirty(arena: &NodeArena, idx: Index) -> (u64, u64) {
1983 match &arena[idx] {
1984 ArenaSparseNode::Leaf { state, .. } => {
1985 let dirty = matches!(state, ArenaSparseNodeState::Dirty) as u64;
1986 (1, dirty)
1987 }
1988 ArenaSparseNode::Branch(b) => {
1989 let mut leaves = 0u64;
1990 let mut dirty = 0u64;
1991 for c in &b.children {
1992 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = c {
1993 let (l, d) = Self::count_leaves_and_dirty(arena, *child_idx);
1994 leaves += l;
1995 dirty += d;
1996 }
1997 }
1998 (leaves, dirty)
1999 }
2000 _ => (0, 0),
2001 }
2002 }
2003
2004 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2010 #[cfg(debug_assertions)]
2011 fn debug_assert_subtrie_structure(&mut self) {
2012 let mut cursor = mem::take(&mut self.buffers.cursor);
2013 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2014
2015 loop {
2016 let result = cursor.next(&mut self.upper_arena, |_, _| true);
2017 match result {
2018 NextResult::Done => break,
2019 NextResult::NonBranch | NextResult::Branch => {
2020 let head = cursor.head().expect("cursor is non-empty");
2021 let path_len = head.path.len();
2022 let node = &self.upper_arena[head.index];
2023
2024 if Self::should_be_subtrie(path_len) {
2025 debug_assert!(
2026 matches!(
2027 node,
2028 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie
2029 ),
2030 "node at path_len={path_len} should be a Subtrie but is {node:?}",
2031 );
2032 } else {
2033 debug_assert!(
2034 !matches!(node, ArenaSparseNode::Subtrie(_)),
2035 "node at path_len={path_len} should NOT be a Subtrie but is",
2036 );
2037 }
2038 }
2039 }
2040 }
2041
2042 self.buffers.cursor = cursor;
2043 }
2044
2045 fn migrate_nodes(
2053 dst: &mut NodeArena,
2054 src: &mut NodeArena,
2055 src_idx: Index,
2056 dst_slot: Option<Index>,
2057 ) -> Index {
2058 let mut node = src.remove(src_idx).expect("node exists in source arena");
2059
2060 if let ArenaSparseNode::Branch(b) = &mut node {
2062 for child in &mut b.children {
2063 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
2064 *child_idx = Self::migrate_nodes(dst, src, *child_idx, None);
2065 }
2066 }
2067 }
2068
2069 if let Some(slot) = dst_slot {
2070 dst[slot] = node;
2071 slot
2072 } else {
2073 dst.insert(node)
2074 }
2075 }
2076
2077 fn remove_pruned_node(
2080 arena: &mut NodeArena,
2081 cursor: &ArenaCursor,
2082 idx: Index,
2083 nibble: Option<u8>,
2084 ) -> ArenaSparseNode {
2085 let path = cursor.head().expect("cursor is non-empty").path;
2086 let node = arena.remove(idx).expect("node must exist to be pruned");
2087 let rlp_node = node
2088 .state_ref()
2089 .and_then(ArenaSparseNodeState::cached_rlp_node)
2090 .cloned()
2091 .expect("prune must run after hashing");
2092 trace!(
2093 target: TRACE_TARGET,
2094 ?path,
2095 variant = %AsRef::<str>::as_ref(&node),
2096 cached_rlp_node = ?rlp_node,
2097 "pruning node",
2098 );
2099
2100 let parent_idx = cursor.parent().expect("pruned child has parent").index;
2101 let child_nibble = nibble.expect("non-root child");
2102 let parent_branch = arena[parent_idx].branch_mut();
2103 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
2104 .expect("child nibble not found in parent state_mask");
2105 parent_branch.children[child_idx] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
2106
2107 node
2108 }
2109
2110 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2119 fn reveal_node(
2120 arena: &mut NodeArena,
2121 cursor: &ArenaCursor,
2122 node: &mut ProofTrieNodeV2,
2123 find_result: SeekResult,
2124 ) -> Option<Index> {
2125 let SeekResult::Blinded = find_result else {
2126 return None;
2128 };
2129
2130 let head = cursor.head().expect("cursor is non-empty");
2131 let head_idx = head.index;
2132 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
2133
2134 debug_assert_eq!(
2135 node.path.len(),
2136 head_branch_logical_path.len() + 1,
2137 "proof node path {:?} is not a direct child of branch at {:?} (expected depth {})",
2138 node.path,
2139 head_branch_logical_path,
2140 head_branch_logical_path.len() + 1,
2141 );
2142
2143 let child_nibble = node.path.get_unchecked(head_branch_logical_path.len());
2144 let head_branch = arena[head_idx].branch_ref();
2145 let dense_child_idx = BranchChildIdx::new(head_branch.state_mask, child_nibble)
2146 .expect("Blinded result but child nibble not in state_mask");
2147
2148 let cached_rlp = match &head_branch.children[dense_child_idx] {
2149 ArenaSparseNodeBranchChild::Blinded(rlp) => rlp.clone(),
2150 ArenaSparseNodeBranchChild::Revealed(_) => return None,
2151 };
2152
2153 trace!(
2154 target: TRACE_TARGET,
2155 path = ?node.path,
2156 rlp_node = ?cached_rlp,
2157 "Revealing node",
2158 );
2159
2160 let proof_node = mem::replace(node, ProofTrieNodeV2::empty());
2161 let mut arena_node = ArenaSparseNode::from_proof_node(proof_node);
2162
2163 let state = arena_node.state_mut();
2164 *state =
2165 ArenaSparseNodeState::Cached { rlp_node: cached_rlp, epoch: TrieNodeEpoch::UNMODIFIED };
2166
2167 let child_idx = arena.insert(arena_node);
2168 arena[head_idx].branch_mut().children[dense_child_idx] =
2169 ArenaSparseNodeBranchChild::Revealed(child_idx);
2170
2171 Some(child_idx)
2172 }
2173
2174 #[cfg(debug_assertions)]
2175 fn collect_reachable_nodes(
2176 arena: &NodeArena,
2177 idx: Index,
2178 reachable: &mut alloy_primitives::map::HashSet<Index>,
2179 ) {
2180 if !reachable.insert(idx) {
2181 return;
2182 }
2183 if let ArenaSparseNode::Branch(b) = &arena[idx] {
2184 for child in &b.children {
2185 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
2186 Self::collect_reachable_nodes(arena, *child_idx, reachable);
2187 }
2188 }
2189 }
2190 }
2191
2192 #[cfg(debug_assertions)]
2193 fn assert_no_orphaned_nodes(arena: &NodeArena, root: Index, label: &str) {
2194 let mut reachable = alloy_primitives::map::HashSet::default();
2195 Self::collect_reachable_nodes(arena, root, &mut reachable);
2196 let all_indices: alloy_primitives::map::HashSet<Index> =
2197 arena.iter().map(|(idx, _)| idx).collect();
2198 let orphaned: Vec<_> = all_indices.difference(&reachable).collect();
2199 debug_assert!(
2200 orphaned.is_empty(),
2201 "{label} has {} orphaned node(s): {orphaned:?}",
2202 orphaned.len(),
2203 );
2204 }
2205}
2206
2207#[cfg(debug_assertions)]
2208impl Drop for ArenaParallelSparseTrie {
2209 fn drop(&mut self) {
2210 Self::assert_no_orphaned_nodes(&self.upper_arena, self.root, "upper arena");
2211
2212 for (_, node) in &self.upper_arena {
2213 if let Some(subtrie) = node.as_subtrie() {
2214 Self::assert_no_orphaned_nodes(
2215 &subtrie.arena,
2216 subtrie.root,
2217 &alloc::format!("subtrie {:?}", subtrie.path),
2218 );
2219 }
2220 }
2221 }
2222}
2223
2224impl Default for ArenaParallelSparseTrie {
2225 fn default() -> Self {
2226 let mut upper_arena = SlotMap::new();
2227 let root = upper_arena
2228 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
2229 Self {
2230 upper_arena,
2231 root,
2232 buffers: ArenaTrieBuffers::default(),
2233 parallelism_thresholds: ArenaParallelismThresholds::default(),
2234 #[cfg(feature = "trie-debug")]
2235 debug_recorder: Default::default(),
2236 }
2237 }
2238}
2239
2240impl ArenaParallelSparseTrie {
2241 fn update_upper_subtrie(&mut self, head_idx: Index, new_epoch: TrieNodeEpoch) {
2243 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx] else {
2244 unreachable!()
2245 };
2246
2247 if !subtrie.arena[subtrie.root].is_cached() {
2248 subtrie.update_cached_rlp(new_epoch);
2249 }
2250
2251 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
2252 }
2253}
2254
2255impl SparseTrie for ArenaParallelSparseTrie {
2256 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2257 fn set_root(
2258 &mut self,
2259 root: TrieNodeV2,
2260 masks: Option<BranchNodeMasks>,
2261 retain_updates: bool,
2262 ) -> SparseTrieResult<()> {
2263 #[cfg(feature = "trie-debug")]
2264 self.debug_recorder.record(RecordedOp::SetRoot {
2265 node: ProofTrieNodeRecord::from_proof_trie_node_v2(&ProofTrieNodeV2 {
2266 path: Nibbles::default(),
2267 node: root.clone(),
2268 masks,
2269 }),
2270 });
2271
2272 debug_assert!(
2273 matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
2274 "set_root called on a trie that already has revealed nodes"
2275 );
2276
2277 self.set_updates(retain_updates);
2278
2279 match root {
2280 TrieNodeV2::EmptyRoot => {
2281 trace!(target: TRACE_TARGET, "Setting empty root");
2282 self.upper_arena[self.root] =
2283 ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed };
2284 }
2285 TrieNodeV2::Leaf(leaf) => {
2286 trace!(target: TRACE_TARGET, key = ?leaf.key, "Setting leaf root");
2287 self.upper_arena[self.root] = ArenaSparseNode::Leaf {
2288 state: ArenaSparseNodeState::Revealed,
2289 key: leaf.key,
2290 value: leaf.value,
2291 };
2292 }
2293 TrieNodeV2::Branch(branch) => {
2294 trace!(target: TRACE_TARGET, state_mask = ?branch.state_mask, num_children = branch.state_mask.count_bits(), "Setting branch root");
2295 let mut children = SmallVec::with_capacity(branch.state_mask.count_bits() as usize);
2296 for (stack_ptr, _nibble) in branch.state_mask.iter().enumerate() {
2297 children
2298 .push(ArenaSparseNodeBranchChild::Blinded(branch.stack[stack_ptr].clone()));
2299 }
2300
2301 self.upper_arena[self.root] = ArenaSparseNode::Branch(ArenaSparseNodeBranch {
2302 state: ArenaSparseNodeState::Revealed,
2303 children,
2304 state_mask: branch.state_mask,
2305 short_key: branch.key,
2306 branch_masks: masks.unwrap_or_default(),
2307 });
2308 }
2309 TrieNodeV2::Extension(_) => {
2310 panic!("set_root does not support Extension nodes; extensions are represented as branches with a short_key")
2311 }
2312 }
2313
2314 Ok(())
2315 }
2316
2317 fn set_updates(&mut self, retain_updates: bool) {
2318 if retain_updates {
2319 self.buffers.updates.get_or_insert_with(SparseTrieUpdates::default).clear();
2320 } else {
2321 self.buffers.updates = None;
2322 }
2323 }
2324
2325 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(num_nodes = nodes.len()))]
2326 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
2327 if nodes.is_empty() {
2328 return Ok(());
2329 }
2330
2331 #[cfg(feature = "trie-debug")]
2332 self.debug_recorder.record(RecordedOp::RevealNodes {
2333 nodes: nodes.iter().map(ProofTrieNodeRecord::from_proof_trie_node_v2).collect(),
2334 });
2335
2336 if matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot { .. }) {
2337 trace!(target: TRACE_TARGET, "Skipping reveal_nodes on empty root");
2338 return Ok(());
2339 }
2340
2341 nodes.sort_unstable_by_key(|n| n.path);
2343
2344 let threshold = self.parallelism_thresholds.min_revealed_nodes;
2345
2346 let mut cursor = mem::take(&mut self.buffers.cursor);
2348 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2349
2350 let mut node_idx = if nodes[0].path.is_empty() { 1 } else { 0 };
2352
2353 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, Vec<ProofTrieNodeV2>)> = Vec::new();
2357
2358 while node_idx < nodes.len() {
2359 let find_result = cursor.seek(&mut self.upper_arena, &nodes[node_idx].path);
2360
2361 match find_result {
2362 SeekResult::RevealedLeaf => {
2363 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, "Skipping reveal: leaf head");
2364 node_idx += 1;
2365 }
2366 SeekResult::Blinded => {
2367 let child_path = nodes[node_idx].path;
2369 let child_idx = Self::reveal_node(
2370 &mut self.upper_arena,
2371 &cursor,
2372 &mut nodes[node_idx],
2373 SeekResult::Blinded,
2374 );
2375 node_idx += 1;
2376
2377 if let Some(child_idx) = child_idx {
2378 self.maybe_wrap_in_subtrie(child_idx, &child_path);
2379 }
2380 }
2381 SeekResult::RevealedSubtrie => {
2382 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2383 let child_idx = subtrie_entry.index;
2384 let prefix = subtrie_entry.path;
2385
2386 let subtrie_start = node_idx;
2387 while node_idx < nodes.len() && nodes[node_idx].path.starts_with(&prefix) {
2388 node_idx += 1;
2389 }
2390 let num_subtrie_nodes = node_idx - subtrie_start;
2391
2392 if num_subtrie_nodes >= threshold {
2393 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Taking subtrie for parallel reveal");
2395 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2396 &mut self.upper_arena[child_idx],
2397 ArenaSparseNode::TakenSubtrie,
2398 ) else {
2399 unreachable!("RevealedSubtrie must point to a Subtrie node")
2400 };
2401 let node_vec: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2402 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2403 .collect();
2404 taken.push((child_idx, subtrie, node_vec));
2405 } else {
2406 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Revealing subtrie inline");
2408 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2409 else {
2410 unreachable!("RevealedSubtrie must point to a Subtrie node")
2411 };
2412 let mut subtrie_nodes: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2413 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2414 .collect();
2415 subtrie.reveal_nodes(&mut subtrie_nodes)?;
2416 }
2417 }
2418 _ => {
2419 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, ?find_result, "Skipping reveal: no blinded child");
2420 node_idx += 1;
2421 }
2422 }
2423 }
2424
2425 cursor.drain(&mut self.upper_arena);
2427 self.buffers.cursor = cursor;
2428
2429 if taken.is_empty() {
2430 return Ok(());
2431 }
2432
2433 if taken.len() == 1 {
2435 let (_, subtrie, node_vec) = &mut taken[0];
2436 subtrie.reveal_nodes(node_vec)?;
2437 } else {
2438 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2439
2440 let parent_span = tracing::Span::current();
2441 let results: Vec<SparseTrieResult<()>> = taken
2442 .par_iter_mut()
2443 .map(|(_, subtrie, node_vec)| {
2444 let _guard = parent_span.enter();
2445 subtrie.reveal_nodes(node_vec)
2446 })
2447 .collect();
2448
2449 if let Some(err) = results.into_iter().find(|r| r.is_err()) {
2450 for (idx, subtrie, _) in taken {
2452 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2453 }
2454 return err;
2455 }
2456 }
2457
2458 for (idx, subtrie, _) in taken {
2460 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2461 }
2462
2463 #[cfg(debug_assertions)]
2464 self.debug_assert_subtrie_structure();
2465
2466 Ok(())
2467 }
2468
2469 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, ret)]
2470 fn root(&mut self, new_epoch: TrieNodeEpoch) -> B256 {
2471 #[cfg(feature = "trie-debug")]
2472 self.debug_recorder.record(RecordedOp::Root);
2473
2474 self.update_subtrie_hashes(new_epoch);
2475
2476 let rlp_node = Self::update_cached_rlp(
2477 &mut self.upper_arena,
2478 self.root,
2479 Nibbles::default(),
2480 &mut self.buffers,
2481 new_epoch,
2482 );
2483
2484 rlp_node.as_hash().expect("root RlpNode must be a hash")
2485 }
2486
2487 fn is_root_cached(&self) -> bool {
2488 self.upper_arena[self.root].is_cached()
2489 }
2490
2491 fn root_epoch(&self) -> Option<TrieNodeEpoch> {
2492 match self.upper_arena[self.root].state_ref()? {
2493 ArenaSparseNodeState::Revealed => Some(TrieNodeEpoch::UNMODIFIED),
2494 ArenaSparseNodeState::Cached { epoch, .. } => Some(*epoch),
2495 ArenaSparseNodeState::Dirty => None,
2496 }
2497 }
2498
2499 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2500 fn update_subtrie_hashes(&mut self, new_epoch: TrieNodeEpoch) {
2501 #[cfg(feature = "trie-debug")]
2502 self.debug_recorder.record(RecordedOp::UpdateSubtrieHashes);
2503
2504 trace!(target: TRACE_TARGET, "Updating subtrie hashes");
2505
2506 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2508 return;
2509 }
2510
2511 let mut total_dirty_leaves: u64 = 0;
2513 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2514 for (idx, node) in &mut self.upper_arena {
2515 let ArenaSparseNode::Subtrie(s) = node else { continue };
2516 if s.num_dirty_leaves == 0 {
2517 continue;
2518 }
2519 total_dirty_leaves += s.num_dirty_leaves;
2520 let ArenaSparseNode::Subtrie(subtrie) =
2521 mem::replace(node, ArenaSparseNode::TakenSubtrie)
2522 else {
2523 unreachable!()
2524 };
2525 taken.push((idx, subtrie));
2526 }
2527
2528 if !taken.is_empty() {
2530 if taken.len() == 1 || total_dirty_leaves < self.parallelism_thresholds.min_dirty_leaves
2531 {
2532 for (_, subtrie) in &mut taken {
2533 subtrie.update_cached_rlp(new_epoch);
2534 }
2535 } else {
2536 use rayon::iter::{IntoParallelIterator, ParallelIterator};
2537
2538 let parent_span = tracing::Span::current();
2539 taken = taken
2540 .into_par_iter()
2541 .map(|(idx, mut subtrie)| {
2542 let _guard = parent_span.enter();
2543 subtrie.update_cached_rlp(new_epoch);
2544 (idx, subtrie)
2545 })
2546 .collect();
2547 }
2548 }
2549
2550 if taken.is_empty() && self.upper_arena[self.root].is_cached() {
2553 return;
2554 }
2555
2556 taken.sort_unstable_by_key(|(_, b)| Reverse(b.path));
2560
2561 self.buffers.cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2562
2563 loop {
2564 let result = self.buffers.cursor.next(&mut self.upper_arena, |_, child| match child {
2565 ArenaSparseNode::Branch(_) | ArenaSparseNode::Subtrie(_) => !child.is_cached(),
2566 ArenaSparseNode::TakenSubtrie => true,
2567 _ => false,
2568 });
2569
2570 match result {
2571 NextResult::Done => break,
2572 NextResult::Branch => continue,
2573 NextResult::NonBranch => {}
2574 }
2575
2576 let head_idx = self.buffers.cursor.head().expect("cursor is non-empty").index;
2578
2579 if matches!(&self.upper_arena[head_idx], ArenaSparseNode::TakenSubtrie) {
2580 let (_, subtrie) = taken.pop().expect("taken subtries must not be exhausted");
2581 debug_assert_eq!(
2582 subtrie.path,
2583 self.buffers.cursor.head().expect("cursor is non-empty").path,
2584 "taken subtrie path mismatch",
2585 );
2586 self.upper_arena[head_idx] = ArenaSparseNode::Subtrie(subtrie);
2587 }
2588
2589 self.update_upper_subtrie(head_idx, new_epoch);
2590 }
2591 }
2592
2593 fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>> {
2594 Self::get_leaf_value_in_arena(&self.upper_arena, self.root, full_path, 0)
2595 }
2596
2597 fn find_leaf(
2598 &self,
2599 full_path: &Nibbles,
2600 expected_value: Option<&Vec<u8>>,
2601 ) -> Result<LeafLookup, LeafLookupError> {
2602 Self::find_leaf_in_arena(&self.upper_arena, self.root, full_path, 0, expected_value)
2603 }
2604
2605 fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates> {
2606 self.buffers
2607 .updates
2608 .as_ref()
2609 .map_or(Cow::Owned(SparseTrieUpdates::default()), Cow::Borrowed)
2610 }
2611
2612 fn take_updates(&mut self) -> SparseTrieUpdates {
2613 match self.buffers.updates.take() {
2614 Some(updates) => {
2615 self.buffers.updates = Some(SparseTrieUpdates::with_capacity(
2616 updates.updated_nodes.len(),
2617 updates.removed_nodes.len(),
2618 ));
2619 updates
2620 }
2621 None => SparseTrieUpdates::default(),
2622 }
2623 }
2624
2625 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2626 fn wipe(&mut self) {
2627 trace!(target: TRACE_TARGET, "Wiping arena trie");
2628 self.clear();
2629 *self.upper_arena[self.root].state_mut() = ArenaSparseNodeState::Dirty;
2630 self.buffers.updates = self.buffers.updates.is_some().then(SparseTrieUpdates::wiped);
2631 }
2632
2633 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2634 fn clear(&mut self) {
2635 #[cfg(feature = "trie-debug")]
2636 self.debug_recorder.reset();
2637
2638 self.upper_arena = SlotMap::new();
2639 self.root = self
2640 .upper_arena
2641 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
2642 self.buffers.clear();
2643 }
2644
2645 #[instrument(
2646 level = "trace",
2647 target = TRACE_TARGET,
2648 skip_all,
2649 fields(prune_before = prune_before.get()),
2650 )]
2651 fn prune(&mut self, prune_before: TrieNodeEpoch) -> usize {
2652 assert!(self.root_epoch().is_some(), "prune cannot run on a dirty trie");
2653
2654 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2656 return 0;
2657 }
2658
2659 let threshold = self.parallelism_thresholds.min_leaves_for_prune;
2660
2661 let mut cursor = mem::take(&mut self.buffers.cursor);
2662 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2663
2664 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2666
2667 let mut pruned = 0;
2668
2669 loop {
2670 let result = cursor.next(&mut self.upper_arena, |_, child| {
2671 matches!(
2672 child,
2673 ArenaSparseNode::Branch(_) |
2674 ArenaSparseNode::Subtrie(_) |
2675 ArenaSparseNode::Leaf { .. }
2676 )
2677 });
2678
2679 if matches!(result, NextResult::Done) {
2680 break
2681 }
2682
2683 let head = cursor.head().expect("cursor is non-empty");
2684 let head_idx = head.index;
2685 let head_path = head.path;
2686
2687 match &self.upper_arena[head_idx] {
2688 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. } => {
2689 if cursor.depth() == 0 {
2691 continue;
2692 }
2693
2694 let node_epoch = self.upper_arena[head_idx]
2695 .state_ref()
2696 .and_then(ArenaSparseNodeState::cached_epoch)
2697 .expect("prune must run after hashing");
2698 if !node_epoch.should_prune(prune_before) {
2699 continue;
2700 }
2701
2702 Self::remove_pruned_node(
2703 &mut self.upper_arena,
2704 &cursor,
2705 head_idx,
2706 head_path.last(),
2707 );
2708 pruned += 1;
2709 }
2710 ArenaSparseNode::Subtrie(_) => {
2711 let root_epoch = self.upper_arena[head_idx]
2712 .state_ref()
2713 .and_then(ArenaSparseNodeState::cached_epoch)
2714 .expect("prune must run after hashing");
2715 if root_epoch.should_prune(prune_before) {
2716 let removed = Self::remove_pruned_node(
2717 &mut self.upper_arena,
2718 &cursor,
2719 head_idx,
2720 head_path.last(),
2721 );
2722 let ArenaSparseNode::Subtrie(s) = &removed else { unreachable!() };
2723 pruned += s.arena.len();
2724 self.recycle_subtrie(removed);
2725 continue;
2726 }
2727
2728 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[head_idx] else {
2729 unreachable!()
2730 };
2731 if subtrie.num_leaves >= threshold {
2732 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2733 &mut self.upper_arena[head_idx],
2734 ArenaSparseNode::TakenSubtrie,
2735 ) else {
2736 unreachable!()
2737 };
2738 taken.push((head_idx, subtrie));
2739 } else {
2740 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx]
2741 else {
2742 unreachable!()
2743 };
2744 pruned += subtrie.prune(prune_before);
2745 }
2746 }
2747 _ => unreachable!("NonBranch in prune walk must be Subtrie, Leaf, or Branch"),
2748 }
2749 }
2750
2751 self.buffers.cursor = cursor;
2752
2753 if !taken.is_empty() {
2754 if taken.len() == 1 {
2756 let (_, ref mut subtrie) = taken[0];
2757 pruned += subtrie.prune(prune_before);
2758 } else {
2759 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2760
2761 let parent_span = tracing::Span::current();
2762 pruned += taken
2763 .par_iter_mut()
2764 .map(|(_, subtrie)| {
2765 let _guard = parent_span.enter();
2766 let _span = tracing::trace_span!(
2767 target: TRACE_TARGET,
2768 "subtrie_prune",
2769 subtrie = ?subtrie.path,
2770 )
2771 .entered();
2772
2773 subtrie.prune(prune_before)
2774 })
2775 .sum::<usize>();
2776 }
2777
2778 for (child_idx, subtrie) in taken {
2780 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
2781 }
2782 }
2783
2784 if pruned > 0 {
2785 compact_arena(&mut self.upper_arena, &mut self.root);
2786 }
2787
2788 #[cfg(feature = "trie-debug")]
2789 self.record_initial_state();
2790
2791 pruned
2792 }
2793
2794 #[instrument(
2795 level = "trace",
2796 target = TRACE_TARGET,
2797 skip_all,
2798 fields(num_updates = updates.len()),
2799 )]
2800 fn update_leaves(
2801 &mut self,
2802 updates: &mut B256Map<LeafUpdate>,
2803 mut proof_required_fn: impl FnMut(B256, ProofV2TargetParent),
2804 ) -> SparseTrieResult<()> {
2805 if updates.is_empty() {
2806 return Ok(());
2807 }
2808
2809 #[cfg(feature = "trie-debug")]
2810 let recorded_updates: Vec<_> =
2811 updates.iter().map(|(k, v)| (*k, LeafUpdateRecord::from(v))).collect();
2812 #[cfg(feature = "trie-debug")]
2813 let mut recorded_proof_targets: Vec<(B256, Option<usize>)> = Vec::new();
2814
2815 let mut sorted: Vec<_> =
2817 updates.drain().map(|(key, update)| (key, Nibbles::unpack(key), update)).collect();
2818 sorted.sort_unstable_by_key(|entry| entry.1);
2819
2820 let threshold = self.parallelism_thresholds.min_updates;
2821 let parallelize_distributed_updates = sorted.len() >= threshold.saturating_mul(4);
2822
2823 let mut cursor = mem::take(&mut self.buffers.cursor);
2824 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2825
2826 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, core::ops::Range<usize>)> = Vec::new();
2828
2829 let mut update_idx = 0;
2830 while update_idx < sorted.len() {
2831 let (key, ref full_path, ref update) = sorted[update_idx];
2832
2833 let find_result = cursor.seek(&mut self.upper_arena, full_path);
2834
2835 match find_result {
2836 SeekResult::Blinded => {
2838 let logical_len = cursor.head_logical_branch_path_len(&self.upper_arena);
2839 let parent = ProofV2TargetParent::new(logical_len);
2840 trace!(target: TRACE_TARGET, ?key, ?parent, "Update hit blinded node, requesting proof");
2841 proof_required_fn(key, parent);
2842 #[cfg(feature = "trie-debug")]
2843 recorded_proof_targets.push((key, parent.path_len()));
2844 updates.insert(key, update.clone());
2845 }
2846 SeekResult::RevealedSubtrie => {
2848 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2849 let child_idx = subtrie_entry.index;
2850 let subtrie_root_path = subtrie_entry.path;
2851
2852 let subtrie_start = update_idx;
2853 while update_idx < sorted.len() &&
2854 sorted[update_idx].1.starts_with(&subtrie_root_path)
2855 {
2856 update_idx += 1;
2857 }
2858
2859 let subtrie_updates = &sorted[subtrie_start..update_idx];
2860
2861 if let Some(proof) = Self::check_subtrie_collapse_needs_proof(
2865 &self.upper_arena,
2866 &cursor,
2867 subtrie_updates,
2868 ) {
2869 trace!(target: TRACE_TARGET, proof_key = ?proof.key, proof_parent = ?proof.parent, "Subtrie collapse would need blinded sibling, requesting proof");
2870 proof_required_fn(proof.key, proof.parent);
2871 #[cfg(feature = "trie-debug")]
2872 recorded_proof_targets.push((proof.key, proof.parent.path_len()));
2873 for &(key, _, ref update) in subtrie_updates {
2874 updates.insert(key, update.clone());
2875 }
2876 continue;
2878 }
2879
2880 let num_subtrie_updates = update_idx - subtrie_start;
2881
2882 let all_removals = subtrie_updates
2886 .iter()
2887 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_)))
2891 .all(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()));
2892 let subtrie_num_leaves = match &self.upper_arena[child_idx] {
2893 ArenaSparseNode::Subtrie(s) => s.num_leaves,
2894 _ => 0,
2895 };
2896 let might_empty_subtrie =
2897 all_removals && num_subtrie_updates as u64 >= subtrie_num_leaves;
2898
2899 if (num_subtrie_updates >= threshold || parallelize_distributed_updates) &&
2900 !might_empty_subtrie
2901 {
2902 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Taking subtrie for parallel update");
2904 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2905 &mut self.upper_arena[child_idx],
2906 ArenaSparseNode::TakenSubtrie,
2907 ) else {
2908 unreachable!()
2909 };
2910 taken.push((child_idx, subtrie, subtrie_start..update_idx));
2911 } else {
2912 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Updating subtrie inline");
2914 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2915 else {
2916 unreachable!()
2917 };
2918
2919 subtrie.update_leaves(subtrie_updates);
2920
2921 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
2922 proof_required_fn(proof.key, proof.parent);
2923 #[cfg(feature = "trie-debug")]
2924 recorded_proof_targets.push((proof.key, proof.parent.path_len()));
2925 let (key, _, ref update) = subtrie_updates[target_idx];
2926 updates.insert(key, update.clone());
2927 }
2928
2929 self.maybe_unwrap_subtrie(&mut cursor);
2931 }
2932
2933 continue;
2935 }
2936 find_result @ (SeekResult::EmptyRoot |
2938 SeekResult::RevealedLeaf |
2939 SeekResult::Diverged |
2940 SeekResult::NoChild { .. }) => match update {
2941 LeafUpdate::Changed(v) if !v.is_empty() => {
2942 let (result, _deltas) = Self::upsert_leaf(
2943 &mut self.upper_arena,
2944 &mut cursor,
2945 &mut self.root,
2946 full_path,
2947 v,
2948 find_result,
2949 );
2950 match result {
2951 UpsertLeafResult::NewChild => {
2952 let head = cursor.head().expect("cursor is non-empty");
2953 if Self::should_be_subtrie(head.path.len()) {
2954 self.maybe_wrap_in_subtrie(head.index, &head.path);
2957 } else {
2958 self.maybe_wrap_branch_children(&cursor);
2962 }
2963 }
2964 UpsertLeafResult::NewLeaf => {
2965 self.maybe_wrap_branch_children(&cursor);
2968 }
2969 UpsertLeafResult::Updated => {}
2970 }
2971 }
2972 LeafUpdate::Changed(_) => {
2973 let (result, _deltas) = Self::remove_leaf(
2974 &mut self.upper_arena,
2975 &mut cursor,
2976 &mut self.root,
2977 key,
2978 full_path,
2979 find_result,
2980 &mut self.buffers.updates,
2981 );
2982 match result {
2983 RemoveLeafResult::NeedsProof { key, proof_key, parent } => {
2984 proof_required_fn(proof_key, parent);
2985 #[cfg(feature = "trie-debug")]
2986 recorded_proof_targets.push((proof_key, parent.path_len()));
2987 let update =
2988 mem::replace(&mut sorted[update_idx].2, LeafUpdate::Touched);
2989 updates.insert(key, update);
2990 }
2991 RemoveLeafResult::Removed => {
2992 self.maybe_collapse_or_remove_branch(&mut cursor);
3001 let head =
3002 cursor.head().expect("cursor always has root after collapse");
3003 self.maybe_wrap_in_subtrie(head.index, &head.path);
3004 }
3005 RemoveLeafResult::NotFound => {}
3006 }
3007 }
3008 LeafUpdate::Touched => {}
3009 },
3010 }
3011
3012 update_idx += 1;
3013 }
3014
3015 cursor.drain(&mut self.upper_arena);
3017 self.buffers.cursor = cursor;
3018
3019 if taken.is_empty() {
3020 #[cfg(debug_assertions)]
3021 self.debug_assert_subtrie_structure();
3022
3023 #[cfg(feature = "trie-debug")]
3024 self.debug_recorder.record(RecordedOp::UpdateLeaves {
3025 updates: recorded_updates,
3026 proof_targets: recorded_proof_targets,
3027 });
3028
3029 return Ok(());
3030 }
3031
3032 if taken.len() == 1 {
3034 let (_, ref mut subtrie, ref range) = taken[0];
3035 subtrie.update_leaves(&sorted[range.clone()]);
3036 } else {
3037 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
3038
3039 let parent_span = tracing::Span::current();
3040 taken.par_iter_mut().for_each(|(_, subtrie, range)| {
3041 let _guard = parent_span.enter();
3042 subtrie.update_leaves(&sorted[range.clone()]);
3043 });
3044 }
3045
3046 let taken_paths: Vec<Nibbles> = taken.iter().map(|(_, s, _)| s.path).collect();
3049 for (child_idx, mut subtrie, range) in taken {
3050 let subtrie_updates = &sorted[range];
3051 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
3052 proof_required_fn(proof.key, proof.parent);
3053 #[cfg(feature = "trie-debug")]
3054 recorded_proof_targets.push((proof.key, proof.parent.path_len()));
3055 let (key, _, ref update) = subtrie_updates[target_idx];
3056 updates.insert(key, update.clone());
3057 }
3058
3059 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
3061 }
3062
3063 {
3069 let mut cursor = mem::take(&mut self.buffers.cursor);
3070 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
3071
3072 for path in &taken_paths {
3073 let find_result = cursor.seek(&mut self.upper_arena, path);
3074 match find_result {
3075 SeekResult::RevealedSubtrie => {
3076 debug_assert!(
3077 {
3078 let head_idx = cursor.head().expect("cursor is non-empty").index;
3079 !matches!(
3080 &self.upper_arena[head_idx],
3081 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot { .. })
3082 )
3083 },
3084 "taken subtrie became EmptyRoot — should have been forced inline"
3085 );
3086
3087 cursor.pop(&mut self.upper_arena);
3088
3089 self.maybe_collapse_or_remove_branch(&mut cursor);
3093 }
3094 _ => {
3095 }
3098 }
3099 }
3100
3101 cursor.drain(&mut self.upper_arena);
3102 self.buffers.cursor = cursor;
3103 }
3104
3105 #[cfg(debug_assertions)]
3106 self.debug_assert_subtrie_structure();
3107
3108 #[cfg(feature = "trie-debug")]
3109 self.debug_recorder.record(RecordedOp::UpdateLeaves {
3110 updates: recorded_updates,
3111 proof_targets: recorded_proof_targets,
3112 });
3113
3114 Ok(())
3115 }
3116
3117 #[cfg(feature = "trie-debug")]
3118 fn take_debug_recorder(&mut self) -> TrieDebugRecorder {
3119 core::mem::take(&mut self.debug_recorder)
3120 }
3121}
3122
3123#[cfg(test)]
3124mod tests {
3125 use super::TRACE_TARGET;
3126 use crate::{
3127 ArenaParallelSparseTrie, ArenaParallelismThresholds, LeafUpdate, SparseTrie, TrieNodeEpoch,
3128 };
3129 use alloy_primitives::{map::B256Map, B256, U256};
3130 use rand::{seq::SliceRandom, Rng, SeedableRng};
3131 use reth_trie::test_utils::TrieTestHarness;
3132 use reth_trie_common::ProofV2Target;
3133 use std::collections::BTreeMap;
3134 use tracing::{info, trace};
3135
3136 const fn epoch(value: u64) -> TrieNodeEpoch {
3137 TrieNodeEpoch::new(value)
3138 }
3139
3140 struct ArenaTrieTestHarness {
3145 inner: TrieTestHarness,
3147 }
3148
3149 impl std::ops::Deref for ArenaTrieTestHarness {
3150 type Target = TrieTestHarness;
3151 fn deref(&self) -> &Self::Target {
3152 &self.inner
3153 }
3154 }
3155
3156 impl std::ops::DerefMut for ArenaTrieTestHarness {
3157 fn deref_mut(&mut self) -> &mut Self::Target {
3158 &mut self.inner
3159 }
3160 }
3161
3162 impl ArenaTrieTestHarness {
3163 fn new(storage: BTreeMap<B256, U256>) -> Self {
3165 Self { inner: TrieTestHarness::new(storage) }
3166 }
3167
3168 fn assert_changes(
3172 &self,
3173 apst: &mut ArenaParallelSparseTrie,
3174 changes: BTreeMap<B256, U256>,
3175 ) {
3176 let (expected_root, mut expected_trie_updates) = if changes.is_empty() {
3178 (self.original_root(), Default::default())
3179 } else {
3180 self.get_root_with_updates(&changes)
3181 };
3182
3183 self.minimize_trie_updates(&mut expected_trie_updates);
3184
3185 let mut leaf_updates: B256Map<LeafUpdate> = changes
3188 .iter()
3189 .map(|(&slot, &value)| {
3190 let rlp_value = if value == U256::ZERO {
3191 Vec::new()
3192 } else {
3193 alloy_rlp::encode_fixed_size(&value).to_vec()
3194 };
3195 (slot, LeafUpdate::Changed(rlp_value))
3196 })
3197 .collect();
3198
3199 loop {
3202 let mut targets: Vec<ProofV2Target> = Vec::new();
3203 apst.update_leaves(&mut leaf_updates, |key, parent| {
3204 targets.push(ProofV2Target::new(key).with_parent(parent));
3205 })
3206 .expect("update_leaves should succeed");
3207
3208 if targets.is_empty() {
3209 break;
3210 }
3211
3212 let (mut proof_nodes, _) = self.proof_v2(&mut targets);
3213 apst.reveal_nodes(&mut proof_nodes).expect("reveal_nodes should succeed");
3214 }
3215
3216 let actual_root = apst.root(epoch(0));
3218 let mut actual_updates = apst.take_updates();
3219
3220 actual_updates.updated_nodes.retain(|path, node| {
3223 self.storage_trie_updates().storage_nodes.get(path) != Some(node)
3224 });
3225 actual_updates
3226 .removed_nodes
3227 .retain(|path| self.storage_trie_updates().storage_nodes.contains_key(path));
3228
3229 pretty_assertions::assert_eq!(
3230 expected_trie_updates.storage_nodes.into_iter().collect::<Vec<_>>().sort(),
3231 actual_updates.updated_nodes.into_iter().collect::<Vec<_>>().sort(),
3232 "updated nodes mismatch"
3233 );
3234 pretty_assertions::assert_eq!(
3235 expected_trie_updates.removed_nodes.into_iter().collect::<Vec<_>>().sort(),
3236 actual_updates.removed_nodes.into_iter().collect::<Vec<_>>().sort(),
3237 "removed nodes mismatch"
3238 );
3239 assert_eq!(expected_root, actual_root, "storage root mismatch");
3240 }
3241 }
3242
3243 use proptest::prelude::*;
3244 use proptest_arbitrary_interop::arb;
3245
3246 fn build_changeset(
3253 base: &BTreeMap<B256, U256>,
3254 new_keys: BTreeMap<B256, U256>,
3255 overlap_pct: f64,
3256 delete_pct: f64,
3257 rng: &mut rand::rngs::StdRng,
3258 ) -> BTreeMap<B256, U256> {
3259 let num_overlap = (base.len() as f64 * overlap_pct) as usize;
3260 let num_delete = (num_overlap as f64 * delete_pct) as usize;
3261
3262 let mut all_keys: Vec<B256> = base.keys().copied().collect();
3263 all_keys.shuffle(rng);
3264 let overlap_keys = &all_keys[..num_overlap];
3265
3266 let mut changeset = new_keys;
3267 for (i, &key) in overlap_keys.iter().enumerate() {
3268 let value =
3269 if i < num_delete { U256::ZERO } else { U256::from(rng.random::<u64>() | 1) };
3270 changeset.entry(key).or_insert(value);
3271 }
3272 changeset
3273 }
3274
3275 proptest! {
3276 #![proptest_config(ProptestConfig::with_cases(1000))]
3277 #[test]
3278 fn arena_trie_proptest(
3279 initial in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=100usize),
3280 changeset1_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3281 changeset2_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3282 overlap_pct in 0.0..=0.5f64,
3283 delete_pct in 0.0..=0.33f64, shuffle_seed in arb::<u64>(),
3285 ) {
3286 reth_tracing::init_test_tracing();
3287 info!(target: TRACE_TARGET, ?shuffle_seed, "PROPTEST START");
3288
3289 let initial: BTreeMap<B256, U256> = initial.into_iter()
3291 .filter(|(_, v)| *v != U256::ZERO)
3292 .collect();
3293
3294 let mut rng = rand::rngs::StdRng::seed_from_u64(shuffle_seed);
3295
3296 let changeset1 = build_changeset(&initial, changeset1_new_keys, overlap_pct, delete_pct, &mut rng);
3297 for (i, (k, v)) in changeset1.iter().enumerate() {
3298 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 1 entry");
3299 }
3300
3301 let mut harness = ArenaTrieTestHarness::new(initial);
3302
3303 let root_node = harness.root_node();
3305 let mut apst = ArenaParallelSparseTrie::default().with_parallelism_thresholds(
3306 ArenaParallelismThresholds {
3307 min_dirty_leaves: 3,
3308 min_revealed_nodes: 3,
3309 min_updates: 3,
3310 min_leaves_for_prune: 3,
3311 },
3312 );
3313 apst.set_root(root_node.node, root_node.masks, true).expect("set_root should succeed");
3314
3315 harness.assert_changes(&mut apst, changeset1.clone());
3316
3317 harness.apply_changeset(changeset1);
3319
3320 apst.prune(epoch(1));
3323
3324 let changeset2 = build_changeset(harness.storage(), changeset2_new_keys, overlap_pct, delete_pct, &mut rng);
3325 for (i, (k, v)) in changeset2.iter().enumerate() {
3326 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 2 entry");
3327 }
3328
3329 harness.assert_changes(&mut apst, changeset2);
3330 }
3331 }
3332}