refactor(trie): hashed post state (#6066)

This commit is contained in:
Roman Krasiuk
2024-01-15 10:44:58 +01:00
committed by GitHub
parent 5354c50445
commit d0b04f8859
12 changed files with 309 additions and 298 deletions

1
Cargo.lock generated
View File

@@ -6858,6 +6858,7 @@ dependencies = [
"reth-interfaces",
"reth-primitives",
"reth-provider",
"revm",
"serde_json",
"thiserror",
"tokio",

View File

@@ -306,8 +306,8 @@ impl Command {
let hashed_state = state.hash_state_slow();
let (state_root, trie_updates) = state
.state_root_calculator(provider_factory.provider()?.tx_ref(), &hashed_state)
.root_with_updates()?;
.hash_state_slow()
.state_root_with_updates(provider_factory.provider()?.tx_ref())?;
if state_root != block_with_senders.state_root {
eyre::bail!(

View File

@@ -1174,12 +1174,9 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
}
None => {
debug!(target: "blockchain_tree", blocks = ?block_hash_numbers, "Recomputing state root for insert");
let (state_root, trie_updates) = state
.state_root_calculator(
self.externals.provider_factory.provider()?.tx_ref(),
&hashed_state,
)
.root_with_updates()
let provider = self.externals.provider_factory.provider()?;
let (state_root, trie_updates) = hashed_state
.state_root_with_updates(provider.tx_ref())
.map_err(Into::<DatabaseError>::into)?;
let tip = blocks.tip();
if state_root != tip.state_root {

View File

@@ -6,16 +6,12 @@ use reth_db::{
};
use reth_interfaces::db::DatabaseError;
use reth_primitives::{
keccak256, logs_bloom,
logs_bloom,
revm::compat::{into_reth_acc, into_revm_acc},
Account, Address, BlockNumber, Bloom, Bytecode, Log, Receipt, Receipts, StorageEntry, B256,
U256,
};
use reth_trie::{
hashed_cursor::{HashedPostState, HashedPostStateCursorFactory, HashedStorage},
updates::TrieUpdates,
StateRoot, StateRootError,
};
use reth_trie::HashedPostState;
use revm::{
db::{states::BundleState, BundleAccount},
primitives::AccountInfo,
@@ -135,106 +131,10 @@ impl BundleStateWithReceipts {
self.bundle.bytecode(code_hash).map(Bytecode)
}
/// Hash all changed accounts and storage entries that are currently stored in the post state.
///
/// # Returns
///
/// The hashed post state.
/// Returns [HashedPostState] for this bundle state.
/// See [HashedPostState::from_bundle_state] for more info.
pub fn hash_state_slow(&self) -> HashedPostState {
let mut hashed_state = HashedPostState::default();
for (address, account) in self.bundle.state() {
let hashed_address = keccak256(address);
if let Some(account) = &account.info {
hashed_state.insert_account(hashed_address, into_reth_acc(account.clone()))
} else {
hashed_state.insert_destroyed_account(hashed_address);
}
// insert storage.
let mut hashed_storage = HashedStorage::new(account.status.was_destroyed());
for (key, value) in account.storage.iter() {
let hashed_key = keccak256(B256::new(key.to_be_bytes()));
if value.present_value.is_zero() {
hashed_storage.insert_zero_valued_slot(hashed_key);
} else {
hashed_storage.insert_non_zero_valued_storage(hashed_key, value.present_value);
}
}
hashed_state.insert_hashed_storage(hashed_address, hashed_storage)
}
hashed_state.sorted()
}
/// Returns [StateRoot] calculator based on database and in-memory state.
pub fn state_root_calculator<'a, 'b, TX: DbTx>(
&self,
tx: &'a TX,
hashed_post_state: &'b HashedPostState,
) -> StateRoot<&'a TX, HashedPostStateCursorFactory<'a, 'b, TX>> {
let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets();
let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, hashed_post_state);
StateRoot::from_tx(tx)
.with_hashed_cursor_factory(hashed_cursor_factory)
.with_changed_account_prefixes(account_prefix_set)
.with_changed_storage_prefixes(storage_prefix_set)
.with_destroyed_accounts(hashed_post_state.destroyed_accounts())
}
/// Calculate the state root for this [BundleState].
/// Internally, function calls [Self::hash_state_slow] to obtain the [HashedPostState].
/// Afterwards, it retrieves the prefixsets from the [HashedPostState] and uses them to
/// calculate the incremental state root.
///
/// # Example
///
/// ```
/// use reth_db::{database::Database, test_utils::create_test_rw_db};
/// use reth_primitives::{Account, Receipts, U256};
/// use reth_provider::BundleStateWithReceipts;
/// use std::collections::HashMap;
///
/// // Initialize the database
/// let db = create_test_rw_db();
///
/// // Initialize the bundle state
/// let bundle = BundleStateWithReceipts::new_init(
/// HashMap::from([(
/// [0x11; 20].into(),
/// (
/// None,
/// Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }),
/// HashMap::from([]),
/// ),
/// )]),
/// HashMap::from([]),
/// vec![],
/// Receipts::new(),
/// 0,
/// );
///
/// // Calculate the state root
/// let tx = db.tx().expect("failed to create transaction");
/// let state_root = bundle.state_root_slow(&tx);
/// ```
///
/// # Returns
///
/// The state root for this [BundleState].
pub fn state_root_slow<TX: DbTx>(&self, tx: &TX) -> Result<B256, StateRootError> {
let hashed_post_state = self.hash_state_slow();
self.state_root_calculator(tx, &hashed_post_state).root()
}
/// Calculates the state root for this [BundleState] and returns it alongside trie updates.
/// See [Self::state_root_slow] for more info.
pub fn state_root_slow_with_updates<TX: DbTx>(
&self,
tx: &TX,
) -> Result<(B256, TrieUpdates), StateRootError> {
let hashed_post_state = self.hash_state_slow();
self.state_root_calculator(tx, &hashed_post_state).root_with_updates()
HashedPostState::from_bundle_state(&self.bundle.state)
}
/// Transform block number to the index of block.
@@ -439,9 +339,10 @@ mod tests {
transaction::DbTx,
};
use reth_primitives::{
revm::compat::into_reth_acc, Address, Receipt, Receipts, StorageEntry, B256, U256,
keccak256, revm::compat::into_reth_acc, Address, Receipt, Receipts, StorageEntry, B256,
U256,
};
use reth_trie::test_utils::state_root;
use reth_trie::{test_utils::state_root, StateRoot};
use revm::{
db::{
states::{
@@ -1265,7 +1166,8 @@ mod tests {
let assert_state_root = |state: &State<EmptyDB>, expected: &PreState, msg| {
assert_eq!(
BundleStateWithReceipts::new(state.bundle_state.clone(), Receipts::default(), 0)
.state_root_slow(&tx)
.hash_state_slow()
.state_root(&tx)
.unwrap(),
state_root(expected.clone().into_iter().map(|(address, (account, storage))| (
address,

View File

@@ -5,7 +5,7 @@ use reth_db::{
DatabaseError,
};
use reth_primitives::{Account, StorageEntry, B256, U256};
use reth_trie::hashed_cursor::HashedPostState;
use reth_trie::HashedPostState;
use std::collections::BTreeMap;
/// Changes to the hashed state.
@@ -73,7 +73,7 @@ mod tests {
use super::*;
use crate::test_utils::create_test_provider_factory;
use reth_primitives::{keccak256, Address};
use reth_trie::hashed_cursor::HashedStorage;
use reth_trie::HashedStorage;
#[test]
fn wiped_entries_are_removed() {

View File

@@ -45,9 +45,7 @@ use reth_primitives::{
StorageEntry, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered,
TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, B256, U256,
};
use reth_trie::{
hashed_cursor::HashedPostState, prefix_set::PrefixSetMut, updates::TrieUpdates, StateRoot,
};
use reth_trie::{prefix_set::PrefixSetMut, updates::TrieUpdates, HashedPostState, StateRoot};
use revm::primitives::{BlockEnv, CfgEnv, SpecId};
use std::{
collections::{hash_map, BTreeMap, BTreeSet, HashMap},

View File

@@ -60,7 +60,10 @@ impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> {
impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> {
fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult<B256> {
bundle_state.state_root_slow(self.db).map_err(|err| ProviderError::Database(err.into()))
bundle_state
.hash_state_slow()
.state_root(self.db)
.map_err(|err| ProviderError::Database(err.into()))
}
fn state_root_with_updates(
@@ -68,7 +71,8 @@ impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> {
bundle_state: &BundleStateWithReceipts,
) -> ProviderResult<(B256, TrieUpdates)> {
bundle_state
.state_root_slow_with_updates(self.db)
.hash_state_slow()
.state_root_with_updates(self.db)
.map_err(|err| ProviderError::Database(err.into()))
}
}

View File

@@ -9,7 +9,7 @@ use reth_primitives::{
Block, BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, BlockWithSenders, ChainSpec,
Header, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, B256,
};
use reth_trie::{hashed_cursor::HashedPostState, updates::TrieUpdates};
use reth_trie::{updates::TrieUpdates, HashedPostState};
use std::ops::RangeInclusive;
/// Enum to control transaction hash inclusion.

View File

@@ -16,9 +16,12 @@ workspace = true
reth-primitives.workspace = true
reth-interfaces.workspace = true
reth-db.workspace = true
revm.workspace = true
# alloy
alloy-rlp.workspace = true
alloy-chains.workspace = true
# tokio
tokio = { workspace = true, default-features = false, features = ["sync"] }

View File

@@ -1,183 +1,11 @@
use super::{HashedAccountCursor, HashedCursorFactory, HashedStorageCursor};
use crate::prefix_set::{PrefixSet, PrefixSetMut};
use ahash::{AHashMap, AHashSet};
use crate::state::HashedPostState;
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
tables,
transaction::DbTx,
};
use reth_primitives::{trie::Nibbles, Account, StorageEntry, B256, U256};
/// The post state account storage with hashed slots.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct HashedStorage {
/// Hashed storage slots with non-zero.
non_zero_valued_storage: Vec<(B256, U256)>,
/// Slots that have been zero valued.
zero_valued_slots: AHashSet<B256>,
/// Whether the storage was wiped or not.
wiped: bool,
/// Whether the storage entries were sorted or not.
sorted: bool,
}
impl HashedStorage {
/// Create new instance of [HashedStorage].
pub fn new(wiped: bool) -> Self {
Self {
non_zero_valued_storage: Vec::new(),
zero_valued_slots: AHashSet::new(),
wiped,
sorted: true, // empty is sorted
}
}
/// Returns `true` if the storage was wiped.
pub fn wiped(&self) -> bool {
self.wiped
}
/// Returns all storage slots.
pub fn storage_slots(&self) -> impl Iterator<Item = (B256, U256)> + '_ {
self.zero_valued_slots
.iter()
.map(|slot| (*slot, U256::ZERO))
.chain(self.non_zero_valued_storage.iter().cloned())
}
/// Sorts the non zero value storage entries.
pub fn sort_storage(&mut self) {
if !self.sorted {
self.non_zero_valued_storage.sort_unstable_by_key(|(slot, _)| *slot);
self.sorted = true;
}
}
/// Insert non zero-valued storage entry.
pub fn insert_non_zero_valued_storage(&mut self, slot: B256, value: U256) {
debug_assert!(value != U256::ZERO, "value cannot be zero");
self.non_zero_valued_storage.push((slot, value));
self.sorted = false;
}
/// Insert zero-valued storage slot.
pub fn insert_zero_valued_slot(&mut self, slot: B256) {
self.zero_valued_slots.insert(slot);
}
}
/// The post state with hashed addresses as keys.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct HashedPostState {
/// Map of hashed addresses to account info.
accounts: Vec<(B256, Account)>,
/// Set of destroyed accounts.
destroyed_accounts: AHashSet<B256>,
/// Map of hashed addresses to hashed storage.
storages: AHashMap<B256, HashedStorage>,
/// Whether the account and storage entries were sorted or not.
sorted: bool,
}
impl Default for HashedPostState {
fn default() -> Self {
Self {
accounts: Vec::new(),
destroyed_accounts: AHashSet::new(),
storages: AHashMap::new(),
sorted: true, // empty is sorted
}
}
}
impl HashedPostState {
/// Sort and return self.
pub fn sorted(mut self) -> Self {
self.sort();
self
}
/// Returns all accounts with their state.
pub fn accounts(&self) -> impl Iterator<Item = (B256, Option<Account>)> + '_ {
self.destroyed_accounts.iter().map(|hashed_address| (*hashed_address, None)).chain(
self.accounts.iter().map(|(hashed_address, account)| (*hashed_address, Some(*account))),
)
}
/// Returns all account storages.
pub fn storages(&self) -> impl Iterator<Item = (&B256, &HashedStorage)> {
self.storages.iter()
}
/// Sort account and storage entries.
pub fn sort(&mut self) {
if !self.sorted {
for (_, storage) in self.storages.iter_mut() {
storage.sort_storage();
}
self.accounts.sort_unstable_by_key(|(address, _)| *address);
self.sorted = true;
}
}
/// Insert non-empty account info.
pub fn insert_account(&mut self, hashed_address: B256, account: Account) {
self.accounts.push((hashed_address, account));
self.sorted = false;
}
/// Insert destroyed hashed account key.
pub fn insert_destroyed_account(&mut self, hashed_address: B256) {
self.destroyed_accounts.insert(hashed_address);
}
/// Insert hashed storage entry.
pub fn insert_hashed_storage(&mut self, hashed_address: B256, hashed_storage: HashedStorage) {
self.sorted &= hashed_storage.sorted;
self.storages.insert(hashed_address, hashed_storage);
}
/// Returns all destroyed accounts.
pub fn destroyed_accounts(&self) -> AHashSet<B256> {
self.destroyed_accounts.clone()
}
/// Construct (PrefixSet)[PrefixSet] from hashed post state.
/// The prefix sets contain the hashed account and storage keys that have been changed in the
/// post state.
pub fn construct_prefix_sets(&self) -> (PrefixSet, AHashMap<B256, PrefixSet>) {
// Initialize prefix sets.
let mut account_prefix_set = PrefixSetMut::default();
let mut storage_prefix_set: AHashMap<B256, PrefixSetMut> = AHashMap::default();
// Populate account prefix set.
for (hashed_address, _) in &self.accounts {
account_prefix_set.insert(Nibbles::unpack(hashed_address));
}
for hashed_address in &self.destroyed_accounts {
account_prefix_set.insert(Nibbles::unpack(hashed_address));
}
// Populate storage prefix sets.
for (hashed_address, hashed_storage) in self.storages.iter() {
account_prefix_set.insert(Nibbles::unpack(hashed_address));
let storage_prefix_set_entry = storage_prefix_set.entry(*hashed_address).or_default();
for (hashed_slot, _) in &hashed_storage.non_zero_valued_storage {
storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot));
}
for hashed_slot in &hashed_storage.zero_valued_slots {
storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot));
}
}
(
account_prefix_set.freeze(),
storage_prefix_set.into_iter().map(|(k, v)| (k, v.freeze())).collect(),
)
}
}
use reth_primitives::{Account, StorageEntry, B256, U256};
/// The hashed cursor factory for the post state.
#[derive(Debug)]
@@ -573,6 +401,8 @@ where
#[cfg(test)]
mod tests {
use crate::HashedStorage;
use super::*;
use proptest::prelude::*;
use reth_db::{database::Database, test_utils::create_test_rw_db, transaction::DbTxMut};

View File

@@ -32,6 +32,10 @@ pub use errors::*;
// The iterators for traversing existing intermediate hashes and updated trie leaves.
pub(crate) mod node_iter;
/// In-memory hashed state.
mod state;
pub use state::{HashedPostState, HashedStorage};
/// Merkle proof generation.
pub mod proof;

272
crates/trie/src/state.rs Normal file
View File

@@ -0,0 +1,272 @@
use crate::{
hashed_cursor::HashedPostStateCursorFactory,
prefix_set::{PrefixSet, PrefixSetMut},
updates::TrieUpdates,
StateRoot, StateRootError,
};
use ahash::{AHashMap, AHashSet};
use reth_db::transaction::DbTx;
use reth_primitives::{
keccak256, revm::compat::into_reth_acc, trie::Nibbles, Account, Address, B256, U256,
};
use revm::db::BundleAccount;
/// The post state with hashed addresses as keys.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct HashedPostState {
/// Collection of hashed addresses and their account info.
pub(crate) accounts: Vec<(B256, Account)>,
/// Set of destroyed account keys.
pub(crate) destroyed_accounts: AHashSet<B256>,
/// Map of hashed addresses to hashed storage.
pub(crate) storages: AHashMap<B256, HashedStorage>,
/// Flag indicating whether the account and storage entries were sorted.
pub(crate) sorted: bool,
}
impl Default for HashedPostState {
fn default() -> Self {
Self {
accounts: Vec::new(),
destroyed_accounts: AHashSet::new(),
storages: AHashMap::new(),
sorted: true, // empty is sorted
}
}
}
impl HashedPostState {
/// Initialize [HashedPostState] from bundle state.
/// Hashes all changed accounts and storage entries that are currently stored in the bundle
/// state.
pub fn from_bundle_state<'a>(
state: impl IntoIterator<Item = (&'a Address, &'a BundleAccount)>,
) -> Self {
let mut hashed_state = Self::default();
for (address, account) in state {
let hashed_address = keccak256(address);
if let Some(account) = &account.info {
hashed_state.insert_account(hashed_address, into_reth_acc(account.clone()))
} else {
hashed_state.insert_destroyed_account(hashed_address);
}
// insert storage.
let mut hashed_storage = HashedStorage::new(account.status.was_destroyed());
for (key, value) in account.storage.iter() {
let hashed_key = keccak256(B256::new(key.to_be_bytes()));
if value.present_value.is_zero() {
hashed_storage.insert_zero_valued_slot(hashed_key);
} else {
hashed_storage.insert_non_zero_valued_storage(hashed_key, value.present_value);
}
}
hashed_state.insert_hashed_storage(hashed_address, hashed_storage)
}
hashed_state.sorted()
}
/// Sort and return self.
pub fn sorted(mut self) -> Self {
self.sort();
self
}
/// Returns all accounts with their state.
pub fn accounts(&self) -> impl Iterator<Item = (B256, Option<Account>)> + '_ {
self.destroyed_accounts.iter().map(|hashed_address| (*hashed_address, None)).chain(
self.accounts.iter().map(|(hashed_address, account)| (*hashed_address, Some(*account))),
)
}
/// Returns all account storages.
pub fn storages(&self) -> impl Iterator<Item = (&B256, &HashedStorage)> {
self.storages.iter()
}
/// Sort account and storage entries.
pub fn sort(&mut self) {
if !self.sorted {
for (_, storage) in self.storages.iter_mut() {
storage.sort_storage();
}
self.accounts.sort_unstable_by_key(|(address, _)| *address);
self.sorted = true;
}
}
/// Insert non-empty account info.
pub fn insert_account(&mut self, hashed_address: B256, account: Account) {
self.accounts.push((hashed_address, account));
self.sorted = false;
}
/// Insert destroyed hashed account key.
pub fn insert_destroyed_account(&mut self, hashed_address: B256) {
self.destroyed_accounts.insert(hashed_address);
}
/// Insert hashed storage entry.
pub fn insert_hashed_storage(&mut self, hashed_address: B256, hashed_storage: HashedStorage) {
self.sorted &= hashed_storage.sorted;
self.storages.insert(hashed_address, hashed_storage);
}
/// Returns all destroyed accounts.
pub fn destroyed_accounts(&self) -> AHashSet<B256> {
self.destroyed_accounts.clone()
}
/// Construct [PrefixSet] from hashed post state.
/// The prefix sets contain the hashed account and storage keys that have been changed in the
/// post state.
pub fn construct_prefix_sets(&self) -> (PrefixSet, AHashMap<B256, PrefixSet>) {
// Initialize prefix sets.
let mut account_prefix_set = PrefixSetMut::default();
let mut storage_prefix_set: AHashMap<B256, PrefixSetMut> = AHashMap::default();
// Populate account prefix set.
for (hashed_address, _) in &self.accounts {
account_prefix_set.insert(Nibbles::unpack(hashed_address));
}
for hashed_address in &self.destroyed_accounts {
account_prefix_set.insert(Nibbles::unpack(hashed_address));
}
// Populate storage prefix sets.
for (hashed_address, hashed_storage) in self.storages.iter() {
account_prefix_set.insert(Nibbles::unpack(hashed_address));
let storage_prefix_set_entry = storage_prefix_set.entry(*hashed_address).or_default();
for (hashed_slot, _) in &hashed_storage.non_zero_valued_storage {
storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot));
}
for hashed_slot in &hashed_storage.zero_valued_slots {
storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot));
}
}
(
account_prefix_set.freeze(),
storage_prefix_set.into_iter().map(|(k, v)| (k, v.freeze())).collect(),
)
}
/// Returns [StateRoot] calculator based on database and in-memory state.
fn state_root_calculator<'a, TX: DbTx>(
&self,
tx: &'a TX,
) -> StateRoot<&'a TX, HashedPostStateCursorFactory<'a, '_, TX>> {
let (account_prefix_set, storage_prefix_set) = self.construct_prefix_sets();
let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, self);
StateRoot::from_tx(tx)
.with_hashed_cursor_factory(hashed_cursor_factory)
.with_changed_account_prefixes(account_prefix_set)
.with_changed_storage_prefixes(storage_prefix_set)
.with_destroyed_accounts(self.destroyed_accounts())
}
/// Calculate the state root for this [HashedPostState].
/// Internally, this method retrieves prefixsets and uses them
/// to calculate incremental state root.
///
/// # Example
///
/// ```
/// use reth_db::{database::Database, test_utils::create_test_rw_db};
/// use reth_primitives::{Account, U256};
/// use reth_trie::HashedPostState;
///
/// // Initialize the database
/// let db = create_test_rw_db();
///
/// // Initialize hashed post state
/// let mut hashed_state = HashedPostState::default();
/// hashed_state.insert_account(
/// [0x11; 32].into(),
/// Account { nonce: 1, balance: U256::from(10), bytecode_hash: None },
/// );
/// hashed_state.sort();
///
/// // Calculate the state root
/// let tx = db.tx().expect("failed to create transaction");
/// let state_root = hashed_state.state_root(&tx);
/// ```
///
/// # Returns
///
/// The state root for this [HashedPostState].
pub fn state_root<TX: DbTx>(&self, tx: &TX) -> Result<B256, StateRootError> {
self.state_root_calculator(tx).root()
}
/// Calculates the state root for this [HashedPostState] and returns it alongside trie updates.
/// See [Self::state_root] for more info.
pub fn state_root_with_updates<TX: DbTx>(
&self,
tx: &TX,
) -> Result<(B256, TrieUpdates), StateRootError> {
self.state_root_calculator(tx).root_with_updates()
}
}
/// The post state account storage with hashed slots.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct HashedStorage {
/// Hashed storage slots with non-zero.
pub(crate) non_zero_valued_storage: Vec<(B256, U256)>,
/// Slots that have been zero valued.
pub(crate) zero_valued_slots: AHashSet<B256>,
/// Whether the storage was wiped or not.
pub(crate) wiped: bool,
/// Whether the storage entries were sorted or not.
pub(crate) sorted: bool,
}
impl HashedStorage {
/// Create new instance of [HashedStorage].
pub fn new(wiped: bool) -> Self {
Self {
non_zero_valued_storage: Vec::new(),
zero_valued_slots: AHashSet::new(),
wiped,
sorted: true, // empty is sorted
}
}
/// Returns `true` if the storage was wiped.
pub fn wiped(&self) -> bool {
self.wiped
}
/// Returns all storage slots.
pub fn storage_slots(&self) -> impl Iterator<Item = (B256, U256)> + '_ {
self.zero_valued_slots
.iter()
.map(|slot| (*slot, U256::ZERO))
.chain(self.non_zero_valued_storage.iter().cloned())
}
/// Sorts the non zero value storage entries.
pub fn sort_storage(&mut self) {
if !self.sorted {
self.non_zero_valued_storage.sort_unstable_by_key(|(slot, _)| *slot);
self.sorted = true;
}
}
/// Insert non zero-valued storage entry.
pub fn insert_non_zero_valued_storage(&mut self, slot: B256, value: U256) {
debug_assert!(value != U256::ZERO, "value cannot be zero");
self.non_zero_valued_storage.push((slot, value));
self.sorted = false;
}
/// Insert zero-valued storage slot.
pub fn insert_zero_valued_slot(&mut self, slot: B256) {
self.zero_valued_slots.insert(slot);
}
}