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