reth_trie/
verify.rs

1use crate::{
2    hashed_cursor::{HashedCursor, HashedCursorFactory},
3    progress::{IntermediateStateRootState, StateRootProgress},
4    trie::StateRoot,
5    trie_cursor::{
6        depth_first::{self, DepthFirstTrieIterator},
7        noop::NoopTrieCursorFactory,
8        TrieCursor, TrieCursorFactory,
9    },
10    Nibbles,
11};
12use alloy_primitives::B256;
13use alloy_trie::BranchNodeCompact;
14use reth_execution_errors::StateRootError;
15use reth_storage_errors::db::DatabaseError;
16use std::cmp::Ordering;
17use tracing::trace;
18
19/// Used by [`StateRootBranchNodesIter`] to iterate over branch nodes in a state root.
20#[derive(Debug)]
21enum BranchNode {
22    Account(Nibbles, BranchNodeCompact),
23    Storage(B256, Nibbles, BranchNodeCompact),
24}
25
26/// Iterates over branch nodes produced by a [`StateRoot`]. The `StateRoot` will only used the
27/// hashed accounts/storages tables, meaning it is recomputing the trie from scratch without the use
28/// of the trie tables.
29///
30/// [`BranchNode`]s are iterated over such that:
31/// * Account nodes and storage nodes may be interleaved.
32/// * Storage nodes for the same account will be ordered by ascending path relative to each other.
33/// * Account nodes will be ordered by ascending path relative to each other.
34/// * All storage nodes for one account will finish before storage nodes for another account are
35///   started. In other words, if the current storage account is not equal to the previous, the
36///   previous has no more nodes.
37#[derive(Debug)]
38struct StateRootBranchNodesIter<H> {
39    hashed_cursor_factory: H,
40    account_nodes: Vec<(Nibbles, BranchNodeCompact)>,
41    storage_tries: Vec<(B256, Vec<(Nibbles, BranchNodeCompact)>)>,
42    curr_storage: Option<(B256, Vec<(Nibbles, BranchNodeCompact)>)>,
43    intermediate_state: Option<Box<IntermediateStateRootState>>,
44    complete: bool,
45}
46
47impl<H> StateRootBranchNodesIter<H> {
48    fn new(hashed_cursor_factory: H) -> Self {
49        Self {
50            hashed_cursor_factory,
51            account_nodes: Default::default(),
52            storage_tries: Default::default(),
53            curr_storage: None,
54            intermediate_state: None,
55            complete: false,
56        }
57    }
58
59    /// Sorts a Vec of updates such that it is ready to be yielded from the `next` method. We yield
60    /// by popping off of the account/storage vecs, so we sort them in reverse order.
61    ///
62    /// Depth-first sorting is used because this is the order that the `HashBuilder` computes
63    /// branch nodes internally, even if it produces them as `B256Map`s.
64    fn sort_updates(updates: &mut [(Nibbles, BranchNodeCompact)]) {
65        updates.sort_unstable_by(|a, b| depth_first::cmp(&b.0, &a.0));
66    }
67}
68
69impl<H: HashedCursorFactory + Clone> Iterator for StateRootBranchNodesIter<H> {
70    type Item = Result<BranchNode, StateRootError>;
71
72    fn next(&mut self) -> Option<Self::Item> {
73        loop {
74            // If we already started iterating through a storage trie's updates, continue doing
75            // so.
76            if let Some((account, storage_updates)) = self.curr_storage.as_mut() &&
77                let Some((path, node)) = storage_updates.pop()
78            {
79                let node = BranchNode::Storage(*account, path, node);
80                return Some(Ok(node))
81            }
82
83            // If there's not a storage trie already being iterated over then check if there's a
84            // storage trie we could start iterating over.
85            if let Some((account, storage_updates)) = self.storage_tries.pop() {
86                debug_assert!(!storage_updates.is_empty());
87
88                self.curr_storage = Some((account, storage_updates));
89                continue;
90            }
91
92            // `storage_updates` is empty, check if there are account updates.
93            if let Some((path, node)) = self.account_nodes.pop() {
94                return Some(Ok(BranchNode::Account(path, node)))
95            }
96
97            // All data from any previous runs of the `StateRoot` has been produced, run the next
98            // partial computation, unless `StateRootProgress::Complete` has been returned in which
99            // case iteration is over.
100            if self.complete {
101                return None
102            }
103
104            let state_root =
105                StateRoot::new(NoopTrieCursorFactory, self.hashed_cursor_factory.clone())
106                    .with_intermediate_state(self.intermediate_state.take().map(|s| *s));
107
108            let updates = match state_root.root_with_progress() {
109                Err(err) => return Some(Err(err)),
110                Ok(StateRootProgress::Complete(_, _, updates)) => {
111                    self.complete = true;
112                    updates
113                }
114                Ok(StateRootProgress::Progress(intermediate_state, _, updates)) => {
115                    self.intermediate_state = Some(intermediate_state);
116                    updates
117                }
118            };
119
120            // collect account updates and sort them in descending order, so that when we pop them
121            // off the Vec they are popped in ascending order.
122            self.account_nodes.extend(updates.account_nodes);
123            Self::sort_updates(&mut self.account_nodes);
124
125            self.storage_tries = updates
126                .storage_tries
127                .into_iter()
128                .filter_map(|(account, t)| {
129                    (!t.storage_nodes.is_empty()).then(|| {
130                        let mut storage_nodes = t.storage_nodes.into_iter().collect::<Vec<_>>();
131                        Self::sort_updates(&mut storage_nodes);
132                        (account, storage_nodes)
133                    })
134                })
135                .collect::<Vec<_>>();
136
137            // `root_with_progress` will output storage updates ordered by their account hash. If
138            // `root_with_progress` only returns a partial result then it will pick up where
139            // it left off in the storage trie on the next run.
140            //
141            // By sorting by the account we ensure that we continue with the partially processed
142            // trie (the last of the previous run) first. We sort in reverse order because we pop
143            // off of this Vec.
144            self.storage_tries.sort_unstable_by(|a, b| b.0.cmp(&a.0));
145
146            // loop back to the top.
147        }
148    }
149}
150
151/// Output describes an inconsistency found when comparing the hashed state tables
152/// ([`HashedCursorFactory`]) with that of the trie tables ([`TrieCursorFactory`]). The hashed
153/// tables are considered the source of truth; outputs are on the part of the trie tables.
154#[derive(Debug, Clone, PartialEq, Eq)]
155pub enum Output {
156    /// An extra account node was found.
157    AccountExtra(Nibbles, BranchNodeCompact),
158    /// An extra storage node was found.
159    StorageExtra(B256, Nibbles, BranchNodeCompact),
160    /// An account node had the wrong value.
161    AccountWrong {
162        /// Path of the node
163        path: Nibbles,
164        /// The node's expected value.
165        expected: BranchNodeCompact,
166        /// The node's found value.
167        found: BranchNodeCompact,
168    },
169    /// A storage node had the wrong value.
170    StorageWrong {
171        /// The account the storage trie belongs to.
172        account: B256,
173        /// Path of the node
174        path: Nibbles,
175        /// The node's expected value.
176        expected: BranchNodeCompact,
177        /// The node's found value.
178        found: BranchNodeCompact,
179    },
180    /// An account node was missing.
181    AccountMissing(Nibbles, BranchNodeCompact),
182    /// A storage node was missing.
183    StorageMissing(B256, Nibbles, BranchNodeCompact),
184    /// Progress indicator with the last seen account path.
185    Progress(Nibbles),
186}
187
188/// Verifies the contents of a trie table against some other data source which is able to produce
189/// stored trie nodes.
190#[derive(Debug)]
191struct SingleVerifier<I> {
192    account: Option<B256>, // None for accounts trie
193    trie_iter: I,
194    curr: Option<(Nibbles, BranchNodeCompact)>,
195}
196
197impl<C: TrieCursor> SingleVerifier<DepthFirstTrieIterator<C>> {
198    fn new(account: Option<B256>, trie_cursor: C) -> Result<Self, DatabaseError> {
199        let mut trie_iter = DepthFirstTrieIterator::new(trie_cursor);
200        let curr = trie_iter.next().transpose()?;
201        Ok(Self { account, trie_iter, curr })
202    }
203
204    const fn output_extra(&self, path: Nibbles, node: BranchNodeCompact) -> Output {
205        if let Some(account) = self.account {
206            Output::StorageExtra(account, path, node)
207        } else {
208            Output::AccountExtra(path, node)
209        }
210    }
211
212    const fn output_wrong(
213        &self,
214        path: Nibbles,
215        expected: BranchNodeCompact,
216        found: BranchNodeCompact,
217    ) -> Output {
218        if let Some(account) = self.account {
219            Output::StorageWrong { account, path, expected, found }
220        } else {
221            Output::AccountWrong { path, expected, found }
222        }
223    }
224
225    const fn output_missing(&self, path: Nibbles, node: BranchNodeCompact) -> Output {
226        if let Some(account) = self.account {
227            Output::StorageMissing(account, path, node)
228        } else {
229            Output::AccountMissing(path, node)
230        }
231    }
232
233    /// Called with the next path and node in the canonical sequence of stored trie nodes. Will
234    /// append to the given `outputs` Vec if walking the trie cursor produces data
235    /// inconsistent with that given.
236    ///
237    /// `next` must be called with paths in depth-first order.
238    fn next(
239        &mut self,
240        outputs: &mut Vec<Output>,
241        path: Nibbles,
242        node: BranchNodeCompact,
243    ) -> Result<(), DatabaseError> {
244        loop {
245            // `curr` is None only if the end of the iterator has been reached. Any further nodes
246            // found must be considered missing.
247            if self.curr.is_none() {
248                outputs.push(self.output_missing(path, node));
249                return Ok(())
250            }
251
252            let (curr_path, curr_node) = self.curr.as_ref().expect("not None");
253            trace!(target: "trie::verify", account=?self.account, ?curr_path, ?path, "Current cursor node");
254
255            // Use depth-first ordering for comparison
256            match depth_first::cmp(&path, curr_path) {
257                Ordering::Less => {
258                    // If the given path comes before the cursor's current path in depth-first
259                    // order, then the given path was not produced by the cursor.
260                    outputs.push(self.output_missing(path, node));
261                    return Ok(())
262                }
263                Ordering::Equal => {
264                    // If the current path matches the given one (happy path) but the nodes
265                    // aren't equal then we produce a wrong node. Either way we want to move the
266                    // iterator forward.
267                    if *curr_node != node {
268                        outputs.push(self.output_wrong(path, node, curr_node.clone()))
269                    }
270                    self.curr = self.trie_iter.next().transpose()?;
271                    return Ok(())
272                }
273                Ordering::Greater => {
274                    // If the given path comes after the current path in depth-first order,
275                    // it means the cursor's path was not found by the caller (otherwise it would
276                    // have hit the equal case) and so is extraneous.
277                    outputs.push(self.output_extra(*curr_path, curr_node.clone()));
278                    self.curr = self.trie_iter.next().transpose()?;
279                    // back to the top of the loop to check the latest `self.curr` value against the
280                    // given path/node.
281                }
282            }
283        }
284    }
285
286    /// Must be called once there are no more calls to `next` to made. All further nodes produced
287    /// by the iterator will be considered extraneous.
288    fn finalize(&mut self, outputs: &mut Vec<Output>) -> Result<(), DatabaseError> {
289        loop {
290            if let Some((curr_path, curr_node)) = self.curr.take() {
291                outputs.push(self.output_extra(curr_path, curr_node));
292                self.curr = self.trie_iter.next().transpose()?;
293            } else {
294                return Ok(())
295            }
296        }
297    }
298}
299
300/// Checks that data stored in the trie database is consistent, using hashed accounts/storages
301/// database tables as the source of truth. This will iteratively recompute the entire trie based
302/// on the hashed state, and produce any discovered [`Output`]s via the `next` method.
303#[derive(Debug)]
304pub struct Verifier<'a, T: TrieCursorFactory, H> {
305    trie_cursor_factory: &'a T,
306    hashed_cursor_factory: H,
307    branch_node_iter: StateRootBranchNodesIter<H>,
308    outputs: Vec<Output>,
309    account: SingleVerifier<DepthFirstTrieIterator<T::AccountTrieCursor<'a>>>,
310    storage: Option<(B256, SingleVerifier<DepthFirstTrieIterator<T::StorageTrieCursor<'a>>>)>,
311    complete: bool,
312}
313
314impl<'a, T: TrieCursorFactory, H: HashedCursorFactory + Clone> Verifier<'a, T, H> {
315    /// Creates a new verifier instance.
316    pub fn new(
317        trie_cursor_factory: &'a T,
318        hashed_cursor_factory: H,
319    ) -> Result<Self, DatabaseError> {
320        Ok(Self {
321            trie_cursor_factory,
322            hashed_cursor_factory: hashed_cursor_factory.clone(),
323            branch_node_iter: StateRootBranchNodesIter::new(hashed_cursor_factory),
324            outputs: Default::default(),
325            account: SingleVerifier::new(None, trie_cursor_factory.account_trie_cursor()?)?,
326            storage: None,
327            complete: false,
328        })
329    }
330}
331
332impl<'a, T: TrieCursorFactory, H: HashedCursorFactory + Clone> Verifier<'a, T, H> {
333    fn new_storage(
334        &mut self,
335        account: B256,
336        path: Nibbles,
337        node: BranchNodeCompact,
338    ) -> Result<(), DatabaseError> {
339        let trie_cursor = self.trie_cursor_factory.storage_trie_cursor(account)?;
340        let mut storage = SingleVerifier::new(Some(account), trie_cursor)?;
341        storage.next(&mut self.outputs, path, node)?;
342        self.storage = Some((account, storage));
343        Ok(())
344    }
345
346    /// This method is called using the account hashes at the boundary of [`BranchNode::Storage`]
347    /// sequences, ie once the [`StateRootBranchNodesIter`] has begun yielding storage nodes for a
348    /// different account than it was yielding previously. All accounts between the two should have
349    /// empty storages.
350    fn verify_empty_storages(
351        &mut self,
352        last_account: B256,
353        next_account: B256,
354        start_inclusive: bool,
355        end_inclusive: bool,
356    ) -> Result<(), DatabaseError> {
357        let mut account_cursor = self.hashed_cursor_factory.hashed_account_cursor()?;
358        let mut account_seeked = false;
359
360        if !start_inclusive {
361            account_seeked = true;
362            account_cursor.seek(last_account)?;
363        }
364
365        loop {
366            let Some((curr_account, _)) = (if account_seeked {
367                account_cursor.next()?
368            } else {
369                account_seeked = true;
370                account_cursor.seek(last_account)?
371            }) else {
372                return Ok(())
373            };
374
375            if curr_account < next_account || (end_inclusive && curr_account == next_account) {
376                trace!(target: "trie::verify", account = ?curr_account, "Verying account has empty storage");
377
378                let mut storage_cursor =
379                    self.trie_cursor_factory.storage_trie_cursor(curr_account)?;
380                let mut seeked = false;
381                while let Some((path, node)) = if seeked {
382                    storage_cursor.next()?
383                } else {
384                    seeked = true;
385                    storage_cursor.seek(Nibbles::new())?
386                } {
387                    self.outputs.push(Output::StorageExtra(curr_account, path, node));
388                }
389            } else {
390                return Ok(())
391            }
392        }
393    }
394
395    fn try_next(&mut self) -> Result<(), StateRootError> {
396        match self.branch_node_iter.next().transpose()? {
397            None => {
398                self.account.finalize(&mut self.outputs)?;
399                if let Some((prev_account, storage)) = self.storage.as_mut() {
400                    storage.finalize(&mut self.outputs)?;
401
402                    // If there was a previous storage account, and it is the final one, then we
403                    // need to validate that all accounts coming after it have empty storages.
404                    let prev_account = *prev_account;
405
406                    // Calculate the max possible account address (all bits set).
407                    let max_account = B256::from([0xFFu8; 32]);
408
409                    self.verify_empty_storages(prev_account, max_account, false, true)?;
410                }
411                self.complete = true;
412            }
413            Some(BranchNode::Account(path, node)) => {
414                trace!(target: "trie::verify", ?path, "Account node from state root");
415                self.account.next(&mut self.outputs, path, node)?;
416                // Push progress indicator
417                if !path.is_empty() {
418                    self.outputs.push(Output::Progress(path));
419                }
420            }
421            Some(BranchNode::Storage(account, path, node)) => {
422                trace!(target: "trie::verify", ?account, ?path, "Storage node from state root");
423                match self.storage.as_mut() {
424                    None => {
425                        // First storage account - check for any empty storages before it
426                        self.verify_empty_storages(B256::ZERO, account, true, false)?;
427                        self.new_storage(account, path, node)?;
428                    }
429                    Some((prev_account, storage)) if *prev_account == account => {
430                        storage.next(&mut self.outputs, path, node)?;
431                    }
432                    Some((prev_account, storage)) => {
433                        storage.finalize(&mut self.outputs)?;
434                        // Clear any storage entries between the previous account and the new one
435                        let prev_account = *prev_account;
436                        self.verify_empty_storages(prev_account, account, false, false)?;
437                        self.new_storage(account, path, node)?;
438                    }
439                }
440            }
441        }
442
443        // If any outputs were appended we want to reverse them, so they are popped off
444        // in the same order they were appended.
445        self.outputs.reverse();
446        Ok(())
447    }
448}
449
450impl<'a, T: TrieCursorFactory, H: HashedCursorFactory + Clone> Iterator for Verifier<'a, T, H> {
451    type Item = Result<Output, StateRootError>;
452
453    fn next(&mut self) -> Option<Self::Item> {
454        loop {
455            if let Some(output) = self.outputs.pop() {
456                return Some(Ok(output))
457            }
458
459            if self.complete {
460                return None
461            }
462
463            if let Err(err) = self.try_next() {
464                return Some(Err(err))
465            }
466        }
467    }
468}
469
470#[cfg(test)]
471mod tests {
472    use super::*;
473    use crate::{
474        hashed_cursor::mock::MockHashedCursorFactory,
475        trie_cursor::mock::{MockTrieCursor, MockTrieCursorFactory},
476    };
477    use alloy_primitives::{address, keccak256, map::B256Map, U256};
478    use alloy_trie::TrieMask;
479    use assert_matches::assert_matches;
480    use reth_primitives_traits::Account;
481    use std::collections::BTreeMap;
482
483    /// Helper function to create a simple test `BranchNodeCompact`
484    fn test_branch_node(
485        state_mask: u16,
486        tree_mask: u16,
487        hash_mask: u16,
488        hashes: Vec<B256>,
489    ) -> BranchNodeCompact {
490        // Ensure the number of hashes matches the number of bits set in hash_mask
491        let expected_hashes = hash_mask.count_ones() as usize;
492        let mut final_hashes = hashes;
493        let mut counter = 100u8;
494        while final_hashes.len() < expected_hashes {
495            final_hashes.push(B256::from([counter; 32]));
496            counter += 1;
497        }
498        final_hashes.truncate(expected_hashes);
499
500        BranchNodeCompact::new(
501            TrieMask::new(state_mask),
502            TrieMask::new(tree_mask),
503            TrieMask::new(hash_mask),
504            final_hashes,
505            None,
506        )
507    }
508
509    /// Helper function to create a simple test `MockTrieCursor`
510    fn create_mock_cursor(trie_nodes: BTreeMap<Nibbles, BranchNodeCompact>) -> MockTrieCursor {
511        let factory = MockTrieCursorFactory::new(trie_nodes, B256Map::default());
512        factory.account_trie_cursor().unwrap()
513    }
514
515    #[test]
516    fn test_state_root_branch_nodes_iter_empty() {
517        // Test with completely empty state
518        let factory = MockHashedCursorFactory::new(BTreeMap::new(), B256Map::default());
519        let mut iter = StateRootBranchNodesIter::new(factory);
520
521        // Collect all results - with empty state, should complete without producing nodes
522        let mut count = 0;
523        for result in iter.by_ref() {
524            assert!(result.is_ok(), "Unexpected error: {:?}", result.unwrap_err());
525            count += 1;
526            // Prevent infinite loop in test
527            assert!(count <= 1000, "Too many iterations");
528        }
529
530        assert!(iter.complete);
531    }
532
533    #[test]
534    fn test_state_root_branch_nodes_iter_basic() {
535        // Simple test with a few accounts and storage
536        let mut accounts = BTreeMap::new();
537        let mut storage_tries = B256Map::default();
538
539        // Create test accounts
540        let addr1 = keccak256(address!("0000000000000000000000000000000000000001"));
541        accounts.insert(
542            addr1,
543            Account {
544                nonce: 1,
545                balance: U256::from(1000),
546                bytecode_hash: Some(keccak256(b"code1")),
547            },
548        );
549
550        // Add storage for the account
551        let mut storage1 = BTreeMap::new();
552        storage1.insert(keccak256(B256::from(U256::from(1))), U256::from(100));
553        storage1.insert(keccak256(B256::from(U256::from(2))), U256::from(200));
554        storage_tries.insert(addr1, storage1);
555
556        let factory = MockHashedCursorFactory::new(accounts, storage_tries);
557        let mut iter = StateRootBranchNodesIter::new(factory);
558
559        // Collect nodes and verify basic properties
560        let mut account_paths = Vec::new();
561        let mut storage_paths_by_account: B256Map<Vec<Nibbles>> = B256Map::default();
562        let mut iterations = 0;
563
564        for result in iter.by_ref() {
565            iterations += 1;
566            assert!(iterations <= 10000, "Too many iterations - possible infinite loop");
567
568            match result {
569                Ok(BranchNode::Account(path, _)) => {
570                    account_paths.push(path);
571                }
572                Ok(BranchNode::Storage(account, path, _)) => {
573                    storage_paths_by_account.entry(account).or_default().push(path);
574                }
575                Err(e) => panic!("Unexpected error: {:?}", e),
576            }
577        }
578
579        // Verify account paths are in ascending order
580        for i in 1..account_paths.len() {
581            assert!(
582                account_paths[i - 1] < account_paths[i],
583                "Account paths should be in ascending order"
584            );
585        }
586
587        // Verify storage paths for each account are in ascending order
588        for (account, paths) in storage_paths_by_account {
589            for i in 1..paths.len() {
590                assert!(
591                    paths[i - 1] < paths[i],
592                    "Storage paths for account {:?} should be in ascending order",
593                    account
594                );
595            }
596        }
597
598        assert!(iter.complete);
599    }
600
601    #[test]
602    fn test_state_root_branch_nodes_iter_multiple_accounts() {
603        // Test with multiple accounts to verify ordering
604        let mut accounts = BTreeMap::new();
605        let mut storage_tries = B256Map::default();
606
607        // Create multiple test addresses
608        for i in 1u8..=3 {
609            let addr = keccak256([i; 20]);
610            accounts.insert(
611                addr,
612                Account {
613                    nonce: i as u64,
614                    balance: U256::from(i as u64 * 1000),
615                    bytecode_hash: (i == 2).then(|| keccak256([i])),
616                },
617            );
618
619            // Add some storage for each account
620            let mut storage = BTreeMap::new();
621            for j in 0..i {
622                storage.insert(keccak256(B256::from(U256::from(j))), U256::from(j as u64 * 10));
623            }
624            if !storage.is_empty() {
625                storage_tries.insert(addr, storage);
626            }
627        }
628
629        let factory = MockHashedCursorFactory::new(accounts, storage_tries);
630        let mut iter = StateRootBranchNodesIter::new(factory);
631
632        // Track what we see
633        let mut seen_storage_accounts = Vec::new();
634        let mut current_storage_account = None;
635        let mut iterations = 0;
636
637        for result in iter.by_ref() {
638            iterations += 1;
639            assert!(iterations <= 10000, "Too many iterations");
640
641            match result {
642                Ok(BranchNode::Storage(account, _, _)) => {
643                    if current_storage_account != Some(account) {
644                        // Verify we don't revisit a storage account
645                        assert!(
646                            !seen_storage_accounts.contains(&account),
647                            "Should not revisit storage account {:?}",
648                            account
649                        );
650                        seen_storage_accounts.push(account);
651                        current_storage_account = Some(account);
652                    }
653                }
654                Ok(BranchNode::Account(_, _)) => {
655                    // Account nodes are fine
656                }
657                Err(e) => panic!("Unexpected error: {:?}", e),
658            }
659        }
660
661        assert!(iter.complete);
662    }
663
664    #[test]
665    fn test_single_verifier_new() {
666        // Test creating a new SingleVerifier for account trie
667        let trie_nodes = BTreeMap::from([(
668            Nibbles::from_nibbles([0x1]),
669            test_branch_node(0b1111, 0, 0, vec![]),
670        )]);
671
672        let cursor = create_mock_cursor(trie_nodes);
673        let verifier = SingleVerifier::new(None, cursor).unwrap();
674
675        // Should have seeked to the beginning and found the first node
676        assert!(verifier.curr.is_some());
677    }
678
679    #[test]
680    fn test_single_verifier_next_exact_match() {
681        // Test when the expected node matches exactly
682        let node1 = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
683        let node2 = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
684
685        let trie_nodes = BTreeMap::from([
686            (Nibbles::from_nibbles([0x1]), node1.clone()),
687            (Nibbles::from_nibbles([0x2]), node2),
688        ]);
689
690        let cursor = create_mock_cursor(trie_nodes);
691        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
692        let mut outputs = Vec::new();
693
694        // Call next with the exact node that exists
695        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
696
697        // Should have no outputs
698        assert!(outputs.is_empty());
699    }
700
701    #[test]
702    fn test_single_verifier_next_wrong_value() {
703        // Test when the path matches but value is different
704        let node_in_trie = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
705        let node_expected = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
706
707        let trie_nodes = BTreeMap::from([(Nibbles::from_nibbles([0x1]), node_in_trie.clone())]);
708
709        let cursor = create_mock_cursor(trie_nodes);
710        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
711        let mut outputs = Vec::new();
712
713        // Call next with different node value
714        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node_expected.clone()).unwrap();
715
716        // Should have one "wrong" output
717        assert_eq!(outputs.len(), 1);
718        assert_matches!(
719            &outputs[0],
720            Output::AccountWrong { path, expected, found }
721                if *path == Nibbles::from_nibbles([0x1]) && *expected == node_expected && *found == node_in_trie
722        );
723    }
724
725    #[test]
726    fn test_single_verifier_next_missing() {
727        // Test when expected node doesn't exist in trie
728        let node1 = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
729        let node_missing = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
730
731        let trie_nodes = BTreeMap::from([(Nibbles::from_nibbles([0x3]), node1)]);
732
733        let cursor = create_mock_cursor(trie_nodes);
734        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
735        let mut outputs = Vec::new();
736
737        // Call next with a node that comes before any in the trie
738        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node_missing.clone()).unwrap();
739
740        // Should have one "missing" output
741        assert_eq!(outputs.len(), 1);
742        assert_matches!(
743            &outputs[0],
744            Output::AccountMissing(path, node)
745                if *path == Nibbles::from_nibbles([0x1]) && *node == node_missing
746        );
747    }
748
749    #[test]
750    fn test_single_verifier_next_extra() {
751        // Test when trie has extra nodes not in expected
752        // Create a proper trie structure with root
753        let node_root = test_branch_node(0b1110, 0, 0b1110, vec![]); // root has children at 1, 2, 3
754        let node1 = test_branch_node(0b0001, 0, 0b0001, vec![]);
755        let node2 = test_branch_node(0b0010, 0, 0b0010, vec![]);
756        let node3 = test_branch_node(0b0100, 0, 0b0100, vec![]);
757
758        let trie_nodes = BTreeMap::from([
759            (Nibbles::new(), node_root.clone()),
760            (Nibbles::from_nibbles([0x1]), node1.clone()),
761            (Nibbles::from_nibbles([0x2]), node2.clone()),
762            (Nibbles::from_nibbles([0x3]), node3.clone()),
763        ]);
764
765        let cursor = create_mock_cursor(trie_nodes);
766        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
767        let mut outputs = Vec::new();
768
769        // The depth-first iterator produces in post-order: 0x1, 0x2, 0x3, root
770        // We only provide 0x1 and 0x3, skipping 0x2 and root
771        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
772        verifier.next(&mut outputs, Nibbles::from_nibbles([0x3]), node3).unwrap();
773        verifier.finalize(&mut outputs).unwrap();
774
775        // Should have two "extra" outputs for nodes in the trie that we skipped
776        if outputs.len() != 2 {
777            eprintln!("Expected 2 outputs, got {}:", outputs.len());
778            for inc in &outputs {
779                eprintln!("  {:?}", inc);
780            }
781        }
782        assert_eq!(outputs.len(), 2);
783        assert_matches!(
784            &outputs[0],
785            Output::AccountExtra(path, node)
786                if *path == Nibbles::from_nibbles([0x2]) && *node == node2
787        );
788        assert_matches!(
789            &outputs[1],
790            Output::AccountExtra(path, node)
791                if *path == Nibbles::new() && *node == node_root
792        );
793    }
794
795    #[test]
796    fn test_single_verifier_finalize() {
797        // Test finalize marks all remaining nodes as extra
798        let node_root = test_branch_node(0b1110, 0, 0b1110, vec![]); // root has children at 1, 2, 3
799        let node1 = test_branch_node(0b0001, 0, 0b0001, vec![]);
800        let node2 = test_branch_node(0b0010, 0, 0b0010, vec![]);
801        let node3 = test_branch_node(0b0100, 0, 0b0100, vec![]);
802
803        let trie_nodes = BTreeMap::from([
804            (Nibbles::new(), node_root.clone()),
805            (Nibbles::from_nibbles([0x1]), node1.clone()),
806            (Nibbles::from_nibbles([0x2]), node2.clone()),
807            (Nibbles::from_nibbles([0x3]), node3.clone()),
808        ]);
809
810        let cursor = create_mock_cursor(trie_nodes);
811        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
812        let mut outputs = Vec::new();
813
814        // The depth-first iterator produces in post-order: 0x1, 0x2, 0x3, root
815        // Process first two nodes correctly
816        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
817        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2]), node2).unwrap();
818        assert!(outputs.is_empty());
819
820        // Finalize - should mark remaining nodes (0x3 and root) as extra
821        verifier.finalize(&mut outputs).unwrap();
822
823        // Should have two extra nodes
824        assert_eq!(outputs.len(), 2);
825        assert_matches!(
826            &outputs[0],
827            Output::AccountExtra(path, node)
828                if *path == Nibbles::from_nibbles([0x3]) && *node == node3
829        );
830        assert_matches!(
831            &outputs[1],
832            Output::AccountExtra(path, node)
833                if *path == Nibbles::new() && *node == node_root
834        );
835    }
836
837    #[test]
838    fn test_single_verifier_storage_trie() {
839        // Test SingleVerifier for storage trie (with account set)
840        let account = B256::from([42u8; 32]);
841        let node = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
842
843        let trie_nodes = BTreeMap::from([(Nibbles::from_nibbles([0x1]), node)]);
844
845        let cursor = create_mock_cursor(trie_nodes);
846        let mut verifier = SingleVerifier::new(Some(account), cursor).unwrap();
847        let mut outputs = Vec::new();
848
849        // Call next with missing node
850        let missing_node = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
851        verifier.next(&mut outputs, Nibbles::from_nibbles([0x0]), missing_node.clone()).unwrap();
852
853        // Should produce StorageMissing, not AccountMissing
854        assert_eq!(outputs.len(), 1);
855        assert_matches!(
856            &outputs[0],
857            Output::StorageMissing(acc, path, node)
858                if *acc == account && *path == Nibbles::from_nibbles([0x0]) && *node == missing_node
859        );
860    }
861
862    #[test]
863    fn test_single_verifier_empty_trie() {
864        // Test with empty trie cursor
865        let trie_nodes = BTreeMap::new();
866        let cursor = create_mock_cursor(trie_nodes);
867        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
868        let mut outputs = Vec::new();
869
870        // Any node should be marked as missing
871        let node = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
872        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node.clone()).unwrap();
873
874        assert_eq!(outputs.len(), 1);
875        assert_matches!(
876            &outputs[0],
877            Output::AccountMissing(path, n)
878                if *path == Nibbles::from_nibbles([0x1]) && *n == node
879        );
880    }
881
882    #[test]
883    fn test_single_verifier_depth_first_ordering() {
884        // Test that nodes must be provided in depth-first order
885        // Create nodes with proper parent-child relationships
886        let node_root = test_branch_node(0b0110, 0, 0b0110, vec![]); // root has children at 1 and 2
887        let node1 = test_branch_node(0b0110, 0, 0b0110, vec![]); // 0x1 has children at 1 and 2
888        let node11 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x11 is a leaf
889        let node12 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x12 is a leaf
890        let node2 = test_branch_node(0b0100, 0, 0b0100, vec![]); // 0x2 is a leaf
891
892        // The depth-first iterator will iterate from the root in this order:
893        // root -> 0x1 -> 0x11, 0x12 (children of 0x1), then 0x2
894        // But because of depth-first, we get: root, 0x1, 0x11, 0x12, 0x2
895        let trie_nodes = BTreeMap::from([
896            (Nibbles::new(), node_root.clone()),                 // root
897            (Nibbles::from_nibbles([0x1]), node1.clone()),       // 0x1
898            (Nibbles::from_nibbles([0x1, 0x1]), node11.clone()), // 0x11
899            (Nibbles::from_nibbles([0x1, 0x2]), node12.clone()), // 0x12
900            (Nibbles::from_nibbles([0x2]), node2.clone()),       // 0x2
901        ]);
902
903        let cursor = create_mock_cursor(trie_nodes);
904        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
905        let mut outputs = Vec::new();
906
907        // The depth-first iterator produces nodes in post-order (children before parents)
908        // Order: 0x11, 0x12, 0x1, 0x2, root
909        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1]), node11).unwrap();
910        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x2]), node12).unwrap();
911        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
912        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2]), node2).unwrap();
913        verifier.next(&mut outputs, Nibbles::new(), node_root).unwrap();
914        verifier.finalize(&mut outputs).unwrap();
915
916        // All should match, no outputs
917        if !outputs.is_empty() {
918            eprintln!(
919                "Test test_single_verifier_depth_first_ordering failed with {} outputs:",
920                outputs.len()
921            );
922            for inc in &outputs {
923                eprintln!("  {:?}", inc);
924            }
925        }
926        assert!(outputs.is_empty());
927    }
928
929    #[test]
930    fn test_single_verifier_wrong_depth_first_order() {
931        // Test that providing nodes in wrong order produces outputs
932        // Create a trie with parent-child relationship
933        let node_root = test_branch_node(0b0010, 0, 0b0010, vec![]); // root has child at 1
934        let node1 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x1 has child at 1
935        let node11 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x11 is a leaf
936
937        let trie_nodes = BTreeMap::from([
938            (Nibbles::new(), node_root.clone()),
939            (Nibbles::from_nibbles([0x1]), node1.clone()),
940            (Nibbles::from_nibbles([0x1, 0x1]), node11.clone()),
941        ]);
942
943        let cursor = create_mock_cursor(trie_nodes);
944        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
945        let mut outputs = Vec::new();
946
947        // Process in WRONG order (skip root, provide child before processing all nodes correctly)
948        // The iterator will produce: root, 0x1, 0x11
949        // But we provide: 0x11, root, 0x1 (completely wrong order)
950        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1]), node11).unwrap();
951        verifier.next(&mut outputs, Nibbles::new(), node_root).unwrap();
952        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
953
954        // Should have outputs since we provided them in wrong order
955        assert!(!outputs.is_empty());
956    }
957
958    #[test]
959    fn test_single_verifier_complex_depth_first() {
960        // Test a complex tree structure with depth-first ordering
961        // Build a tree structure with proper parent-child relationships
962        let node_root = test_branch_node(0b0110, 0, 0b0110, vec![]); // root: children at nibbles 1 and 2
963        let node1 = test_branch_node(0b0110, 0, 0b0110, vec![]); // 0x1: children at nibbles 1 and 2
964        let node11 = test_branch_node(0b0110, 0, 0b0110, vec![]); // 0x11: children at nibbles 1 and 2
965        let node111 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x111: leaf
966        let node112 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x112: leaf
967        let node12 = test_branch_node(0b0100, 0, 0b0100, vec![]); // 0x12: leaf
968        let node2 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x2: child at nibble 1
969        let node21 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x21: leaf
970
971        // Create the trie structure
972        let trie_nodes = BTreeMap::from([
973            (Nibbles::new(), node_root.clone()),
974            (Nibbles::from_nibbles([0x1]), node1.clone()),
975            (Nibbles::from_nibbles([0x1, 0x1]), node11.clone()),
976            (Nibbles::from_nibbles([0x1, 0x1, 0x1]), node111.clone()),
977            (Nibbles::from_nibbles([0x1, 0x1, 0x2]), node112.clone()),
978            (Nibbles::from_nibbles([0x1, 0x2]), node12.clone()),
979            (Nibbles::from_nibbles([0x2]), node2.clone()),
980            (Nibbles::from_nibbles([0x2, 0x1]), node21.clone()),
981        ]);
982
983        let cursor = create_mock_cursor(trie_nodes);
984        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
985        let mut outputs = Vec::new();
986
987        // The depth-first iterator produces nodes in post-order (children before parents)
988        // Order: 0x111, 0x112, 0x11, 0x12, 0x1, 0x21, 0x2, root
989        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1, 0x1]), node111).unwrap();
990        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1, 0x2]), node112).unwrap();
991        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1]), node11).unwrap();
992        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x2]), node12).unwrap();
993        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
994        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2, 0x1]), node21).unwrap();
995        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2]), node2).unwrap();
996        verifier.next(&mut outputs, Nibbles::new(), node_root).unwrap();
997        verifier.finalize(&mut outputs).unwrap();
998
999        // All should match, no outputs
1000        if !outputs.is_empty() {
1001            eprintln!(
1002                "Test test_single_verifier_complex_depth_first failed with {} outputs:",
1003                outputs.len()
1004            );
1005            for inc in &outputs {
1006                eprintln!("  {:?}", inc);
1007            }
1008        }
1009        assert!(outputs.is_empty());
1010    }
1011}