reth_trie/
state.rs

1use crate::{
2    prefix_set::{PrefixSetMut, TriePrefixSetsMut},
3    Nibbles,
4};
5use alloy_primitives::{
6    keccak256,
7    map::{hash_map, B256HashMap, B256HashSet, HashMap, HashSet},
8    Address, B256, U256,
9};
10use itertools::Itertools;
11use rayon::prelude::{IntoParallelIterator, ParallelIterator};
12use reth_primitives::Account;
13use reth_trie_common::KeyHasher;
14use revm::{
15    db::{states::CacheAccount, AccountStatus, BundleAccount},
16    primitives::FlaggedStorage,
17};
18use std::borrow::Cow;
19
20/// Representation of in-memory hashed state.
21#[derive(PartialEq, Eq, Clone, Default, Debug)]
22pub struct HashedPostState {
23    /// Mapping of hashed address to account info, `None` if destroyed.
24    pub accounts: B256HashMap<Option<Account>>,
25    /// Mapping of hashed address to hashed storage.
26    pub storages: B256HashMap<HashedStorage>,
27}
28
29impl HashedPostState {
30    /// Initialize [`HashedPostState`] from bundle state.
31    /// Hashes all changed accounts and storage entries that are currently stored in the bundle
32    /// state.
33    pub fn from_bundle_state<'a, KH: KeyHasher>(
34        state: impl IntoParallelIterator<Item = (&'a Address, &'a BundleAccount)>,
35    ) -> Self {
36        let hashed = state
37            .into_par_iter()
38            .map(|(address, account)| {
39                let hashed_address = KH::hash_key(address);
40                let hashed_account = account.info.as_ref().map(Into::into);
41                let hashed_storage = HashedStorage::from_plain_storage(
42                    account.status,
43                    account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
44                );
45                (hashed_address, (hashed_account, hashed_storage))
46            })
47            .collect::<Vec<(B256, (Option<Account>, HashedStorage))>>();
48
49        let mut accounts = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
50        let mut storages = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
51        for (address, (account, storage)) in hashed {
52            accounts.insert(address, account);
53            storages.insert(address, storage);
54        }
55        Self { accounts, storages }
56    }
57
58    /// Initialize [`HashedPostState`] from cached state.
59    /// Hashes all changed accounts and storage entries that are currently stored in cache.
60    pub fn from_cache_state<'a, KH: KeyHasher>(
61        state: impl IntoParallelIterator<Item = (&'a Address, &'a CacheAccount)>,
62    ) -> Self {
63        let hashed = state
64            .into_par_iter()
65            .map(|(address, account)| {
66                let hashed_address = KH::hash_key(address);
67                let hashed_account = account.account.as_ref().map(|a| (&a.info).into());
68                let hashed_storage = HashedStorage::from_plain_storage(
69                    account.status,
70                    account.account.as_ref().map(|a| a.storage.iter()).into_iter().flatten(),
71                );
72                (hashed_address, (hashed_account, hashed_storage))
73            })
74            .collect::<Vec<(B256, (Option<Account>, HashedStorage))>>();
75
76        let mut accounts = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
77        let mut storages = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
78        for (address, (account, storage)) in hashed {
79            accounts.insert(address, account);
80            storages.insert(address, storage);
81        }
82        Self { accounts, storages }
83    }
84
85    /// Construct [`HashedPostState`] from a single [`HashedStorage`].
86    pub fn from_hashed_storage(hashed_address: B256, storage: HashedStorage) -> Self {
87        Self {
88            accounts: HashMap::default(),
89            storages: HashMap::from_iter([(hashed_address, storage)]),
90        }
91    }
92
93    /// Set account entries on hashed state.
94    pub fn with_accounts(
95        mut self,
96        accounts: impl IntoIterator<Item = (B256, Option<Account>)>,
97    ) -> Self {
98        self.accounts = HashMap::from_iter(accounts);
99        self
100    }
101
102    /// Set storage entries on hashed state.
103    pub fn with_storages(
104        mut self,
105        storages: impl IntoIterator<Item = (B256, HashedStorage)>,
106    ) -> Self {
107        self.storages = HashMap::from_iter(storages);
108        self
109    }
110
111    /// Returns `true` if the hashed state is empty.
112    pub fn is_empty(&self) -> bool {
113        self.accounts.is_empty() && self.storages.is_empty()
114    }
115
116    /// Construct [`TriePrefixSetsMut`] from hashed post state.
117    /// The prefix sets contain the hashed account and storage keys that have been changed in the
118    /// post state.
119    pub fn construct_prefix_sets(&self) -> TriePrefixSetsMut {
120        // Populate account prefix set.
121        let mut account_prefix_set = PrefixSetMut::with_capacity(self.accounts.len());
122        let mut destroyed_accounts = HashSet::default();
123        for (hashed_address, account) in &self.accounts {
124            account_prefix_set.insert(Nibbles::unpack(hashed_address));
125
126            if account.is_none() {
127                destroyed_accounts.insert(*hashed_address);
128            }
129        }
130
131        // Populate storage prefix sets.
132        let mut storage_prefix_sets =
133            HashMap::with_capacity_and_hasher(self.storages.len(), Default::default());
134        for (hashed_address, hashed_storage) in &self.storages {
135            account_prefix_set.insert(Nibbles::unpack(hashed_address));
136            storage_prefix_sets.insert(*hashed_address, hashed_storage.construct_prefix_set());
137        }
138
139        TriePrefixSetsMut { account_prefix_set, storage_prefix_sets, destroyed_accounts }
140    }
141
142    /// Extend this hashed post state with contents of another.
143    /// Entries in the second hashed post state take precedence.
144    pub fn extend(&mut self, other: Self) {
145        self.extend_inner(Cow::Owned(other));
146    }
147
148    /// Extend this hashed post state with contents of another.
149    /// Entries in the second hashed post state take precedence.
150    ///
151    /// Slightly less efficient than [`Self::extend`], but preferred to `extend(other.clone())`.
152    pub fn extend_ref(&mut self, other: &Self) {
153        self.extend_inner(Cow::Borrowed(other));
154    }
155
156    fn extend_inner(&mut self, other: Cow<'_, Self>) {
157        self.accounts.extend(other.accounts.iter().map(|(&k, &v)| (k, v)));
158
159        self.storages.reserve(other.storages.len());
160        match other {
161            Cow::Borrowed(other) => {
162                self.extend_storages(other.storages.iter().map(|(k, v)| (*k, Cow::Borrowed(v))))
163            }
164            Cow::Owned(other) => {
165                self.extend_storages(other.storages.into_iter().map(|(k, v)| (k, Cow::Owned(v))))
166            }
167        }
168    }
169
170    fn extend_storages<'a>(
171        &mut self,
172        storages: impl IntoIterator<Item = (B256, Cow<'a, HashedStorage>)>,
173    ) {
174        for (hashed_address, storage) in storages {
175            match self.storages.entry(hashed_address) {
176                hash_map::Entry::Vacant(entry) => {
177                    entry.insert(storage.into_owned());
178                }
179                hash_map::Entry::Occupied(mut entry) => {
180                    entry.get_mut().extend(&storage);
181                }
182            }
183        }
184    }
185
186    /// Converts hashed post state into [`HashedPostStateSorted`].
187    pub fn into_sorted(self) -> HashedPostStateSorted {
188        let mut updated_accounts = Vec::new();
189        let mut destroyed_accounts = HashSet::default();
190        for (hashed_address, info) in self.accounts {
191            if let Some(info) = info {
192                updated_accounts.push((hashed_address, info));
193            } else {
194                destroyed_accounts.insert(hashed_address);
195            }
196        }
197        updated_accounts.sort_unstable_by_key(|(address, _)| *address);
198        let accounts = HashedAccountsSorted { accounts: updated_accounts, destroyed_accounts };
199
200        let storages = self
201            .storages
202            .into_iter()
203            .map(|(hashed_address, storage)| (hashed_address, storage.into_sorted()))
204            .collect();
205
206        HashedPostStateSorted { accounts, storages }
207    }
208}
209
210/// Representation of in-memory hashed storage.
211#[derive(PartialEq, Eq, Clone, Debug, Default)]
212pub struct HashedStorage {
213    /// Flag indicating whether the storage was wiped or not.
214    pub wiped: bool,
215    /// Mapping of hashed storage slot to storage value.
216    pub storage: B256HashMap<FlaggedStorage>,
217}
218
219impl HashedStorage {
220    /// Create new instance of [`HashedStorage`].
221    pub fn new(wiped: bool) -> Self {
222        Self { wiped, storage: HashMap::default() }
223    }
224
225    /// Create new hashed storage from iterator.
226    pub fn from_iter(wiped: bool, iter: impl IntoIterator<Item = (B256, FlaggedStorage)>) -> Self {
227        Self { wiped, storage: HashMap::from_iter(iter) }
228    }
229
230    /// Create new hashed storage from account status and plain storage.
231    pub fn from_plain_storage<'a>(
232        status: AccountStatus,
233        storage: impl IntoIterator<Item = (&'a U256, &'a FlaggedStorage)>,
234    ) -> Self {
235        Self::from_iter(
236            status.was_destroyed(),
237            storage.into_iter().map(|(key, value)| (keccak256(B256::from(*key)), value.clone())),
238        )
239    }
240
241    /// Construct [`PrefixSetMut`] from hashed storage.
242    pub fn construct_prefix_set(&self) -> PrefixSetMut {
243        if self.wiped {
244            PrefixSetMut::all()
245        } else {
246            let mut prefix_set = PrefixSetMut::with_capacity(self.storage.len());
247            for hashed_slot in self.storage.keys() {
248                prefix_set.insert(Nibbles::unpack(hashed_slot));
249            }
250            prefix_set
251        }
252    }
253
254    /// Extend hashed storage with contents of other.
255    /// The entries in second hashed storage take precedence.
256    pub fn extend(&mut self, other: &Self) {
257        if other.wiped {
258            self.wiped = true;
259            self.storage.clear();
260        }
261        self.storage.extend(other.storage.iter().map(|(&k, &v)| (k, v)));
262    }
263
264    /// Converts hashed storage into [`HashedStorageSorted`].
265    pub fn into_sorted(self) -> HashedStorageSorted {
266        let mut non_zero_valued_slots = Vec::new();
267        let mut zero_valued_slots = HashSet::default();
268        for (hashed_slot, value) in self.storage {
269            if value.is_zero() {
270                zero_valued_slots.insert(hashed_slot);
271            } else {
272                non_zero_valued_slots.push((hashed_slot, value));
273            }
274        }
275        non_zero_valued_slots.sort_unstable_by_key(|(key, _)| *key);
276
277        HashedStorageSorted { non_zero_valued_slots, zero_valued_slots, wiped: self.wiped }
278    }
279}
280
281/// Sorted hashed post state optimized for iterating during state trie calculation.
282#[derive(PartialEq, Eq, Clone, Default, Debug)]
283pub struct HashedPostStateSorted {
284    /// Updated state of accounts.
285    pub(crate) accounts: HashedAccountsSorted,
286    /// Map of hashed addresses to hashed storage.
287    pub(crate) storages: B256HashMap<HashedStorageSorted>,
288}
289
290impl HashedPostStateSorted {
291    /// Create new instance of [`HashedPostStateSorted`]
292    pub const fn new(
293        accounts: HashedAccountsSorted,
294        storages: B256HashMap<HashedStorageSorted>,
295    ) -> Self {
296        Self { accounts, storages }
297    }
298
299    /// Returns reference to hashed accounts.
300    pub const fn accounts(&self) -> &HashedAccountsSorted {
301        &self.accounts
302    }
303
304    /// Returns reference to hashed account storages.
305    pub const fn account_storages(&self) -> &B256HashMap<HashedStorageSorted> {
306        &self.storages
307    }
308}
309
310/// Sorted account state optimized for iterating during state trie calculation.
311#[derive(Clone, Eq, PartialEq, Default, Debug)]
312pub struct HashedAccountsSorted {
313    /// Sorted collection of hashed addresses and their account info.
314    pub(crate) accounts: Vec<(B256, Account)>,
315    /// Set of destroyed account keys.
316    pub(crate) destroyed_accounts: B256HashSet,
317}
318
319impl HashedAccountsSorted {
320    /// Returns a sorted iterator over updated accounts.
321    pub fn accounts_sorted(&self) -> impl Iterator<Item = (B256, Option<Account>)> {
322        self.accounts
323            .iter()
324            .map(|(address, account)| (*address, Some(*account)))
325            .chain(self.destroyed_accounts.iter().map(|address| (*address, None)))
326            .sorted_by_key(|entry| *entry.0)
327    }
328}
329
330/// Sorted hashed storage optimized for iterating during state trie calculation.
331#[derive(Clone, Eq, PartialEq, Debug)]
332pub struct HashedStorageSorted {
333    /// Sorted hashed storage slots with non-zero value.
334    pub(crate) non_zero_valued_slots: Vec<(B256, FlaggedStorage)>,
335    /// Slots that have been zero valued.
336    pub(crate) zero_valued_slots: B256HashSet,
337    /// Flag indicating whether the storage was wiped or not.
338    pub(crate) wiped: bool,
339}
340
341impl HashedStorageSorted {
342    /// Returns `true` if the account was wiped.
343    pub const fn is_wiped(&self) -> bool {
344        self.wiped
345    }
346
347    /// Returns a sorted iterator over updated storage slots.
348    pub fn storage_slots_sorted(&self) -> impl Iterator<Item = (B256, FlaggedStorage)> {
349        self.non_zero_valued_slots
350            .iter()
351            .map(|(hashed_slot, value)| (*hashed_slot, *value))
352            .chain(
353                self.zero_valued_slots
354                    .iter()
355                    .map(|hashed_slot| (*hashed_slot, FlaggedStorage::ZERO)),
356            )
357            .sorted_by_key(|entry| *entry.0)
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364    use alloy_primitives::Bytes;
365    use reth_trie_common::KeccakKeyHasher;
366    use revm::{
367        db::{
368            states::{plain_account::PlainStorage, StorageSlot},
369            PlainAccount, StorageWithOriginalValues,
370        },
371        primitives::{AccountInfo, Bytecode},
372    };
373
374    #[test]
375    fn hashed_state_wiped_extension() {
376        let hashed_address = B256::default();
377        let hashed_slot = B256::with_last_byte(64);
378        let hashed_slot2 = B256::with_last_byte(65);
379
380        // Initialize post state storage
381        let original_slot_value = FlaggedStorage::new(123, true);
382        let mut hashed_state = HashedPostState::default().with_storages([(
383            hashed_address,
384            HashedStorage::from_iter(
385                false,
386                [(hashed_slot, original_slot_value), (hashed_slot2, original_slot_value)],
387            ),
388        )]);
389
390        // Update single slot value
391        let updated_slot_value = FlaggedStorage::new_from_tuple((321, false));
392        let extension = HashedPostState::default().with_storages([(
393            hashed_address,
394            HashedStorage::from_iter(false, [(hashed_slot, updated_slot_value)]),
395        )]);
396        hashed_state.extend(extension);
397
398        let account_storage = hashed_state.storages.get(&hashed_address);
399        assert_eq!(
400            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
401            Some(&updated_slot_value)
402        );
403        assert_eq!(
404            account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
405            Some(&original_slot_value)
406        );
407        assert_eq!(account_storage.map(|st| st.wiped), Some(false));
408
409        // Wipe account storage
410        let wiped_extension =
411            HashedPostState::default().with_storages([(hashed_address, HashedStorage::new(true))]);
412        hashed_state.extend(wiped_extension);
413
414        let account_storage = hashed_state.storages.get(&hashed_address);
415        assert_eq!(account_storage.map(|st| st.storage.is_empty()), Some(true));
416        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
417
418        // Reinitialize single slot value
419        hashed_state.extend(HashedPostState::default().with_storages([(
420            hashed_address,
421            HashedStorage::from_iter(false, [(hashed_slot, original_slot_value)]),
422        )]));
423        let account_storage = hashed_state.storages.get(&hashed_address);
424        assert_eq!(
425            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
426            Some(&original_slot_value)
427        );
428        assert_eq!(account_storage.and_then(|st| st.storage.get(&hashed_slot2)), None);
429        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
430
431        // Reinitialize single slot value
432        hashed_state.extend(HashedPostState::default().with_storages([(
433            hashed_address,
434            HashedStorage::from_iter(false, [(hashed_slot2, updated_slot_value)]),
435        )]));
436        let account_storage = hashed_state.storages.get(&hashed_address);
437        assert_eq!(
438            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
439            Some(&original_slot_value)
440        );
441        assert_eq!(
442            account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
443            Some(&updated_slot_value)
444        );
445        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
446    }
447
448    #[test]
449    fn test_hashed_post_state_from_bundle_state() {
450        // Prepare a random Ethereum address as a key for the account.
451        let address = Address::random();
452
453        // Create a mock account info object.
454        let account_info = AccountInfo {
455            balance: U256::from(123),
456            nonce: 42,
457            code_hash: B256::random(),
458            code: Some(Bytecode::LegacyRaw(Bytes::from(vec![1, 2]))),
459        };
460
461        let mut storage = StorageWithOriginalValues::default();
462        storage.insert(
463            U256::from(1),
464            StorageSlot { present_value: FlaggedStorage::new_from_value(4), ..Default::default() },
465        );
466
467        // Create a `BundleAccount` struct to represent the account and its storage.
468        let account = BundleAccount {
469            status: AccountStatus::Changed,
470            info: Some(account_info.clone()),
471            storage,
472            original_info: None,
473        };
474
475        // Create a vector of tuples representing the bundle state.
476        let state = vec![(&address, &account)];
477
478        // Convert the bundle state into a hashed post state.
479        let hashed_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(state);
480
481        // Validate the hashed post state.
482        assert_eq!(hashed_state.accounts.len(), 1);
483        assert_eq!(hashed_state.storages.len(), 1);
484
485        // Validate the account info.
486        assert_eq!(
487            *hashed_state.accounts.get(&keccak256(address)).unwrap(),
488            Some(account_info.into())
489        );
490    }
491
492    #[test]
493    fn test_hashed_post_state_from_cache_state() {
494        // Prepare a random Ethereum address.
495        let address = Address::random();
496
497        // Create mock account info.
498        let account_info = AccountInfo {
499            balance: U256::from(500),
500            nonce: 5,
501            code_hash: B256::random(),
502            code: None,
503        };
504
505        let mut storage = PlainStorage::default();
506        storage.insert(U256::from(1), FlaggedStorage::new_from_value(35636));
507
508        // Create a `CacheAccount` with the mock account info.
509        let account = CacheAccount {
510            account: Some(PlainAccount { info: account_info.clone(), storage }),
511            status: AccountStatus::Changed,
512        };
513
514        // Create a vector of tuples representing the cache state.
515        let state = vec![(&address, &account)];
516
517        // Convert the cache state into a hashed post state.
518        let hashed_state = HashedPostState::from_cache_state::<KeccakKeyHasher>(state);
519
520        // Validate the hashed post state.
521        assert_eq!(hashed_state.accounts.len(), 1);
522        assert_eq!(hashed_state.storages.len(), 1);
523
524        // Validate the account info.
525        assert_eq!(
526            *hashed_state.accounts.get(&keccak256(address)).unwrap(),
527            Some(account_info.into())
528        );
529    }
530
531    #[test]
532    fn test_hashed_post_state_with_accounts() {
533        // Prepare random addresses and mock account info.
534        let address_1 = Address::random();
535        let address_2 = Address::random();
536
537        let account_info_1 = AccountInfo {
538            balance: U256::from(1000),
539            nonce: 1,
540            code_hash: B256::random(),
541            code: None,
542        };
543
544        // Create hashed accounts with addresses.
545        let account_1 = (keccak256(address_1), Some(account_info_1.into()));
546        let account_2 = (keccak256(address_2), None);
547
548        // Add accounts to the hashed post state.
549        let hashed_state = HashedPostState::default().with_accounts(vec![account_1, account_2]);
550
551        // Validate the hashed post state.
552        assert_eq!(hashed_state.accounts.len(), 2);
553        assert!(hashed_state.accounts.contains_key(&keccak256(address_1)));
554        assert!(hashed_state.accounts.contains_key(&keccak256(address_2)));
555    }
556
557    #[test]
558    fn test_hashed_post_state_with_storages() {
559        // Prepare random addresses and mock storage entries.
560        let address_1 = Address::random();
561        let address_2 = Address::random();
562
563        let storage_1 = (keccak256(address_1), HashedStorage::new(false));
564        let storage_2 = (keccak256(address_2), HashedStorage::new(true));
565
566        // Add storages to the hashed post state.
567        let hashed_state = HashedPostState::default().with_storages(vec![storage_1, storage_2]);
568
569        // Validate the hashed post state.
570        assert_eq!(hashed_state.storages.len(), 2);
571        assert!(hashed_state.storages.contains_key(&keccak256(address_1)));
572        assert!(hashed_state.storages.contains_key(&keccak256(address_2)));
573    }
574
575    #[test]
576    fn test_hashed_post_state_is_empty() {
577        // Create an empty hashed post state and validate it's empty.
578        let empty_state = HashedPostState::default();
579        assert!(empty_state.is_empty());
580
581        // Add an account and validate the state is no longer empty.
582        let non_empty_state = HashedPostState::default()
583            .with_accounts(vec![(keccak256(Address::random()), Some(Account::default()))]);
584        assert!(!non_empty_state.is_empty());
585    }
586}