Skip to main content

reth_trie_db/
state.rs

1use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
2use alloy_primitives::{keccak256, map::B256Map, BlockNumber, B256};
3use reth_db_api::{
4    models::{AccountBeforeTx, BlockNumberAddress},
5    transaction::DbTx,
6};
7use reth_execution_errors::StateRootError;
8use reth_storage_api::{
9    BlockNumReader, ChangeSetReader, DBProvider, StorageChangeSetReader, StorageSettingsCache,
10};
11use reth_storage_errors::provider::ProviderError;
12use reth_trie::{
13    hashed_cursor::HashedPostStateCursorFactory, trie_cursor::InMemoryTrieCursorFactory,
14    updates::TrieUpdates, HashedPostStateSorted, HashedStorageSorted, StateRoot, StateRootProgress,
15    TrieInputSorted,
16};
17use std::{
18    collections::HashSet,
19    ops::{Bound, RangeBounds, RangeInclusive},
20};
21use tracing::{debug, instrument};
22
23/// Extends [`StateRoot`] with operations specific for working with a database transaction.
24pub trait DatabaseStateRoot<'a, TX>: Sized {
25    /// Create a new [`StateRoot`] instance.
26    fn from_tx(tx: &'a TX) -> Self;
27
28    /// Given a block number range, identifies all the accounts and storage keys that
29    /// have changed.
30    ///
31    /// # Returns
32    ///
33    /// An instance of state root calculator with account and storage prefixes loaded.
34    fn incremental_root_calculator(
35        provider: &'a (impl ChangeSetReader
36                 + StorageChangeSetReader
37                 + StorageSettingsCache
38                 + DBProvider<Tx = TX>),
39        range: RangeInclusive<BlockNumber>,
40    ) -> Result<Self, StateRootError>;
41
42    /// Computes the state root of the trie with the changed account and storage prefixes and
43    /// existing trie nodes.
44    ///
45    /// # Returns
46    ///
47    /// The updated state root.
48    fn incremental_root(
49        provider: &'a (impl ChangeSetReader
50                 + StorageChangeSetReader
51                 + StorageSettingsCache
52                 + DBProvider<Tx = TX>),
53        range: RangeInclusive<BlockNumber>,
54    ) -> Result<B256, StateRootError>;
55
56    /// Computes the state root of the trie with the changed account and storage prefixes and
57    /// existing trie nodes collecting updates in the process.
58    ///
59    /// Ignores the threshold.
60    ///
61    /// # Returns
62    ///
63    /// The updated state root and the trie updates.
64    fn incremental_root_with_updates(
65        provider: &'a (impl ChangeSetReader
66                 + StorageChangeSetReader
67                 + StorageSettingsCache
68                 + DBProvider<Tx = TX>),
69        range: RangeInclusive<BlockNumber>,
70    ) -> Result<(B256, TrieUpdates), StateRootError>;
71
72    /// Computes the state root of the trie with the changed account and storage prefixes and
73    /// existing trie nodes collecting updates in the process.
74    ///
75    /// # Returns
76    ///
77    /// The intermediate progress of state root computation.
78    fn incremental_root_with_progress(
79        provider: &'a (impl ChangeSetReader
80                 + StorageChangeSetReader
81                 + StorageSettingsCache
82                 + DBProvider<Tx = TX>),
83        range: RangeInclusive<BlockNumber>,
84    ) -> Result<StateRootProgress, StateRootError>;
85
86    /// Calculate the state root for this [`HashedPostStateSorted`].
87    /// Internally, this method retrieves prefixsets and uses them
88    /// to calculate incremental state root.
89    ///
90    /// # Example
91    ///
92    /// ```
93    /// use alloy_primitives::U256;
94    /// use reth_db::test_utils::create_test_rw_db;
95    /// use reth_db_api::database::Database;
96    /// use reth_primitives_traits::Account;
97    /// use reth_trie::{updates::TrieUpdates, HashedPostState, StateRoot};
98    /// use reth_trie_db::{DatabaseStateRoot, LegacyKeyAdapter};
99    ///
100    /// // Initialize the database
101    /// let db = create_test_rw_db();
102    ///
103    /// // Initialize hashed post state
104    /// let mut hashed_state = HashedPostState::default();
105    /// hashed_state.accounts.insert(
106    ///     [0x11; 32].into(),
107    ///     Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }),
108    /// );
109    ///
110    /// // Calculate the state root
111    /// let tx = db.tx().expect("failed to create transaction");
112    /// let state_root = <StateRoot<
113    ///     reth_trie_db::DatabaseTrieCursorFactory<_, LegacyKeyAdapter>,
114    ///     reth_trie_db::DatabaseHashedCursorFactory<_>,
115    /// > as DatabaseStateRoot<_>>::overlay_root(&tx, &hashed_state.into_sorted());
116    /// ```
117    ///
118    /// # Returns
119    ///
120    /// The state root for this [`HashedPostStateSorted`].
121    fn overlay_root(tx: &'a TX, post_state: &HashedPostStateSorted)
122        -> Result<B256, StateRootError>;
123
124    /// Calculates the state root for this [`HashedPostStateSorted`] and returns it alongside trie
125    /// updates. See [`Self::overlay_root`] for more info.
126    fn overlay_root_with_updates(
127        tx: &'a TX,
128        post_state: &HashedPostStateSorted,
129    ) -> Result<(B256, TrieUpdates), StateRootError>;
130
131    /// Calculates the state root for provided [`HashedPostStateSorted`] using cached intermediate
132    /// nodes.
133    fn overlay_root_from_nodes(tx: &'a TX, input: TrieInputSorted) -> Result<B256, StateRootError>;
134
135    /// Calculates the state root and trie updates for provided [`HashedPostStateSorted`] using
136    /// cached intermediate nodes.
137    fn overlay_root_from_nodes_with_updates(
138        tx: &'a TX,
139        input: TrieInputSorted,
140    ) -> Result<(B256, TrieUpdates), StateRootError>;
141}
142
143/// Extends [`HashedPostStateSorted`] with operations specific for working with a database
144/// transaction.
145pub trait DatabaseHashedPostState: Sized {
146    /// Initializes [`HashedPostStateSorted`] from reverts. Iterates over state reverts in the
147    /// specified range and aggregates them into sorted hashed state.
148    fn from_reverts(
149        provider: &(impl ChangeSetReader + StorageChangeSetReader + BlockNumReader + DBProvider),
150        range: impl RangeBounds<BlockNumber>,
151    ) -> Result<HashedPostStateSorted, ProviderError>;
152}
153
154impl<'a, TX: DbTx, A: crate::TrieTableAdapter> DatabaseStateRoot<'a, TX>
155    for StateRoot<DatabaseTrieCursorFactory<&'a TX, A>, DatabaseHashedCursorFactory<&'a TX>>
156{
157    fn from_tx(tx: &'a TX) -> Self {
158        Self::new(DatabaseTrieCursorFactory::new(tx), DatabaseHashedCursorFactory::new(tx))
159    }
160
161    fn incremental_root_calculator(
162        provider: &'a (impl ChangeSetReader
163                 + StorageChangeSetReader
164                 + StorageSettingsCache
165                 + DBProvider<Tx = TX>),
166        range: RangeInclusive<BlockNumber>,
167    ) -> Result<Self, StateRootError> {
168        let loaded_prefix_sets =
169            crate::prefix_set::load_prefix_sets_with_provider(provider, range)?;
170        Ok(Self::from_tx(provider.tx_ref()).with_prefix_sets(loaded_prefix_sets))
171    }
172
173    fn incremental_root(
174        provider: &'a (impl ChangeSetReader
175                 + StorageChangeSetReader
176                 + StorageSettingsCache
177                 + DBProvider<Tx = TX>),
178        range: RangeInclusive<BlockNumber>,
179    ) -> Result<B256, StateRootError> {
180        debug!(target: "trie::loader", ?range, "incremental state root");
181        Self::incremental_root_calculator(provider, range)?.root()
182    }
183
184    fn incremental_root_with_updates(
185        provider: &'a (impl ChangeSetReader
186                 + StorageChangeSetReader
187                 + StorageSettingsCache
188                 + DBProvider<Tx = TX>),
189        range: RangeInclusive<BlockNumber>,
190    ) -> Result<(B256, TrieUpdates), StateRootError> {
191        debug!(target: "trie::loader", ?range, "incremental state root");
192        Self::incremental_root_calculator(provider, range)?.root_with_updates()
193    }
194
195    fn incremental_root_with_progress(
196        provider: &'a (impl ChangeSetReader
197                 + StorageChangeSetReader
198                 + StorageSettingsCache
199                 + DBProvider<Tx = TX>),
200        range: RangeInclusive<BlockNumber>,
201    ) -> Result<StateRootProgress, StateRootError> {
202        debug!(target: "trie::loader", ?range, "incremental state root with progress");
203        Self::incremental_root_calculator(provider, range)?.root_with_progress()
204    }
205
206    fn overlay_root(
207        tx: &'a TX,
208        post_state: &HashedPostStateSorted,
209    ) -> Result<B256, StateRootError> {
210        let prefix_sets = post_state.construct_prefix_sets().freeze();
211        StateRoot::new(
212            DatabaseTrieCursorFactory::<_, A>::new(tx),
213            HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), post_state),
214        )
215        .with_prefix_sets(prefix_sets)
216        .root()
217    }
218
219    fn overlay_root_with_updates(
220        tx: &'a TX,
221        post_state: &HashedPostStateSorted,
222    ) -> Result<(B256, TrieUpdates), StateRootError> {
223        let prefix_sets = post_state.construct_prefix_sets().freeze();
224        StateRoot::new(
225            DatabaseTrieCursorFactory::<_, A>::new(tx),
226            HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), post_state),
227        )
228        .with_prefix_sets(prefix_sets)
229        .root_with_updates()
230    }
231
232    fn overlay_root_from_nodes(tx: &'a TX, input: TrieInputSorted) -> Result<B256, StateRootError> {
233        StateRoot::new(
234            InMemoryTrieCursorFactory::new(
235                DatabaseTrieCursorFactory::<_, A>::new(tx),
236                input.nodes.as_ref(),
237            ),
238            HashedPostStateCursorFactory::new(
239                DatabaseHashedCursorFactory::new(tx),
240                input.state.as_ref(),
241            ),
242        )
243        .with_prefix_sets(input.prefix_sets.freeze())
244        .root()
245    }
246
247    fn overlay_root_from_nodes_with_updates(
248        tx: &'a TX,
249        input: TrieInputSorted,
250    ) -> Result<(B256, TrieUpdates), StateRootError> {
251        StateRoot::new(
252            InMemoryTrieCursorFactory::new(
253                DatabaseTrieCursorFactory::<_, A>::new(tx),
254                input.nodes.as_ref(),
255            ),
256            HashedPostStateCursorFactory::new(
257                DatabaseHashedCursorFactory::new(tx),
258                input.state.as_ref(),
259            ),
260        )
261        .with_prefix_sets(input.prefix_sets.freeze())
262        .root_with_updates()
263    }
264}
265
266/// Calls [`HashedPostStateSorted::from_reverts`].
267pub fn from_reverts_auto(
268    provider: &(impl ChangeSetReader
269          + StorageChangeSetReader
270          + BlockNumReader
271          + DBProvider
272          + StorageSettingsCache),
273    range: impl RangeBounds<BlockNumber>,
274) -> Result<HashedPostStateSorted, ProviderError> {
275    HashedPostStateSorted::from_reverts(provider, range)
276}
277
278impl DatabaseHashedPostState for HashedPostStateSorted {
279    /// Builds a sorted hashed post-state from reverts.
280    ///
281    /// Reads MDBX data directly into Vecs, using `HashSet`s only to track seen keys.
282    /// This avoids intermediate `HashMap` allocations since MDBX data is already sorted.
283    ///
284    /// - Reads the first occurrence of each changed account/storage slot in the range.
285    /// - Addresses are always keccak256-hashed.
286    /// - Storage keys are always plain and are hashed via `keccak256`.
287    /// - Returns keys already ordered for trie iteration.
288    #[instrument(target = "trie::db", skip(provider), fields(range))]
289    fn from_reverts(
290        provider: &(impl ChangeSetReader + StorageChangeSetReader + BlockNumReader + DBProvider),
291        range: impl RangeBounds<BlockNumber>,
292    ) -> Result<Self, ProviderError> {
293        // Extract concrete start/end values to use for both account and storage changesets.
294        let start = match range.start_bound() {
295            Bound::Included(&n) => n,
296            Bound::Excluded(&n) => n + 1,
297            Bound::Unbounded => 0,
298        };
299
300        let end = match range.end_bound() {
301            Bound::Included(&n) => n + 1,
302            Bound::Excluded(&n) => n,
303            Bound::Unbounded => BlockNumber::MAX,
304        };
305
306        // Iterate over account changesets and record value before first occurring account change
307        let mut accounts = Vec::new();
308        let mut seen_accounts = HashSet::new();
309        for entry in provider.account_changesets_range(start..end)? {
310            let (_, AccountBeforeTx { address, info }) = entry;
311            if seen_accounts.insert(address) {
312                accounts.push((keccak256(address), info));
313            }
314        }
315        accounts.sort_unstable_by_key(|(hash, _)| *hash);
316
317        // Read storages into B256Map<Vec<_>> with HashSet to track seen keys.
318        // Only keep the first (oldest) occurrence of each (address, slot) pair.
319        let mut storages = B256Map::<Vec<_>>::default();
320        let mut seen_storage_keys = HashSet::new();
321
322        if start < end {
323            let end_inclusive = end.saturating_sub(1);
324            for (BlockNumberAddress((_, address)), storage) in
325                provider.storage_changesets_range(start..=end_inclusive)?
326            {
327                if seen_storage_keys.insert((address, storage.key)) {
328                    let hashed_address = keccak256(address);
329                    storages
330                        .entry(hashed_address)
331                        .or_default()
332                        .push((keccak256(storage.key), storage.value));
333                }
334            }
335        }
336
337        // Sort storage slots and convert to HashedStorageSorted
338        let hashed_storages = storages
339            .into_iter()
340            .map(|(address, mut slots)| {
341                slots.sort_unstable_by_key(|(slot, _)| *slot);
342                (address, HashedStorageSorted { storage_slots: slots, wiped: false })
343            })
344            .collect();
345
346        Ok(Self::new(accounts, hashed_storages))
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353    use alloy_primitives::{hex, keccak256, map::HashMap, Address, B256, U256};
354    use reth_db_api::{
355        models::{AccountBeforeTx, BlockNumberAddress},
356        tables,
357        transaction::DbTxMut,
358    };
359    use reth_execution_errors::StateRootError;
360    use reth_primitives_traits::{Account, StorageEntry};
361    use reth_provider::test_utils::create_test_provider_factory;
362    use reth_storage_api::StorageSettingsCache;
363    use reth_trie::{
364        HashedPostState, HashedPostStateSorted, HashedStorage, KeccakKeyHasher, StateRoot,
365    };
366    use revm::state::AccountInfo;
367    use revm_database::BundleState;
368
369    fn overlay_root_for_provider<TX: reth_db_api::transaction::DbTx>(
370        provider: &impl StorageSettingsCache,
371        tx: &TX,
372        sorted: &HashedPostStateSorted,
373    ) -> Result<B256, StateRootError> {
374        crate::with_adapter!(provider, |A| {
375            type S<'a, TX> = StateRoot<
376                crate::DatabaseTrieCursorFactory<&'a TX, A>,
377                crate::DatabaseHashedCursorFactory<&'a TX>,
378            >;
379            S::overlay_root(tx, sorted)
380        })
381    }
382
383    /// Overlay root calculation works with sorted state.
384    #[test]
385    fn overlay_root_with_sorted_state() {
386        let factory = create_test_provider_factory();
387        let provider = factory.provider_rw().unwrap();
388
389        let mut hashed_state = HashedPostState::default();
390        hashed_state.accounts.insert(
391            B256::from(U256::from(1)),
392            Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }),
393        );
394        hashed_state.accounts.insert(B256::from(U256::from(2)), None);
395        hashed_state.storages.insert(
396            B256::from(U256::from(1)),
397            HashedStorage::from_iter(false, [(B256::from(U256::from(3)), U256::from(30))]),
398        );
399
400        let sorted = hashed_state.into_sorted();
401        let overlay_root =
402            overlay_root_for_provider(&*provider, provider.tx_ref(), &sorted).unwrap();
403
404        // Just verify it produces a valid root
405        assert!(!overlay_root.is_zero());
406    }
407
408    /// Builds hashed state from a bundle and checks the known state root.
409    #[test]
410    fn from_bundle_state_with_rayon() {
411        let address1 = Address::with_last_byte(1);
412        let address2 = Address::with_last_byte(2);
413        let slot1 = U256::from(1015);
414        let slot2 = U256::from(2015);
415
416        let account1 = AccountInfo { nonce: 1, ..Default::default() };
417        let account2 = AccountInfo { nonce: 2, ..Default::default() };
418
419        let bundle_state = BundleState::builder(2..=2)
420            .state_present_account_info(address1, account1)
421            .state_present_account_info(address2, account2)
422            .state_storage(address1, HashMap::from_iter([(slot1, (U256::ZERO, U256::from(10)))]))
423            .state_storage(address2, HashMap::from_iter([(slot2, (U256::ZERO, U256::from(20)))]))
424            .build();
425        assert_eq!(bundle_state.reverts.len(), 1);
426
427        let post_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(&bundle_state.state);
428        assert_eq!(post_state.accounts.len(), 2);
429        assert_eq!(post_state.storages.len(), 2);
430
431        let factory = create_test_provider_factory();
432        let provider = factory.provider_rw().unwrap();
433        let sorted = post_state.into_sorted();
434        assert_eq!(
435            overlay_root_for_provider(&*provider, provider.tx_ref(), &sorted).unwrap(),
436            hex!("b464525710cafcf5d4044ac85b72c08b1e76231b8d91f288fe438cc41d8eaafd")
437        );
438    }
439
440    /// Verifies `from_reverts` keeps first occurrence per key and preserves ordering guarantees.
441    #[test]
442    fn from_reverts_keeps_first_occurrence_and_ordering() {
443        let factory = create_test_provider_factory();
444        let provider = factory.provider_rw().unwrap();
445
446        let address1 = Address::with_last_byte(1);
447        let address2 = Address::with_last_byte(2);
448        let slot1 = B256::from(U256::from(11));
449        let slot2 = B256::from(U256::from(22));
450
451        // Account changesets: only first occurrence per address should be kept.
452        provider
453            .tx_ref()
454            .put::<tables::AccountChangeSets>(
455                1,
456                AccountBeforeTx {
457                    address: address1,
458                    info: Some(Account { nonce: 1, ..Default::default() }),
459                },
460            )
461            .unwrap();
462        provider
463            .tx_ref()
464            .put::<tables::AccountChangeSets>(
465                2,
466                AccountBeforeTx {
467                    address: address1,
468                    info: Some(Account { nonce: 2, ..Default::default() }),
469                },
470            )
471            .unwrap();
472        provider
473            .tx_ref()
474            .put::<tables::AccountChangeSets>(3, AccountBeforeTx { address: address2, info: None })
475            .unwrap();
476
477        // Storage changesets: only first occurrence per slot should be kept, and slots sorted.
478        provider
479            .tx_ref()
480            .put::<tables::StorageChangeSets>(
481                BlockNumberAddress((1, address1)),
482                StorageEntry { key: slot2, value: U256::from(200) },
483            )
484            .unwrap();
485        provider
486            .tx_ref()
487            .put::<tables::StorageChangeSets>(
488                BlockNumberAddress((2, address1)),
489                StorageEntry { key: slot1, value: U256::from(100) },
490            )
491            .unwrap();
492        provider
493            .tx_ref()
494            .put::<tables::StorageChangeSets>(
495                BlockNumberAddress((3, address1)),
496                StorageEntry { key: slot1, value: U256::from(999) }, // should be ignored
497            )
498            .unwrap();
499
500        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
501
502        // Verify first occurrences were kept (nonce 1, not 2)
503        assert_eq!(sorted.accounts.len(), 2);
504        let hashed_addr1 = keccak256(address1);
505        let account1 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr1).unwrap();
506        assert_eq!(account1.1.unwrap().nonce, 1);
507
508        // Ordering guarantees - accounts sorted by hashed address
509        assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
510
511        // Ordering guarantees - storage slots sorted by hashed slot
512        for storage in sorted.storages.values() {
513            assert!(storage.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
514        }
515    }
516
517    /// Empty block range returns empty state.
518    #[test]
519    fn from_reverts_empty_range() {
520        let factory = create_test_provider_factory();
521        let provider = factory.provider_rw().unwrap();
522
523        // Insert data outside the query range
524        provider
525            .tx_ref()
526            .put::<tables::AccountChangeSets>(
527                100,
528                AccountBeforeTx {
529                    address: Address::with_last_byte(1),
530                    info: Some(Account { nonce: 1, ..Default::default() }),
531                },
532            )
533            .unwrap();
534
535        // Query a range with no data
536        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=10).unwrap();
537        assert!(sorted.accounts.is_empty());
538        assert!(sorted.storages.is_empty());
539    }
540
541    #[test]
542    fn from_reverts_with_hashed_state() {
543        use reth_db_api::models::{StorageBeforeTx, StorageSettings};
544        use reth_provider::{StaticFileProviderFactory, StaticFileSegment, StaticFileWriter};
545
546        let factory = create_test_provider_factory();
547
548        factory.set_storage_settings_cache(StorageSettings::v2());
549
550        let provider = factory.provider_rw().unwrap();
551
552        let address1 = Address::with_last_byte(1);
553        let address2 = Address::with_last_byte(2);
554
555        let plain_slot1 = B256::from(U256::from(11));
556        let plain_slot2 = B256::from(U256::from(22));
557        let hashed_slot1 = keccak256(plain_slot1);
558        let hashed_slot2 = keccak256(plain_slot2);
559
560        {
561            let sf = factory.static_file_provider();
562
563            // Write account changesets to static files (v2 reads from here)
564            let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
565            aw.append_account_changeset(vec![], 0).unwrap();
566            aw.append_account_changeset(
567                vec![AccountBeforeTx {
568                    address: address1,
569                    info: Some(Account { nonce: 1, ..Default::default() }),
570                }],
571                1,
572            )
573            .unwrap();
574            aw.append_account_changeset(
575                vec![AccountBeforeTx {
576                    address: address1,
577                    info: Some(Account { nonce: 2, ..Default::default() }),
578                }],
579                2,
580            )
581            .unwrap();
582            aw.append_account_changeset(vec![AccountBeforeTx { address: address2, info: None }], 3)
583                .unwrap();
584            aw.commit().unwrap();
585
586            let mut writer = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
587            writer.append_storage_changeset(vec![], 0).unwrap();
588            writer
589                .append_storage_changeset(
590                    vec![StorageBeforeTx {
591                        address: address1,
592                        key: plain_slot2,
593                        value: U256::from(200),
594                    }],
595                    1,
596                )
597                .unwrap();
598            writer
599                .append_storage_changeset(
600                    vec![StorageBeforeTx {
601                        address: address1,
602                        key: plain_slot1,
603                        value: U256::from(100),
604                    }],
605                    2,
606                )
607                .unwrap();
608            writer
609                .append_storage_changeset(
610                    vec![StorageBeforeTx {
611                        address: address1,
612                        key: plain_slot1,
613                        value: U256::from(999),
614                    }],
615                    3,
616                )
617                .unwrap();
618            writer.commit().unwrap();
619        }
620
621        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
622
623        assert_eq!(sorted.accounts.len(), 2);
624
625        let hashed_addr1 = keccak256(address1);
626        let hashed_addr2 = keccak256(address2);
627
628        let account1 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr1).unwrap();
629        assert_eq!(account1.1.unwrap().nonce, 1);
630
631        let account2 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr2).unwrap();
632        assert!(account2.1.is_none());
633
634        assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
635
636        let storage = sorted.storages.get(&hashed_addr1).expect("storage for address1");
637        assert_eq!(storage.storage_slots.len(), 2);
638
639        let found_slot1 = storage.storage_slots.iter().find(|(k, _)| *k == hashed_slot1).unwrap();
640        assert_eq!(found_slot1.1, U256::from(100));
641
642        let found_slot2 = storage.storage_slots.iter().find(|(k, _)| *k == hashed_slot2).unwrap();
643        assert_eq!(found_slot2.1, U256::from(200));
644
645        assert_ne!(hashed_slot1, plain_slot1);
646        assert_ne!(hashed_slot2, plain_slot2);
647
648        assert!(storage.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
649    }
650
651    #[test]
652    fn from_reverts_legacy_keccak_hashes_all_keys() {
653        let factory = create_test_provider_factory();
654        let provider = factory.provider_rw().unwrap();
655
656        let address1 = Address::with_last_byte(1);
657        let address2 = Address::with_last_byte(2);
658        let plain_slot1 = B256::from(U256::from(11));
659        let plain_slot2 = B256::from(U256::from(22));
660
661        provider
662            .tx_ref()
663            .put::<tables::AccountChangeSets>(
664                1,
665                AccountBeforeTx {
666                    address: address1,
667                    info: Some(Account { nonce: 10, ..Default::default() }),
668                },
669            )
670            .unwrap();
671        provider
672            .tx_ref()
673            .put::<tables::AccountChangeSets>(
674                2,
675                AccountBeforeTx {
676                    address: address2,
677                    info: Some(Account { nonce: 20, ..Default::default() }),
678                },
679            )
680            .unwrap();
681        provider
682            .tx_ref()
683            .put::<tables::AccountChangeSets>(
684                3,
685                AccountBeforeTx {
686                    address: address1,
687                    info: Some(Account { nonce: 99, ..Default::default() }),
688                },
689            )
690            .unwrap();
691
692        provider
693            .tx_ref()
694            .put::<tables::StorageChangeSets>(
695                BlockNumberAddress((1, address1)),
696                StorageEntry { key: plain_slot1, value: U256::from(100) },
697            )
698            .unwrap();
699        provider
700            .tx_ref()
701            .put::<tables::StorageChangeSets>(
702                BlockNumberAddress((2, address1)),
703                StorageEntry { key: plain_slot2, value: U256::from(200) },
704            )
705            .unwrap();
706        provider
707            .tx_ref()
708            .put::<tables::StorageChangeSets>(
709                BlockNumberAddress((3, address2)),
710                StorageEntry { key: plain_slot1, value: U256::from(300) },
711            )
712            .unwrap();
713
714        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
715
716        let expected_hashed_addr1 = keccak256(address1);
717        let expected_hashed_addr2 = keccak256(address2);
718        assert_eq!(sorted.accounts.len(), 2);
719
720        let account1 =
721            sorted.accounts.iter().find(|(addr, _)| *addr == expected_hashed_addr1).unwrap();
722        assert_eq!(account1.1.unwrap().nonce, 10);
723
724        let account2 =
725            sorted.accounts.iter().find(|(addr, _)| *addr == expected_hashed_addr2).unwrap();
726        assert_eq!(account2.1.unwrap().nonce, 20);
727
728        assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
729
730        let expected_hashed_slot1 = keccak256(plain_slot1);
731        let expected_hashed_slot2 = keccak256(plain_slot2);
732
733        assert_ne!(expected_hashed_slot1, plain_slot1);
734        assert_ne!(expected_hashed_slot2, plain_slot2);
735
736        let storage1 = sorted.storages.get(&expected_hashed_addr1).expect("storage for address1");
737        assert_eq!(storage1.storage_slots.len(), 2);
738        assert!(storage1
739            .storage_slots
740            .iter()
741            .any(|(k, v)| *k == expected_hashed_slot1 && *v == U256::from(100)));
742        assert!(storage1
743            .storage_slots
744            .iter()
745            .any(|(k, v)| *k == expected_hashed_slot2 && *v == U256::from(200)));
746        assert!(storage1.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
747
748        let storage2 = sorted.storages.get(&expected_hashed_addr2).expect("storage for address2");
749        assert_eq!(storage2.storage_slots.len(), 1);
750        assert_eq!(storage2.storage_slots[0].0, expected_hashed_slot1);
751        assert_eq!(storage2.storage_slots[0].1, U256::from(300));
752    }
753}