Skip to main content

reth_trie_db/
state.rs

1use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
2use alloy_primitives::{keccak256, map::B256Map, Address, 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), ..Default::default() }),
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            // Rows are ordered by `BlockNumberAddress`, so all slots of one account arrive
311            // consecutively and the address hash can be reused across them.
312            let mut last_address: Option<(Address, B256)> = None;
313            for (BlockNumberAddress((_, address)), storage) in
314                provider.storage_changesets_range(start..=end_inclusive)?
315            {
316                if seen_storage_keys.insert((address, storage.key)) {
317                    let hashed_address = match last_address {
318                        Some((last, hashed)) if last == address => hashed,
319                        _ => {
320                            let hashed = keccak256(address);
321                            last_address = Some((address, hashed));
322                            hashed
323                        }
324                    };
325                    storages
326                        .entry(hashed_address)
327                        .or_default()
328                        .push((keccak256(storage.key), storage.value));
329                }
330            }
331        }
332
333        // Sort storage slots and convert to HashedStorageSorted
334        let hashed_storages = storages
335            .into_iter()
336            .map(|(address, mut slots)| {
337                slots.sort_unstable_by_key(|(slot, _)| *slot);
338                (address, HashedStorageSorted { storage_slots: slots })
339            })
340            .collect();
341
342        Ok(Self::new(accounts, hashed_storages))
343    }
344}
345
346#[cfg(test)]
347mod tests {
348    use super::*;
349    use alloy_primitives::{hex, keccak256, map::HashMap, Address, B256, U256};
350    use evm2::evm::AccountInfo;
351    use reth_db_api::{
352        models::{AccountBeforeTx, BlockNumberAddress, StorageBeforeTx, StorageSettings},
353        tables,
354        transaction::DbTxMut,
355    };
356    use reth_execution_errors::StateRootError;
357    use reth_primitives_traits::{Account, StorageEntry};
358    use reth_provider::{
359        test_utils::create_test_provider_factory, StaticFileProviderFactory, StaticFileSegment,
360        StaticFileWriter,
361    };
362    use reth_storage_api::StorageSettingsCache;
363    use reth_trie::{
364        HashedPostState, HashedPostStateSorted, HashedStorage, KeccakKeyHasher, StateRoot,
365    };
366    use reth_trie_common::bundle_state::BundleState;
367
368    fn overlay_root_for_provider<TX: reth_db_api::transaction::DbTx>(
369        provider: &impl StorageSettingsCache,
370        tx: &TX,
371        sorted: &HashedPostStateSorted,
372    ) -> Result<B256, StateRootError> {
373        crate::with_adapter!(provider, |A| {
374            type S<'a, TX> = StateRoot<
375                crate::DatabaseTrieCursorFactory<&'a TX, A>,
376                crate::DatabaseHashedCursorFactory<&'a TX>,
377            >;
378            S::overlay_root(tx, sorted)
379        })
380    }
381
382    /// Overlay root calculation works with sorted state.
383    #[test]
384    fn overlay_root_with_sorted_state() {
385        let factory = create_test_provider_factory();
386        let provider = factory.provider_rw().unwrap();
387
388        let mut hashed_state = HashedPostState::default();
389        hashed_state.accounts.insert(
390            B256::with_last_byte(1),
391            Some(Account { nonce: 1, balance: U256::from(10), ..Default::default() }),
392        );
393        hashed_state.accounts.insert(B256::with_last_byte(2), None);
394        hashed_state.storages.insert(
395            B256::with_last_byte(1),
396            HashedStorage::from_iter([(B256::with_last_byte(3), U256::from(30))]),
397        );
398
399        let sorted = hashed_state.into_sorted();
400        let overlay_root =
401            overlay_root_for_provider(&*provider, provider.tx_ref(), &sorted).unwrap();
402
403        // Just verify it produces a valid root
404        assert!(!overlay_root.is_zero());
405    }
406
407    /// Builds hashed state from a bundle and checks the known state root.
408    #[test]
409    fn from_bundle_state_with_rayon() {
410        let address1 = Address::with_last_byte(1);
411        let address2 = Address::with_last_byte(2);
412        let slot1 = U256::from(1015);
413        let slot2 = U256::from(2015);
414
415        let account1 = AccountInfo { nonce: 1, ..Default::default() };
416        let account2 = AccountInfo { nonce: 2, ..Default::default() };
417
418        let bundle_state = BundleState::builder(2..=2)
419            .state_present_account_info(address1, account1)
420            .state_present_account_info(address2, account2)
421            .state_storage(address1, HashMap::from_iter([(slot1, (U256::ZERO, U256::from(10)))]))
422            .state_storage(address2, HashMap::from_iter([(slot2, (U256::ZERO, U256::from(20)))]))
423            .build();
424        assert_eq!(bundle_state.reverts.len(), 1);
425
426        let post_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(&bundle_state.state);
427        assert_eq!(post_state.accounts.len(), 2);
428        assert_eq!(post_state.storages.len(), 2);
429
430        let factory = create_test_provider_factory();
431        let provider = factory.provider_rw().unwrap();
432        let sorted = post_state.into_sorted();
433        assert_eq!(
434            overlay_root_for_provider(&*provider, provider.tx_ref(), &sorted).unwrap(),
435            hex!("b464525710cafcf5d4044ac85b72c08b1e76231b8d91f288fe438cc41d8eaafd")
436        );
437    }
438
439    /// Verifies `from_reverts` keeps first occurrence per key and preserves ordering guarantees.
440    #[test]
441    fn from_reverts_keeps_first_occurrence_and_ordering() {
442        let factory = create_test_provider_factory();
443        let provider = factory.provider_rw().unwrap();
444
445        let address1 = Address::with_last_byte(1);
446        let address2 = Address::with_last_byte(2);
447        let slot1 = B256::with_last_byte(11);
448        let slot2 = B256::with_last_byte(22);
449
450        // Account changesets: only first occurrence per address should be kept.
451        provider
452            .tx_ref()
453            .put::<tables::AccountChangeSets>(
454                1,
455                AccountBeforeTx {
456                    address: address1,
457                    info: Some(Account { nonce: 1, ..Default::default() }),
458                },
459            )
460            .unwrap();
461        provider
462            .tx_ref()
463            .put::<tables::AccountChangeSets>(
464                2,
465                AccountBeforeTx {
466                    address: address1,
467                    info: Some(Account { nonce: 2, ..Default::default() }),
468                },
469            )
470            .unwrap();
471        provider
472            .tx_ref()
473            .put::<tables::AccountChangeSets>(3, AccountBeforeTx { address: address2, info: None })
474            .unwrap();
475
476        // Storage changesets: only first occurrence per slot should be kept, and slots sorted.
477        provider
478            .tx_ref()
479            .put::<tables::StorageChangeSets>(
480                BlockNumberAddress((1, address1)),
481                StorageEntry { key: slot2, value: U256::from(200) },
482            )
483            .unwrap();
484        provider
485            .tx_ref()
486            .put::<tables::StorageChangeSets>(
487                BlockNumberAddress((2, address1)),
488                StorageEntry { key: slot1, value: U256::from(100) },
489            )
490            .unwrap();
491        provider
492            .tx_ref()
493            .put::<tables::StorageChangeSets>(
494                BlockNumberAddress((3, address1)),
495                StorageEntry { key: slot1, value: U256::from(999) }, // should be ignored
496            )
497            .unwrap();
498
499        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
500
501        // Verify first occurrences were kept (nonce 1, not 2)
502        assert_eq!(sorted.accounts.len(), 2);
503        let hashed_addr1 = keccak256(address1);
504        let account1 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr1).unwrap();
505        assert_eq!(account1.1.as_ref().unwrap().nonce, 1);
506
507        // Ordering guarantees - accounts sorted by hashed address
508        assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
509
510        // Ordering guarantees - storage slots sorted by hashed slot
511        for storage in sorted.storages.values() {
512            assert!(storage.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
513        }
514    }
515
516    /// Empty block range returns empty state.
517    #[test]
518    fn from_reverts_empty_range() {
519        let factory = create_test_provider_factory();
520        let provider = factory.provider_rw().unwrap();
521
522        // Insert data outside the query range
523        provider
524            .tx_ref()
525            .put::<tables::AccountChangeSets>(
526                100,
527                AccountBeforeTx {
528                    address: Address::with_last_byte(1),
529                    info: Some(Account { nonce: 1, ..Default::default() }),
530                },
531            )
532            .unwrap();
533
534        // Query a range with no data
535        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=10).unwrap();
536        assert!(sorted.accounts.is_empty());
537        assert!(sorted.storages.is_empty());
538    }
539
540    #[test]
541    fn from_reverts_with_hashed_state() {
542        let factory = create_test_provider_factory();
543
544        factory.set_storage_settings_cache(StorageSettings::v2());
545
546        let provider = factory.provider_rw().unwrap();
547
548        let address1 = Address::with_last_byte(1);
549        let address2 = Address::with_last_byte(2);
550
551        let plain_slot1 = B256::with_last_byte(11);
552        let plain_slot2 = B256::with_last_byte(22);
553        let hashed_slot1 = keccak256(plain_slot1);
554        let hashed_slot2 = keccak256(plain_slot2);
555
556        {
557            let sf = factory.static_file_provider();
558
559            // Write account changesets to static files (v2 reads from here)
560            let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
561            aw.append_account_changeset(vec![], 0).unwrap();
562            aw.append_account_changeset(
563                vec![AccountBeforeTx {
564                    address: address1,
565                    info: Some(Account { nonce: 1, ..Default::default() }),
566                }],
567                1,
568            )
569            .unwrap();
570            aw.append_account_changeset(
571                vec![AccountBeforeTx {
572                    address: address1,
573                    info: Some(Account { nonce: 2, ..Default::default() }),
574                }],
575                2,
576            )
577            .unwrap();
578            aw.append_account_changeset(vec![AccountBeforeTx { address: address2, info: None }], 3)
579                .unwrap();
580            aw.commit().unwrap();
581
582            let mut writer = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
583            writer.append_storage_changeset(vec![], 0).unwrap();
584            writer
585                .append_storage_changeset(
586                    vec![StorageBeforeTx {
587                        address: address1,
588                        key: plain_slot2,
589                        value: U256::from(200),
590                    }],
591                    1,
592                )
593                .unwrap();
594            writer
595                .append_storage_changeset(
596                    vec![StorageBeforeTx {
597                        address: address1,
598                        key: plain_slot1,
599                        value: U256::from(100),
600                    }],
601                    2,
602                )
603                .unwrap();
604            writer
605                .append_storage_changeset(
606                    vec![StorageBeforeTx {
607                        address: address1,
608                        key: plain_slot1,
609                        value: U256::from(999),
610                    }],
611                    3,
612                )
613                .unwrap();
614            writer.commit().unwrap();
615        }
616
617        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
618
619        assert_eq!(sorted.accounts.len(), 2);
620
621        let hashed_addr1 = keccak256(address1);
622        let hashed_addr2 = keccak256(address2);
623
624        let account1 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr1).unwrap();
625        assert_eq!(account1.1.as_ref().unwrap().nonce, 1);
626
627        let account2 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr2).unwrap();
628        assert!(account2.1.is_none());
629
630        assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
631
632        let storage = sorted.storages.get(&hashed_addr1).expect("storage for address1");
633        assert_eq!(storage.storage_slots.len(), 2);
634
635        let found_slot1 = storage.storage_slots.iter().find(|(k, _)| *k == hashed_slot1).unwrap();
636        assert_eq!(found_slot1.1, U256::from(100));
637
638        let found_slot2 = storage.storage_slots.iter().find(|(k, _)| *k == hashed_slot2).unwrap();
639        assert_eq!(found_slot2.1, U256::from(200));
640
641        assert_ne!(hashed_slot1, plain_slot1);
642        assert_ne!(hashed_slot2, plain_slot2);
643
644        assert!(storage.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
645    }
646
647    #[test]
648    fn from_reverts_legacy_keccak_hashes_all_keys() {
649        let factory = create_test_provider_factory();
650        let provider = factory.provider_rw().unwrap();
651
652        let address1 = Address::with_last_byte(1);
653        let address2 = Address::with_last_byte(2);
654        let plain_slot1 = B256::with_last_byte(11);
655        let plain_slot2 = B256::with_last_byte(22);
656
657        provider
658            .tx_ref()
659            .put::<tables::AccountChangeSets>(
660                1,
661                AccountBeforeTx {
662                    address: address1,
663                    info: Some(Account { nonce: 10, ..Default::default() }),
664                },
665            )
666            .unwrap();
667        provider
668            .tx_ref()
669            .put::<tables::AccountChangeSets>(
670                2,
671                AccountBeforeTx {
672                    address: address2,
673                    info: Some(Account { nonce: 20, ..Default::default() }),
674                },
675            )
676            .unwrap();
677        provider
678            .tx_ref()
679            .put::<tables::AccountChangeSets>(
680                3,
681                AccountBeforeTx {
682                    address: address1,
683                    info: Some(Account { nonce: 99, ..Default::default() }),
684                },
685            )
686            .unwrap();
687
688        provider
689            .tx_ref()
690            .put::<tables::StorageChangeSets>(
691                BlockNumberAddress((1, address1)),
692                StorageEntry { key: plain_slot1, value: U256::from(100) },
693            )
694            .unwrap();
695        provider
696            .tx_ref()
697            .put::<tables::StorageChangeSets>(
698                BlockNumberAddress((2, address1)),
699                StorageEntry { key: plain_slot2, value: U256::from(200) },
700            )
701            .unwrap();
702        provider
703            .tx_ref()
704            .put::<tables::StorageChangeSets>(
705                BlockNumberAddress((3, address2)),
706                StorageEntry { key: plain_slot1, value: U256::from(300) },
707            )
708            .unwrap();
709
710        let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
711
712        let expected_hashed_addr1 = keccak256(address1);
713        let expected_hashed_addr2 = keccak256(address2);
714        assert_eq!(sorted.accounts.len(), 2);
715
716        let account1 =
717            sorted.accounts.iter().find(|(addr, _)| *addr == expected_hashed_addr1).unwrap();
718        assert_eq!(account1.1.as_ref().unwrap().nonce, 10);
719
720        let account2 =
721            sorted.accounts.iter().find(|(addr, _)| *addr == expected_hashed_addr2).unwrap();
722        assert_eq!(account2.1.as_ref().unwrap().nonce, 20);
723
724        assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
725
726        let expected_hashed_slot1 = keccak256(plain_slot1);
727        let expected_hashed_slot2 = keccak256(plain_slot2);
728
729        assert_ne!(expected_hashed_slot1, plain_slot1);
730        assert_ne!(expected_hashed_slot2, plain_slot2);
731
732        let storage1 = sorted.storages.get(&expected_hashed_addr1).expect("storage for address1");
733        assert_eq!(storage1.storage_slots.len(), 2);
734        assert!(storage1
735            .storage_slots
736            .iter()
737            .any(|(k, v)| *k == expected_hashed_slot1 && *v == U256::from(100)));
738        assert!(storage1
739            .storage_slots
740            .iter()
741            .any(|(k, v)| *k == expected_hashed_slot2 && *v == U256::from(200)));
742        assert!(storage1.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
743
744        let storage2 = sorted.storages.get(&expected_hashed_addr2).expect("storage for address2");
745        assert_eq!(storage2.storage_slots.len(), 1);
746        assert_eq!(storage2.storage_slots[0].0, expected_hashed_slot1);
747        assert_eq!(storage2.storage_slots[0].1, U256::from(300));
748    }
749}