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                if let Some((path, node)) = storage_updates.pop() {
78                    let node = BranchNode::Storage(*account, path, node);
79                    return Some(Ok(node))
80                }
81            }
82
83            // If there's not a storage trie already being iterated over than 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 with 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    /// A 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 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 re-compute 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<T: TrieCursorFactory, H> {
305    trie_cursor_factory: T,
306    hashed_cursor_factory: H,
307    branch_node_iter: StateRootBranchNodesIter<H>,
308    outputs: Vec<Output>,
309    account: SingleVerifier<DepthFirstTrieIterator<T::AccountTrieCursor>>,
310    storage: Option<(B256, SingleVerifier<DepthFirstTrieIterator<T::StorageTrieCursor>>)>,
311    complete: bool,
312}
313
314impl<T: TrieCursorFactory + Clone, H: HashedCursorFactory + Clone> Verifier<T, H> {
315    /// Creates a new verifier instance.
316    pub fn new(trie_cursor_factory: T, hashed_cursor_factory: H) -> Result<Self, DatabaseError> {
317        Ok(Self {
318            trie_cursor_factory: trie_cursor_factory.clone(),
319            hashed_cursor_factory: hashed_cursor_factory.clone(),
320            branch_node_iter: StateRootBranchNodesIter::new(hashed_cursor_factory),
321            outputs: Default::default(),
322            account: SingleVerifier::new(None, trie_cursor_factory.account_trie_cursor()?)?,
323            storage: None,
324            complete: false,
325        })
326    }
327}
328
329impl<T: TrieCursorFactory, H: HashedCursorFactory + Clone> Verifier<T, H> {
330    fn new_storage(
331        &mut self,
332        account: B256,
333        path: Nibbles,
334        node: BranchNodeCompact,
335    ) -> Result<(), DatabaseError> {
336        let trie_cursor = self.trie_cursor_factory.storage_trie_cursor(account)?;
337        let mut storage = SingleVerifier::new(Some(account), trie_cursor)?;
338        storage.next(&mut self.outputs, path, node)?;
339        self.storage = Some((account, storage));
340        Ok(())
341    }
342
343    /// This method is called using the account hashes at the boundary of [`BranchNode::Storage`]
344    /// sequences, ie once the [`StateRootBranchNodesIter`] has begun yielding storage nodes for a
345    /// different account than it was yielding previously. All accounts between the two should have
346    /// empty storages.
347    fn verify_empty_storages(
348        &mut self,
349        last_account: B256,
350        next_account: B256,
351        start_inclusive: bool,
352        end_inclusive: bool,
353    ) -> Result<(), DatabaseError> {
354        let mut account_cursor = self.hashed_cursor_factory.hashed_account_cursor()?;
355        let mut account_seeked = false;
356
357        if !start_inclusive {
358            account_seeked = true;
359            account_cursor.seek(last_account)?;
360        }
361
362        loop {
363            let Some((curr_account, _)) = (if account_seeked {
364                account_cursor.next()?
365            } else {
366                account_seeked = true;
367                account_cursor.seek(last_account)?
368            }) else {
369                return Ok(())
370            };
371
372            if curr_account < next_account || (end_inclusive && curr_account == next_account) {
373                trace!(target: "trie::verify", account = ?curr_account, "Verying account has empty storage");
374
375                let mut storage_cursor =
376                    self.trie_cursor_factory.storage_trie_cursor(curr_account)?;
377                let mut seeked = false;
378                while let Some((path, node)) = if seeked {
379                    storage_cursor.next()?
380                } else {
381                    seeked = true;
382                    storage_cursor.seek(Nibbles::new())?
383                } {
384                    self.outputs.push(Output::StorageExtra(curr_account, path, node));
385                }
386            } else {
387                return Ok(())
388            }
389        }
390    }
391
392    fn try_next(&mut self) -> Result<(), StateRootError> {
393        match self.branch_node_iter.next().transpose()? {
394            None => {
395                self.account.finalize(&mut self.outputs)?;
396                if let Some((prev_account, storage)) = self.storage.as_mut() {
397                    storage.finalize(&mut self.outputs)?;
398
399                    // If there was a previous storage account, and it is the final one, then we
400                    // need to validate that all accounts coming after it have empty storages.
401                    let prev_account = *prev_account;
402
403                    // Calculate the max possible account address.
404                    let mut max_account = B256::ZERO;
405                    max_account.reverse();
406
407                    self.verify_empty_storages(prev_account, max_account, false, true)?;
408                }
409                self.complete = true;
410            }
411            Some(BranchNode::Account(path, node)) => {
412                trace!(target: "trie::verify", ?path, "Account node from state root");
413                self.account.next(&mut self.outputs, path, node)?;
414                // Push progress indicator
415                if !path.is_empty() {
416                    self.outputs.push(Output::Progress(path));
417                }
418            }
419            Some(BranchNode::Storage(account, path, node)) => {
420                trace!(target: "trie::verify", ?account, ?path, "Storage node from state root");
421                match self.storage.as_mut() {
422                    None => {
423                        // First storage account - check for any empty storages before it
424                        self.verify_empty_storages(B256::ZERO, account, true, false)?;
425                        self.new_storage(account, path, node)?;
426                    }
427                    Some((prev_account, storage)) if *prev_account == account => {
428                        storage.next(&mut self.outputs, path, node)?;
429                    }
430                    Some((prev_account, storage)) => {
431                        storage.finalize(&mut self.outputs)?;
432                        // Clear any storage entries between the previous account and the new one
433                        let prev_account = *prev_account;
434                        self.verify_empty_storages(prev_account, account, false, false)?;
435                        self.new_storage(account, path, node)?;
436                    }
437                }
438            }
439        }
440
441        // If any outputs were appended we want to reverse them, so they are popped off
442        // in the same order they were appended.
443        self.outputs.reverse();
444        Ok(())
445    }
446}
447
448impl<T: TrieCursorFactory, H: HashedCursorFactory + Clone> Iterator for Verifier<T, H> {
449    type Item = Result<Output, StateRootError>;
450
451    fn next(&mut self) -> Option<Self::Item> {
452        loop {
453            if let Some(output) = self.outputs.pop() {
454                return Some(Ok(output))
455            }
456
457            if self.complete {
458                return None
459            }
460
461            if let Err(err) = self.try_next() {
462                return Some(Err(err))
463            }
464        }
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471    use crate::{
472        hashed_cursor::mock::MockHashedCursorFactory,
473        trie_cursor::mock::{MockTrieCursor, MockTrieCursorFactory},
474    };
475    use alloy_primitives::{address, keccak256, map::B256Map, U256};
476    use alloy_trie::TrieMask;
477    use assert_matches::assert_matches;
478    use reth_primitives_traits::Account;
479    use std::collections::BTreeMap;
480
481    /// Helper function to create a simple test `BranchNodeCompact`
482    fn test_branch_node(
483        state_mask: u16,
484        tree_mask: u16,
485        hash_mask: u16,
486        hashes: Vec<B256>,
487    ) -> BranchNodeCompact {
488        // Ensure the number of hashes matches the number of bits set in hash_mask
489        let expected_hashes = hash_mask.count_ones() as usize;
490        let mut final_hashes = hashes;
491        let mut counter = 100u8;
492        while final_hashes.len() < expected_hashes {
493            final_hashes.push(B256::from([counter; 32]));
494            counter += 1;
495        }
496        final_hashes.truncate(expected_hashes);
497
498        BranchNodeCompact::new(
499            TrieMask::new(state_mask),
500            TrieMask::new(tree_mask),
501            TrieMask::new(hash_mask),
502            final_hashes,
503            None,
504        )
505    }
506
507    /// Helper function to create a simple test `MockTrieCursor`
508    fn create_mock_cursor(trie_nodes: BTreeMap<Nibbles, BranchNodeCompact>) -> MockTrieCursor {
509        let factory = MockTrieCursorFactory::new(trie_nodes, B256Map::default());
510        factory.account_trie_cursor().unwrap()
511    }
512
513    #[test]
514    fn test_state_root_branch_nodes_iter_empty() {
515        // Test with completely empty state
516        let factory = MockHashedCursorFactory::new(BTreeMap::new(), B256Map::default());
517        let mut iter = StateRootBranchNodesIter::new(factory);
518
519        // Collect all results - with empty state, should complete without producing nodes
520        let mut count = 0;
521        for result in iter.by_ref() {
522            assert!(result.is_ok(), "Unexpected error: {:?}", result.unwrap_err());
523            count += 1;
524            // Prevent infinite loop in test
525            assert!(count <= 1000, "Too many iterations");
526        }
527
528        assert!(iter.complete);
529    }
530
531    #[test]
532    fn test_state_root_branch_nodes_iter_basic() {
533        // Simple test with a few accounts and storage
534        let mut accounts = BTreeMap::new();
535        let mut storage_tries = B256Map::default();
536
537        // Create test accounts
538        let addr1 = keccak256(address!("0000000000000000000000000000000000000001"));
539        accounts.insert(
540            addr1,
541            Account {
542                nonce: 1,
543                balance: U256::from(1000),
544                bytecode_hash: Some(keccak256(b"code1")),
545            },
546        );
547
548        // Add storage for the account
549        let mut storage1 = BTreeMap::new();
550        storage1.insert(keccak256(B256::from(U256::from(1))), U256::from(100));
551        storage1.insert(keccak256(B256::from(U256::from(2))), U256::from(200));
552        storage_tries.insert(addr1, storage1);
553
554        let factory = MockHashedCursorFactory::new(accounts, storage_tries);
555        let mut iter = StateRootBranchNodesIter::new(factory);
556
557        // Collect nodes and verify basic properties
558        let mut account_paths = Vec::new();
559        let mut storage_paths_by_account: B256Map<Vec<Nibbles>> = B256Map::default();
560        let mut iterations = 0;
561
562        for result in iter.by_ref() {
563            iterations += 1;
564            assert!(iterations <= 10000, "Too many iterations - possible infinite loop");
565
566            match result {
567                Ok(BranchNode::Account(path, _)) => {
568                    account_paths.push(path);
569                }
570                Ok(BranchNode::Storage(account, path, _)) => {
571                    storage_paths_by_account.entry(account).or_default().push(path);
572                }
573                Err(e) => panic!("Unexpected error: {:?}", e),
574            }
575        }
576
577        // Verify account paths are in ascending order
578        for i in 1..account_paths.len() {
579            assert!(
580                account_paths[i - 1] < account_paths[i],
581                "Account paths should be in ascending order"
582            );
583        }
584
585        // Verify storage paths for each account are in ascending order
586        for (account, paths) in storage_paths_by_account {
587            for i in 1..paths.len() {
588                assert!(
589                    paths[i - 1] < paths[i],
590                    "Storage paths for account {:?} should be in ascending order",
591                    account
592                );
593            }
594        }
595
596        assert!(iter.complete);
597    }
598
599    #[test]
600    fn test_state_root_branch_nodes_iter_multiple_accounts() {
601        // Test with multiple accounts to verify ordering
602        let mut accounts = BTreeMap::new();
603        let mut storage_tries = B256Map::default();
604
605        // Create multiple test addresses
606        for i in 1u8..=3 {
607            let addr = keccak256([i; 20]);
608            accounts.insert(
609                addr,
610                Account {
611                    nonce: i as u64,
612                    balance: U256::from(i as u64 * 1000),
613                    bytecode_hash: (i == 2).then(|| keccak256([i])),
614                },
615            );
616
617            // Add some storage for each account
618            let mut storage = BTreeMap::new();
619            for j in 0..i {
620                storage.insert(keccak256(B256::from(U256::from(j))), U256::from(j as u64 * 10));
621            }
622            if !storage.is_empty() {
623                storage_tries.insert(addr, storage);
624            }
625        }
626
627        let factory = MockHashedCursorFactory::new(accounts, storage_tries);
628        let mut iter = StateRootBranchNodesIter::new(factory);
629
630        // Track what we see
631        let mut seen_storage_accounts = Vec::new();
632        let mut current_storage_account = None;
633        let mut iterations = 0;
634
635        for result in iter.by_ref() {
636            iterations += 1;
637            assert!(iterations <= 10000, "Too many iterations");
638
639            match result {
640                Ok(BranchNode::Storage(account, _, _)) => {
641                    if current_storage_account != Some(account) {
642                        // Verify we don't revisit a storage account
643                        assert!(
644                            !seen_storage_accounts.contains(&account),
645                            "Should not revisit storage account {:?}",
646                            account
647                        );
648                        seen_storage_accounts.push(account);
649                        current_storage_account = Some(account);
650                    }
651                }
652                Ok(BranchNode::Account(_, _)) => {
653                    // Account nodes are fine
654                }
655                Err(e) => panic!("Unexpected error: {:?}", e),
656            }
657        }
658
659        assert!(iter.complete);
660    }
661
662    #[test]
663    fn test_single_verifier_new() {
664        // Test creating a new SingleVerifier for account trie
665        let trie_nodes = BTreeMap::from([(
666            Nibbles::from_nibbles([0x1]),
667            test_branch_node(0b1111, 0, 0, vec![]),
668        )]);
669
670        let cursor = create_mock_cursor(trie_nodes);
671        let verifier = SingleVerifier::new(None, cursor).unwrap();
672
673        // Should have seeked to the beginning and found the first node
674        assert!(verifier.curr.is_some());
675    }
676
677    #[test]
678    fn test_single_verifier_next_exact_match() {
679        // Test when the expected node matches exactly
680        let node1 = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
681        let node2 = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
682
683        let trie_nodes = BTreeMap::from([
684            (Nibbles::from_nibbles([0x1]), node1.clone()),
685            (Nibbles::from_nibbles([0x2]), node2),
686        ]);
687
688        let cursor = create_mock_cursor(trie_nodes);
689        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
690        let mut outputs = Vec::new();
691
692        // Call next with the exact node that exists
693        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
694
695        // Should have no outputs
696        assert!(outputs.is_empty());
697    }
698
699    #[test]
700    fn test_single_verifier_next_wrong_value() {
701        // Test when the path matches but value is different
702        let node_in_trie = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
703        let node_expected = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
704
705        let trie_nodes = BTreeMap::from([(Nibbles::from_nibbles([0x1]), node_in_trie.clone())]);
706
707        let cursor = create_mock_cursor(trie_nodes);
708        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
709        let mut outputs = Vec::new();
710
711        // Call next with different node value
712        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node_expected.clone()).unwrap();
713
714        // Should have one "wrong" output
715        assert_eq!(outputs.len(), 1);
716        assert_matches!(
717            &outputs[0],
718            Output::AccountWrong { path, expected, found }
719                if *path == Nibbles::from_nibbles([0x1]) && *expected == node_expected && *found == node_in_trie
720        );
721    }
722
723    #[test]
724    fn test_single_verifier_next_missing() {
725        // Test when expected node doesn't exist in trie
726        let node1 = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
727        let node_missing = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
728
729        let trie_nodes = BTreeMap::from([(Nibbles::from_nibbles([0x3]), node1)]);
730
731        let cursor = create_mock_cursor(trie_nodes);
732        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
733        let mut outputs = Vec::new();
734
735        // Call next with a node that comes before any in the trie
736        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node_missing.clone()).unwrap();
737
738        // Should have one "missing" output
739        assert_eq!(outputs.len(), 1);
740        assert_matches!(
741            &outputs[0],
742            Output::AccountMissing(path, node)
743                if *path == Nibbles::from_nibbles([0x1]) && *node == node_missing
744        );
745    }
746
747    #[test]
748    fn test_single_verifier_next_extra() {
749        // Test when trie has extra nodes not in expected
750        // Create a proper trie structure with root
751        let node_root = test_branch_node(0b1110, 0, 0b1110, vec![]); // root has children at 1, 2, 3
752        let node1 = test_branch_node(0b0001, 0, 0b0001, vec![]);
753        let node2 = test_branch_node(0b0010, 0, 0b0010, vec![]);
754        let node3 = test_branch_node(0b0100, 0, 0b0100, vec![]);
755
756        let trie_nodes = BTreeMap::from([
757            (Nibbles::new(), node_root.clone()),
758            (Nibbles::from_nibbles([0x1]), node1.clone()),
759            (Nibbles::from_nibbles([0x2]), node2.clone()),
760            (Nibbles::from_nibbles([0x3]), node3.clone()),
761        ]);
762
763        let cursor = create_mock_cursor(trie_nodes);
764        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
765        let mut outputs = Vec::new();
766
767        // The depth-first iterator produces in post-order: 0x1, 0x2, 0x3, root
768        // We only provide 0x1 and 0x3, skipping 0x2 and root
769        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
770        verifier.next(&mut outputs, Nibbles::from_nibbles([0x3]), node3).unwrap();
771        verifier.finalize(&mut outputs).unwrap();
772
773        // Should have two "extra" outputs for nodes in the trie that we skipped
774        if outputs.len() != 2 {
775            eprintln!("Expected 2 outputs, got {}:", outputs.len());
776            for inc in &outputs {
777                eprintln!("  {:?}", inc);
778            }
779        }
780        assert_eq!(outputs.len(), 2);
781        assert_matches!(
782            &outputs[0],
783            Output::AccountExtra(path, node)
784                if *path == Nibbles::from_nibbles([0x2]) && *node == node2
785        );
786        assert_matches!(
787            &outputs[1],
788            Output::AccountExtra(path, node)
789                if *path == Nibbles::new() && *node == node_root
790        );
791    }
792
793    #[test]
794    fn test_single_verifier_finalize() {
795        // Test finalize marks all remaining nodes as extra
796        let node_root = test_branch_node(0b1110, 0, 0b1110, vec![]); // root has children at 1, 2, 3
797        let node1 = test_branch_node(0b0001, 0, 0b0001, vec![]);
798        let node2 = test_branch_node(0b0010, 0, 0b0010, vec![]);
799        let node3 = test_branch_node(0b0100, 0, 0b0100, vec![]);
800
801        let trie_nodes = BTreeMap::from([
802            (Nibbles::new(), node_root.clone()),
803            (Nibbles::from_nibbles([0x1]), node1.clone()),
804            (Nibbles::from_nibbles([0x2]), node2.clone()),
805            (Nibbles::from_nibbles([0x3]), node3.clone()),
806        ]);
807
808        let cursor = create_mock_cursor(trie_nodes);
809        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
810        let mut outputs = Vec::new();
811
812        // The depth-first iterator produces in post-order: 0x1, 0x2, 0x3, root
813        // Process first two nodes correctly
814        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
815        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2]), node2).unwrap();
816        assert!(outputs.is_empty());
817
818        // Finalize - should mark remaining nodes (0x3 and root) as extra
819        verifier.finalize(&mut outputs).unwrap();
820
821        // Should have two extra nodes
822        assert_eq!(outputs.len(), 2);
823        assert_matches!(
824            &outputs[0],
825            Output::AccountExtra(path, node)
826                if *path == Nibbles::from_nibbles([0x3]) && *node == node3
827        );
828        assert_matches!(
829            &outputs[1],
830            Output::AccountExtra(path, node)
831                if *path == Nibbles::new() && *node == node_root
832        );
833    }
834
835    #[test]
836    fn test_single_verifier_storage_trie() {
837        // Test SingleVerifier for storage trie (with account set)
838        let account = B256::from([42u8; 32]);
839        let node = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
840
841        let trie_nodes = BTreeMap::from([(Nibbles::from_nibbles([0x1]), node)]);
842
843        let cursor = create_mock_cursor(trie_nodes);
844        let mut verifier = SingleVerifier::new(Some(account), cursor).unwrap();
845        let mut outputs = Vec::new();
846
847        // Call next with missing node
848        let missing_node = test_branch_node(0b0101, 0b0001, 0b0100, vec![B256::from([2u8; 32])]);
849        verifier.next(&mut outputs, Nibbles::from_nibbles([0x0]), missing_node.clone()).unwrap();
850
851        // Should produce StorageMissing, not AccountMissing
852        assert_eq!(outputs.len(), 1);
853        assert_matches!(
854            &outputs[0],
855            Output::StorageMissing(acc, path, node)
856                if *acc == account && *path == Nibbles::from_nibbles([0x0]) && *node == missing_node
857        );
858    }
859
860    #[test]
861    fn test_single_verifier_empty_trie() {
862        // Test with empty trie cursor
863        let trie_nodes = BTreeMap::new();
864        let cursor = create_mock_cursor(trie_nodes);
865        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
866        let mut outputs = Vec::new();
867
868        // Any node should be marked as missing
869        let node = test_branch_node(0b1111, 0, 0b1111, vec![B256::from([1u8; 32])]);
870        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node.clone()).unwrap();
871
872        assert_eq!(outputs.len(), 1);
873        assert_matches!(
874            &outputs[0],
875            Output::AccountMissing(path, n)
876                if *path == Nibbles::from_nibbles([0x1]) && *n == node
877        );
878    }
879
880    #[test]
881    fn test_single_verifier_depth_first_ordering() {
882        // Test that nodes must be provided in depth-first order
883        // Create nodes with proper parent-child relationships
884        let node_root = test_branch_node(0b0110, 0, 0b0110, vec![]); // root has children at 1 and 2
885        let node1 = test_branch_node(0b0110, 0, 0b0110, vec![]); // 0x1 has children at 1 and 2
886        let node11 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x11 is a leaf
887        let node12 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x12 is a leaf
888        let node2 = test_branch_node(0b0100, 0, 0b0100, vec![]); // 0x2 is a leaf
889
890        // The depth-first iterator will iterate from the root in this order:
891        // root -> 0x1 -> 0x11, 0x12 (children of 0x1), then 0x2
892        // But because of depth-first, we get: root, 0x1, 0x11, 0x12, 0x2
893        let trie_nodes = BTreeMap::from([
894            (Nibbles::new(), node_root.clone()),                 // root
895            (Nibbles::from_nibbles([0x1]), node1.clone()),       // 0x1
896            (Nibbles::from_nibbles([0x1, 0x1]), node11.clone()), // 0x11
897            (Nibbles::from_nibbles([0x1, 0x2]), node12.clone()), // 0x12
898            (Nibbles::from_nibbles([0x2]), node2.clone()),       // 0x2
899        ]);
900
901        let cursor = create_mock_cursor(trie_nodes);
902        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
903        let mut outputs = Vec::new();
904
905        // The depth-first iterator produces nodes in post-order (children before parents)
906        // Order: 0x11, 0x12, 0x1, 0x2, root
907        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1]), node11).unwrap();
908        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x2]), node12).unwrap();
909        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
910        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2]), node2).unwrap();
911        verifier.next(&mut outputs, Nibbles::new(), node_root).unwrap();
912        verifier.finalize(&mut outputs).unwrap();
913
914        // All should match, no outputs
915        if !outputs.is_empty() {
916            eprintln!(
917                "Test test_single_verifier_depth_first_ordering failed with {} outputs:",
918                outputs.len()
919            );
920            for inc in &outputs {
921                eprintln!("  {:?}", inc);
922            }
923        }
924        assert!(outputs.is_empty());
925    }
926
927    #[test]
928    fn test_single_verifier_wrong_depth_first_order() {
929        // Test that providing nodes in wrong order produces outputs
930        // Create a trie with parent-child relationship
931        let node_root = test_branch_node(0b0010, 0, 0b0010, vec![]); // root has child at 1
932        let node1 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x1 has child at 1
933        let node11 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x11 is a leaf
934
935        let trie_nodes = BTreeMap::from([
936            (Nibbles::new(), node_root.clone()),
937            (Nibbles::from_nibbles([0x1]), node1.clone()),
938            (Nibbles::from_nibbles([0x1, 0x1]), node11.clone()),
939        ]);
940
941        let cursor = create_mock_cursor(trie_nodes);
942        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
943        let mut outputs = Vec::new();
944
945        // Process in WRONG order (skip root, provide child before processing all nodes correctly)
946        // The iterator will produce: root, 0x1, 0x11
947        // But we provide: 0x11, root, 0x1 (completely wrong order)
948        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1]), node11).unwrap();
949        verifier.next(&mut outputs, Nibbles::new(), node_root).unwrap();
950        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
951
952        // Should have outputs since we provided them in wrong order
953        assert!(!outputs.is_empty());
954    }
955
956    #[test]
957    fn test_single_verifier_complex_depth_first() {
958        // Test a complex tree structure with depth-first ordering
959        // Build a tree structure with proper parent-child relationships
960        let node_root = test_branch_node(0b0110, 0, 0b0110, vec![]); // root: children at nibbles 1 and 2
961        let node1 = test_branch_node(0b0110, 0, 0b0110, vec![]); // 0x1: children at nibbles 1 and 2
962        let node11 = test_branch_node(0b0110, 0, 0b0110, vec![]); // 0x11: children at nibbles 1 and 2
963        let node111 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x111: leaf
964        let node112 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x112: leaf
965        let node12 = test_branch_node(0b0100, 0, 0b0100, vec![]); // 0x12: leaf
966        let node2 = test_branch_node(0b0010, 0, 0b0010, vec![]); // 0x2: child at nibble 1
967        let node21 = test_branch_node(0b0001, 0, 0b0001, vec![]); // 0x21: leaf
968
969        // Create the trie structure
970        let trie_nodes = BTreeMap::from([
971            (Nibbles::new(), node_root.clone()),
972            (Nibbles::from_nibbles([0x1]), node1.clone()),
973            (Nibbles::from_nibbles([0x1, 0x1]), node11.clone()),
974            (Nibbles::from_nibbles([0x1, 0x1, 0x1]), node111.clone()),
975            (Nibbles::from_nibbles([0x1, 0x1, 0x2]), node112.clone()),
976            (Nibbles::from_nibbles([0x1, 0x2]), node12.clone()),
977            (Nibbles::from_nibbles([0x2]), node2.clone()),
978            (Nibbles::from_nibbles([0x2, 0x1]), node21.clone()),
979        ]);
980
981        let cursor = create_mock_cursor(trie_nodes);
982        let mut verifier = SingleVerifier::new(None, cursor).unwrap();
983        let mut outputs = Vec::new();
984
985        // The depth-first iterator produces nodes in post-order (children before parents)
986        // Order: 0x111, 0x112, 0x11, 0x12, 0x1, 0x21, 0x2, root
987        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1, 0x1]), node111).unwrap();
988        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1, 0x2]), node112).unwrap();
989        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x1]), node11).unwrap();
990        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1, 0x2]), node12).unwrap();
991        verifier.next(&mut outputs, Nibbles::from_nibbles([0x1]), node1).unwrap();
992        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2, 0x1]), node21).unwrap();
993        verifier.next(&mut outputs, Nibbles::from_nibbles([0x2]), node2).unwrap();
994        verifier.next(&mut outputs, Nibbles::new(), node_root).unwrap();
995        verifier.finalize(&mut outputs).unwrap();
996
997        // All should match, no outputs
998        if !outputs.is_empty() {
999            eprintln!(
1000                "Test test_single_verifier_complex_depth_first failed with {} outputs:",
1001                outputs.len()
1002            );
1003            for inc in &outputs {
1004                eprintln!("  {:?}", inc);
1005            }
1006        }
1007        assert!(outputs.is_empty());
1008    }
1009}