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