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