mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix(poststate): handle secondary selfdestructs (#2634)
This commit is contained in:
@ -604,7 +604,7 @@ mod tests {
|
||||
Bytecode, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, H256, MAINNET, U256,
|
||||
};
|
||||
use reth_provider::{
|
||||
post_state::{ChangedStorage, Storage},
|
||||
post_state::{Storage, StorageTransition, StorageWipe},
|
||||
AccountProvider, BlockHashProvider, StateProvider, StateRootProvider,
|
||||
};
|
||||
use reth_rlp::Decodable;
|
||||
@ -823,8 +823,8 @@ mod tests {
|
||||
block.number,
|
||||
BTreeMap::from([(
|
||||
account1,
|
||||
ChangedStorage {
|
||||
wiped: false,
|
||||
StorageTransition {
|
||||
wipe: StorageWipe::None,
|
||||
// Slot 1 changed from 0 to 2
|
||||
storage: BTreeMap::from([(U256::from(1), U256::ZERO)])
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ mod account;
|
||||
pub use account::AccountChanges;
|
||||
|
||||
mod storage;
|
||||
pub use storage::{ChangedStorage, Storage, StorageChanges, StorageChangeset};
|
||||
pub use storage::{Storage, StorageChanges, StorageChangeset, StorageTransition, StorageWipe};
|
||||
|
||||
// todo: rewrite all the docs for this
|
||||
/// The state of accounts after execution of one or more transactions, including receipts and new
|
||||
@ -245,15 +245,39 @@ impl PostState {
|
||||
// todo: note overwrite behavior, i.e. changes in `other` take precedent
|
||||
/// Extend this [PostState] with the changes in another [PostState].
|
||||
pub fn extend(&mut self, mut other: PostState) {
|
||||
// Update plain state
|
||||
self.accounts.extend(other.accounts);
|
||||
for (address, their_storage) in other.storage {
|
||||
let our_storage = self.storage.entry(address).or_default();
|
||||
if their_storage.wiped() {
|
||||
our_storage.times_wiped += their_storage.times_wiped;
|
||||
our_storage.storage.clear();
|
||||
// Insert storage change sets
|
||||
for (block_number, storage_changes) in std::mem::take(&mut other.storage_changes).inner {
|
||||
for (address, their_storage_transition) in storage_changes {
|
||||
let our_storage = self.storage.entry(address).or_default();
|
||||
let (wipe, storage) = if their_storage_transition.wipe.is_wiped() {
|
||||
// Check existing storage change.
|
||||
match self.storage_changes.get(&block_number).and_then(|ch| ch.get(&address)) {
|
||||
Some(change) if change.wipe.is_wiped() => (), // already counted
|
||||
_ => {
|
||||
our_storage.times_wiped += 1;
|
||||
}
|
||||
};
|
||||
// Check if this is the first wipe.
|
||||
let wipe = if our_storage.times_wiped == 1 {
|
||||
StorageWipe::Primary
|
||||
} else {
|
||||
// Even if the wipe in other poststate was primary before, demote it to
|
||||
// secondary.
|
||||
StorageWipe::Secondary
|
||||
};
|
||||
let mut wiped_storage = std::mem::take(&mut our_storage.storage);
|
||||
wiped_storage.extend(their_storage_transition.storage);
|
||||
(wipe, wiped_storage)
|
||||
} else {
|
||||
(StorageWipe::None, their_storage_transition.storage)
|
||||
};
|
||||
self.storage_changes.insert_for_block_and_address(
|
||||
block_number,
|
||||
address,
|
||||
wipe,
|
||||
storage.into_iter(),
|
||||
);
|
||||
}
|
||||
our_storage.storage.extend(their_storage.storage);
|
||||
}
|
||||
|
||||
// Insert account change sets
|
||||
@ -261,19 +285,13 @@ impl PostState {
|
||||
self.account_changes.insert_for_block(block_number, account_changes);
|
||||
}
|
||||
|
||||
// Insert storage change sets
|
||||
for (block_number, storage_changes) in std::mem::take(&mut other.storage_changes).inner {
|
||||
for (address, their_storage) in storage_changes {
|
||||
if their_storage.wiped {
|
||||
self.storage_changes.set_wiped(block_number, address);
|
||||
}
|
||||
self.storage_changes.insert_for_block_and_address(
|
||||
block_number,
|
||||
address,
|
||||
their_storage.storage.into_iter(),
|
||||
);
|
||||
}
|
||||
// Update plain state
|
||||
self.accounts.extend(other.accounts);
|
||||
for (address, their_storage) in other.storage {
|
||||
let our_storage = self.storage.entry(address).or_default();
|
||||
our_storage.storage.extend(their_storage.storage);
|
||||
}
|
||||
|
||||
self.receipts.extend(other.receipts);
|
||||
self.bytecode.extend(other.bytecode);
|
||||
}
|
||||
@ -291,7 +309,7 @@ impl PostState {
|
||||
for (_, storages) in storage_changes_to_revert.into_iter().rev() {
|
||||
for (address, storage) in storages {
|
||||
self.storage.entry(address).and_modify(|head_storage| {
|
||||
if storage.wiped {
|
||||
if storage.wipe.is_wiped() {
|
||||
head_storage.times_wiped -= 1;
|
||||
}
|
||||
head_storage.storage.extend(storage.clone().storage);
|
||||
@ -368,8 +386,16 @@ impl PostState {
|
||||
|
||||
let storage = self.storage.entry(address).or_default();
|
||||
storage.times_wiped += 1;
|
||||
storage.storage.clear();
|
||||
self.storage_changes.set_wiped(block_number, address);
|
||||
let wipe =
|
||||
if storage.times_wiped == 1 { StorageWipe::Primary } else { StorageWipe::Secondary };
|
||||
|
||||
let wiped_storage = std::mem::take(&mut storage.storage);
|
||||
self.storage_changes.insert_for_block_and_address(
|
||||
block_number,
|
||||
address,
|
||||
wipe,
|
||||
wiped_storage.into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Add changed storage values to the post-state.
|
||||
@ -387,6 +413,7 @@ impl PostState {
|
||||
self.storage_changes.insert_for_block_and_address(
|
||||
block_number,
|
||||
address,
|
||||
StorageWipe::None,
|
||||
changeset.into_iter().map(|(slot, (old, _))| (slot, old)),
|
||||
);
|
||||
}
|
||||
@ -413,19 +440,6 @@ impl PostState {
|
||||
&mut self,
|
||||
tx: &TX,
|
||||
) -> Result<(), DbError> {
|
||||
// Write account changes
|
||||
tracing::trace!(target: "provider::post_state", "Writing account changes");
|
||||
let mut account_changeset_cursor = tx.cursor_dup_write::<tables::AccountChangeSet>()?;
|
||||
for (block_number, account_changes) in
|
||||
std::mem::take(&mut self.account_changes).inner.into_iter()
|
||||
{
|
||||
for (address, info) in account_changes.into_iter() {
|
||||
tracing::trace!(target: "provider::post_state", block_number, ?address, old = ?info, "Account changed");
|
||||
account_changeset_cursor
|
||||
.append_dup(block_number, AccountBeforeTx { address, info })?;
|
||||
}
|
||||
}
|
||||
|
||||
// Write storage changes
|
||||
tracing::trace!(target: "provider::post_state", "Writing storage changes");
|
||||
let mut storages_cursor = tx.cursor_dup_write::<tables::PlainStorageState>()?;
|
||||
@ -436,13 +450,33 @@ impl PostState {
|
||||
for (address, mut storage) in storage_changes.into_iter() {
|
||||
let storage_id = BlockNumberAddress((block_number, address));
|
||||
|
||||
if storage.wiped {
|
||||
// If the account was created and wiped at the same block, skip all storage changes
|
||||
if storage.wipe.is_wiped() &&
|
||||
self.account_changes
|
||||
.get(&block_number)
|
||||
.and_then(|changes| changes.get(&address).map(|info| info.is_none()))
|
||||
// No account info available, fallback to `false`
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
// If we are writing the primary storage wipe transition, the pre-existing plain
|
||||
// storage state has to be taken from the database and written to storage history.
|
||||
// See [StorageWipe::Primary] for more details.
|
||||
if storage.wipe.is_primary() {
|
||||
if let Some((_, entry)) = storages_cursor.seek_exact(address)? {
|
||||
tracing::trace!(target: "provider::post_state", ?storage_id, key = ?entry.key, "Storage wiped");
|
||||
storage.storage.insert(entry.key.into(), entry.value);
|
||||
let key = U256::from_be_bytes(entry.key.to_fixed_bytes());
|
||||
if !storage.storage.contains_key(&key) {
|
||||
storage.storage.insert(entry.key.into(), entry.value);
|
||||
}
|
||||
|
||||
while let Some(entry) = storages_cursor.next_dup_val()? {
|
||||
storage.storage.insert(entry.key.into(), entry.value);
|
||||
let key = U256::from_be_bytes(entry.key.to_fixed_bytes());
|
||||
if !storage.storage.contains_key(&key) {
|
||||
storage.storage.insert(entry.key.into(), entry.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -457,6 +491,19 @@ impl PostState {
|
||||
}
|
||||
}
|
||||
|
||||
// Write account changes
|
||||
tracing::trace!(target: "provider::post_state", "Writing account changes");
|
||||
let mut account_changeset_cursor = tx.cursor_dup_write::<tables::AccountChangeSet>()?;
|
||||
for (block_number, account_changes) in
|
||||
std::mem::take(&mut self.account_changes).inner.into_iter()
|
||||
{
|
||||
for (address, info) in account_changes.into_iter() {
|
||||
tracing::trace!(target: "provider::post_state", block_number, ?address, old = ?info, "Account changed");
|
||||
account_changeset_cursor
|
||||
.append_dup(block_number, AccountBeforeTx { address, info })?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -467,7 +514,8 @@ impl PostState {
|
||||
// Write new storage state
|
||||
let mut storages_cursor = tx.cursor_dup_write::<tables::PlainStorageState>()?;
|
||||
for (address, storage) in self.storage.into_iter() {
|
||||
// If the storage was wiped, remove all previous entries from the database.
|
||||
// If the storage was wiped at least once, remove all previous entries from the
|
||||
// database.
|
||||
if storage.wiped() {
|
||||
tracing::trace!(target: "provider::post_state", ?address, "Wiping storage from plain state");
|
||||
if storages_cursor.seek_exact(address)?.is_some() {
|
||||
@ -775,6 +823,197 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_to_db_multiple_selfdestructs() {
|
||||
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
||||
let tx = db.tx_mut().expect("Could not get database tx");
|
||||
|
||||
let address1 = Address::random();
|
||||
|
||||
let mut init_state = PostState::new();
|
||||
init_state.create_account(0, address1, Account::default());
|
||||
init_state.change_storage(
|
||||
0,
|
||||
address1,
|
||||
// 0x00 => 0 => 1
|
||||
// 0x01 => 0 => 2
|
||||
BTreeMap::from([
|
||||
(U256::from(0), (U256::ZERO, U256::from(1))),
|
||||
(U256::from(1), (U256::ZERO, U256::from(2))),
|
||||
]),
|
||||
);
|
||||
init_state.write_to_db(&tx).expect("Could not write init state to DB");
|
||||
|
||||
let mut post_state = PostState::new();
|
||||
post_state.change_storage(
|
||||
1,
|
||||
address1,
|
||||
// 0x00 => 1 => 2
|
||||
BTreeMap::from([(U256::from(0), (U256::from(1), U256::from(2)))]),
|
||||
);
|
||||
post_state.destroy_account(2, address1, Account::default());
|
||||
post_state.create_account(3, address1, Account::default());
|
||||
post_state.change_storage(
|
||||
4,
|
||||
address1,
|
||||
// 0x00 => 0 => 2
|
||||
// 0x02 => 0 => 4
|
||||
// 0x06 => 0 => 6
|
||||
BTreeMap::from([
|
||||
(U256::from(0), (U256::ZERO, U256::from(2))),
|
||||
(U256::from(2), (U256::ZERO, U256::from(4))),
|
||||
(U256::from(6), (U256::ZERO, U256::from(6))),
|
||||
]),
|
||||
);
|
||||
post_state.destroy_account(5, address1, Account::default());
|
||||
|
||||
// Create, change, destroy and recreate in the same block.
|
||||
post_state.create_account(6, address1, Account::default());
|
||||
post_state.change_storage(
|
||||
6,
|
||||
address1,
|
||||
// 0x00 => 0 => 2
|
||||
BTreeMap::from([(U256::from(0), (U256::ZERO, U256::from(2)))]),
|
||||
);
|
||||
post_state.destroy_account(6, address1, Account::default());
|
||||
post_state.create_account(6, address1, Account::default());
|
||||
|
||||
post_state.change_storage(
|
||||
7,
|
||||
address1,
|
||||
// 0x00 => 0 => 9
|
||||
BTreeMap::from([(U256::from(0), (U256::ZERO, U256::from(9)))]),
|
||||
);
|
||||
|
||||
post_state.write_to_db(&tx).expect("Could not write post state to DB");
|
||||
|
||||
let mut storage_changeset_cursor = tx
|
||||
.cursor_dup_read::<tables::StorageChangeSet>()
|
||||
.expect("Could not open plain storage state cursor");
|
||||
let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap();
|
||||
|
||||
// Iterate through all storage changes
|
||||
|
||||
// Block <number>
|
||||
// <slot>: <expected value before>
|
||||
// ...
|
||||
|
||||
// Block #0
|
||||
// 0x00: 0
|
||||
// 0x01: 0
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((0, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO }
|
||||
)))
|
||||
);
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((0, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(1), value: U256::ZERO }
|
||||
)))
|
||||
);
|
||||
|
||||
// Block #1
|
||||
// 0x00: 1
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((1, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(1) }
|
||||
)))
|
||||
);
|
||||
|
||||
// Block #2 (destroyed)
|
||||
// 0x00: 2
|
||||
// 0x01: 2
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((2, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(2) }
|
||||
)))
|
||||
);
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((2, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(1), value: U256::from(2) }
|
||||
)))
|
||||
);
|
||||
|
||||
// Block #3
|
||||
// no storage changes
|
||||
|
||||
// Block #4
|
||||
// 0x00: 0
|
||||
// 0x02: 0
|
||||
// 0x06: 0
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((4, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO }
|
||||
)))
|
||||
);
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((4, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(2), value: U256::ZERO }
|
||||
)))
|
||||
);
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((4, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(6), value: U256::ZERO }
|
||||
)))
|
||||
);
|
||||
|
||||
// Block #5 (destroyed)
|
||||
// 0x00: 2
|
||||
// 0x02: 4
|
||||
// 0x06: 6
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((5, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(2) }
|
||||
)))
|
||||
);
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((5, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(2), value: U256::from(4) }
|
||||
)))
|
||||
);
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((5, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(6), value: U256::from(6) }
|
||||
)))
|
||||
);
|
||||
|
||||
// Block #6
|
||||
// no storage changes (only inter block changes)
|
||||
|
||||
// Block #7
|
||||
// 0x00: 0
|
||||
assert_eq!(
|
||||
storage_changes.next(),
|
||||
Some(Ok((
|
||||
BlockNumberAddress((7, address1)),
|
||||
StorageEntry { key: H256::from_low_u64_be(0), value: U256::ZERO }
|
||||
)))
|
||||
);
|
||||
assert_eq!(storage_changes.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reuse_selfdestructed_account() {
|
||||
let address_a = Address::zero();
|
||||
@ -1017,12 +1256,12 @@ mod tests {
|
||||
block,
|
||||
BTreeMap::from([(
|
||||
address,
|
||||
ChangedStorage {
|
||||
StorageTransition {
|
||||
storage: BTreeMap::from([
|
||||
(U256::from(0), U256::from(0)),
|
||||
(U256::from(1), U256::from(3))
|
||||
]),
|
||||
wiped: false,
|
||||
wipe: StorageWipe::None,
|
||||
}
|
||||
)])
|
||||
)]),
|
||||
@ -1089,9 +1328,9 @@ mod tests {
|
||||
block,
|
||||
BTreeMap::from([(
|
||||
address,
|
||||
ChangedStorage {
|
||||
StorageTransition {
|
||||
storage: BTreeMap::from([(U256::from(0), U256::from(0)),]),
|
||||
wiped: false,
|
||||
wipe: StorageWipe::None,
|
||||
}
|
||||
)])
|
||||
)]),
|
||||
@ -1132,9 +1371,9 @@ mod tests {
|
||||
block,
|
||||
BTreeMap::from([(
|
||||
address,
|
||||
ChangedStorage {
|
||||
StorageTransition {
|
||||
storage: BTreeMap::from([(U256::from(0), U256::from(1)),]),
|
||||
wiped: false,
|
||||
wipe: StorageWipe::None,
|
||||
}
|
||||
)])
|
||||
)]),
|
||||
@ -1166,9 +1405,9 @@ mod tests {
|
||||
block,
|
||||
BTreeMap::from([(
|
||||
address,
|
||||
ChangedStorage {
|
||||
StorageTransition {
|
||||
storage: BTreeMap::from([(U256::from(0), U256::from(0)),]),
|
||||
wiped: false,
|
||||
wipe: StorageWipe::None,
|
||||
}
|
||||
)])
|
||||
)]),
|
||||
|
||||
@ -1,24 +1,47 @@
|
||||
use derive_more::Deref;
|
||||
use reth_primitives::{Address, BlockNumber, U256};
|
||||
use std::collections::{btree_map::Entry, BTreeMap};
|
||||
use std::collections::{btree_map::Entry, BTreeMap, HashSet};
|
||||
|
||||
/// Storage for an account with the old and new values for each slot: (slot -> (old, new)).
|
||||
pub type StorageChangeset = BTreeMap<U256, (U256, U256)>;
|
||||
|
||||
/// Changed storage state for the account.
|
||||
///
|
||||
/// # Wiped Storage
|
||||
///
|
||||
/// The field `wiped` denotes whether the pre-existing storage in the database should be cleared or
|
||||
/// not.
|
||||
/// The storage state of the account before the state transition.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct ChangedStorage {
|
||||
/// Whether the storage was wiped or not.
|
||||
pub wiped: bool,
|
||||
pub struct StorageTransition {
|
||||
/// The indicator of the storage wipe.
|
||||
pub wipe: StorageWipe,
|
||||
/// The storage slots.
|
||||
pub storage: BTreeMap<U256, U256>,
|
||||
}
|
||||
|
||||
/// The indicator of the storage wipe.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub enum StorageWipe {
|
||||
/// The storage was not wiped at this change.
|
||||
#[default]
|
||||
None,
|
||||
/// The storage was wiped for the first time in the current in-memory state.
|
||||
///
|
||||
/// When writing history to the database, on the primary storage wipe the pre-existing storage
|
||||
/// will be inserted as the storage state before this transition.
|
||||
Primary,
|
||||
/// The storage had been already wiped before.
|
||||
Secondary,
|
||||
}
|
||||
|
||||
impl StorageWipe {
|
||||
/// Returns `true` if the wipe occurred at this transition.
|
||||
pub fn is_wiped(&self) -> bool {
|
||||
matches!(self, Self::Primary | Self::Secondary)
|
||||
}
|
||||
|
||||
/// Returns `true` if the primary wiped occurred at this transition.
|
||||
/// See [StorageWipe::Primary] for more details.
|
||||
pub fn is_primary(&self) -> bool {
|
||||
matches!(self, Self::Primary)
|
||||
}
|
||||
}
|
||||
|
||||
/// Latest storage state for the account.
|
||||
///
|
||||
/// # Wiped Storage
|
||||
@ -48,28 +71,27 @@ impl Storage {
|
||||
pub struct StorageChanges {
|
||||
/// The inner mapping of block changes.
|
||||
#[deref]
|
||||
pub inner: BTreeMap<BlockNumber, BTreeMap<Address, ChangedStorage>>,
|
||||
pub inner: BTreeMap<BlockNumber, BTreeMap<Address, StorageTransition>>,
|
||||
/// Hand tracked change size.
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl StorageChanges {
|
||||
/// Set storage `wiped` flag for specified block number and address.
|
||||
pub fn set_wiped(&mut self, block: BlockNumber, address: Address) {
|
||||
self.inner.entry(block).or_default().entry(address).or_default().wiped = true;
|
||||
}
|
||||
|
||||
/// Insert storage entries for specified block number and address.
|
||||
pub fn insert_for_block_and_address<I>(
|
||||
&mut self,
|
||||
block: BlockNumber,
|
||||
address: Address,
|
||||
wipe: StorageWipe,
|
||||
storage: I,
|
||||
) where
|
||||
I: Iterator<Item = (U256, U256)>,
|
||||
{
|
||||
let block_entry = self.inner.entry(block).or_default();
|
||||
let storage_entry = block_entry.entry(address).or_default();
|
||||
if wipe.is_wiped() {
|
||||
storage_entry.wipe = wipe;
|
||||
}
|
||||
for (slot, value) in storage {
|
||||
if let Entry::Vacant(entry) = storage_entry.storage.entry(slot) {
|
||||
entry.insert(value);
|
||||
@ -82,7 +104,7 @@ impl StorageChanges {
|
||||
pub fn drain_above(
|
||||
&mut self,
|
||||
target_block: BlockNumber,
|
||||
) -> BTreeMap<BlockNumber, BTreeMap<Address, ChangedStorage>> {
|
||||
) -> BTreeMap<BlockNumber, BTreeMap<Address, StorageTransition>> {
|
||||
let mut evicted = BTreeMap::new();
|
||||
self.inner.retain(|block_number, storages| {
|
||||
if *block_number > target_block {
|
||||
@ -100,8 +122,28 @@ impl StorageChanges {
|
||||
|
||||
/// Retain entries only above specified block number.
|
||||
pub fn retain_above(&mut self, target_block: BlockNumber) {
|
||||
let mut observed_storage_wipes: HashSet<Address> = HashSet::default();
|
||||
self.inner.retain(|block_number, storages| {
|
||||
if *block_number > target_block {
|
||||
for (address, storage) in storages.iter_mut() {
|
||||
storage.wipe = match storage.wipe {
|
||||
StorageWipe::Primary => {
|
||||
observed_storage_wipes.insert(*address);
|
||||
StorageWipe::Primary
|
||||
}
|
||||
StorageWipe::Secondary => {
|
||||
if observed_storage_wipes.contains(address) {
|
||||
// We already observed the storage wipe for this address
|
||||
StorageWipe::Secondary
|
||||
} else {
|
||||
// No wipe was observed, promote the secondary wipe to primary
|
||||
observed_storage_wipes.insert(*address);
|
||||
StorageWipe::Primary
|
||||
}
|
||||
}
|
||||
StorageWipe::None => StorageWipe::None, // nothing to do
|
||||
};
|
||||
}
|
||||
true
|
||||
} else {
|
||||
// This is fine, because it's called only on post state splits
|
||||
|
||||
Reference in New Issue
Block a user