Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,14 @@ impl<N: ProviderNodeTypes> ChangeSetReader for BlockchainProvider<N> {
) -> ProviderResult<Vec<AccountBeforeTx>> {
self.consistent_provider()?.account_block_changeset(block_number)
}

fn get_account_before_block(
&self,
block_number: BlockNumber,
address: Address,
) -> ProviderResult<Option<AccountBeforeTx>> {
self.consistent_provider()?.get_account_before_block(block_number, address)
}
}

impl<N: ProviderNodeTypes> AccountReader for BlockchainProvider<N> {
Expand Down
46 changes: 46 additions & 0 deletions crates/storage/provider/src/providers/consistent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,52 @@ impl<N: ProviderNodeTypes> ChangeSetReader for ConsistentProvider<N> {
self.storage_provider.account_block_changeset(block_number)
}
}

fn get_account_before_block(
&self,
block_number: BlockNumber,
address: Address,
) -> ProviderResult<Option<AccountBeforeTx>> {
if let Some(state) =
self.head_block.as_ref().and_then(|b| b.block_on_chain(block_number.into()))
{
// Search in-memory state for the account changeset
let changeset = state
.block_ref()
.execution_output
.bundle
.reverts
.clone()
.to_plain_state_reverts()
.accounts
.into_iter()
.flatten()
.find(|(addr, _)| addr == &address)
.map(|(address, info)| AccountBeforeTx { address, info: info.map(Into::into) });
Ok(changeset)
} else {
// Perform checks on whether or not changesets exist for the block.
// No prune checkpoint means history should exist and we should `unwrap_or(true)`
let account_history_exists = self
.storage_provider
.get_prune_checkpoint(PruneSegment::AccountHistory)?
.and_then(|checkpoint| {
// return true if the block number is ahead of the prune checkpoint.
//
// The checkpoint stores the highest pruned block number, so we should make
// sure the block_number is strictly greater.
checkpoint.block_number.map(|checkpoint| block_number > checkpoint)
})
.unwrap_or(true);

if !account_history_exists {
return Err(ProviderError::StateAtBlockPruned(block_number))
}

// Delegate to the storage provider for database lookups
self.storage_provider.get_account_before_block(block_number, address)
}
}
}

impl<N: ProviderNodeTypes> AccountReader for ConsistentProvider<N> {
Expand Down
13 changes: 13 additions & 0 deletions crates/storage/provider/src/providers/database/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,19 @@ impl<TX: DbTx, N: NodeTypes> ChangeSetReader for DatabaseProvider<TX, N> {
})
.collect()
}

fn get_account_before_block(
&self,
block_number: BlockNumber,
address: Address,
) -> ProviderResult<Option<AccountBeforeTx>> {
self.tx
.cursor_dup_read::<tables::AccountChangeSets>()?
.seek_by_key_subkey(block_number, address)?
.filter(|acc| acc.address == address)
.map(Ok)
.transpose()
}
}

impl<TX: DbTx + 'static, N: NodeTypesForProvider> HeaderSyncGapProvider
Expand Down
36 changes: 20 additions & 16 deletions crates/storage/provider/src/providers/state/historical.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader,
HashedPostStateProvider, ProviderError, StateProvider, StateRootProvider,
ChangeSetReader, HashedPostStateProvider, ProviderError, StateProvider, StateRootProvider,
};
use alloy_eips::merge::EPOCH_SLOTS;
use alloy_primitives::{Address, BlockNumber, Bytes, StorageKey, StorageValue, B256};
Expand Down Expand Up @@ -241,23 +241,23 @@ impl<Provider: DBProvider + BlockNumReader> HistoricalStateProviderRef<'_, Provi
}
}

impl<Provider: DBProvider + BlockNumReader> AccountReader
impl<Provider: DBProvider + BlockNumReader + ChangeSetReader> AccountReader
for HistoricalStateProviderRef<'_, Provider>
{
/// Get basic account information.
fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
match self.account_history_lookup(*address)? {
HistoryInfo::NotYetWritten => Ok(None),
HistoryInfo::InChangeset(changeset_block_number) => Ok(self
.tx()
.cursor_dup_read::<tables::AccountChangeSets>()?
.seek_by_key_subkey(changeset_block_number, *address)?
.filter(|acc| &acc.address == address)
.ok_or(ProviderError::AccountChangesetNotFound {
block_number: changeset_block_number,
address: *address,
})?
.info),
HistoryInfo::InChangeset(changeset_block_number) => {
// Use ChangeSetReader trait method to get the account from changesets
self.provider
.get_account_before_block(changeset_block_number, *address)?
.ok_or(ProviderError::AccountChangesetNotFound {
block_number: changeset_block_number,
address: *address,
})
.map(|account_before| account_before.info)
}
HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
Ok(self.tx().get_by_encoded_key::<tables::PlainAccountState>(address)?)
}
Expand Down Expand Up @@ -394,7 +394,7 @@ impl<Provider: Sync> HashedPostStateProvider for HistoricalStateProviderRef<'_,
}
}

impl<Provider: DBProvider + BlockNumReader + BlockHashReader> StateProvider
impl<Provider: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader> StateProvider
for HistoricalStateProviderRef<'_, Provider>
{
/// Get storage.
Expand Down Expand Up @@ -485,7 +485,7 @@ impl<Provider: DBProvider + BlockNumReader> HistoricalStateProvider<Provider> {
}

// Delegates all provider impls to [HistoricalStateProviderRef]
delegate_provider_impls!(HistoricalStateProvider<Provider> where [Provider: DBProvider + BlockNumReader + BlockHashReader ]);
delegate_provider_impls!(HistoricalStateProvider<Provider> where [Provider: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader]);

/// Lowest blocks at which different parts of the state are available.
/// They may be [Some] if pruning is enabled.
Expand Down Expand Up @@ -530,7 +530,9 @@ mod tests {
BlockNumberList,
};
use reth_primitives_traits::{Account, StorageEntry};
use reth_storage_api::{BlockHashReader, BlockNumReader, DBProvider, DatabaseProviderFactory};
use reth_storage_api::{
BlockHashReader, BlockNumReader, ChangeSetReader, DBProvider, DatabaseProviderFactory,
};
use reth_storage_errors::provider::ProviderError;

const ADDRESS: Address = address!("0x0000000000000000000000000000000000000001");
Expand All @@ -540,7 +542,9 @@ mod tests {

const fn assert_state_provider<T: StateProvider>() {}
#[expect(dead_code)]
const fn assert_historical_state_provider<T: DBProvider + BlockNumReader + BlockHashReader>() {
const fn assert_historical_state_provider<
T: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader,
>() {
assert_state_provider::<HistoricalStateProvider<T>>();
}

Expand Down
8 changes: 8 additions & 0 deletions crates/storage/provider/src/test_utils/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,14 @@ impl<T: NodePrimitives, ChainSpec: Send + Sync> ChangeSetReader for MockEthProvi
) -> ProviderResult<Vec<AccountBeforeTx>> {
Ok(Vec::default())
}

fn get_account_before_block(
&self,
_block_number: BlockNumber,
_address: Address,
) -> ProviderResult<Option<AccountBeforeTx>> {
Ok(None)
}
}

impl<T: NodePrimitives, ChainSpec: Send + Sync> StateReader for MockEthProvider<T, ChainSpec> {
Expand Down
8 changes: 8 additions & 0 deletions crates/storage/rpc-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1764,6 +1764,14 @@ where
) -> Result<Vec<reth_db_api::models::AccountBeforeTx>, ProviderError> {
Err(ProviderError::UnsupportedProvider)
}

fn get_account_before_block(
&self,
_block_number: BlockNumber,
_address: Address,
) -> ProviderResult<Option<reth_db_api::models::AccountBeforeTx>> {
Err(ProviderError::UnsupportedProvider)
}
}

impl<P, Node, N> StateProviderFactory for RpcBlockchainStateProvider<P, Node, N>
Expand Down
9 changes: 9 additions & 0 deletions crates/storage/storage-api/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,13 @@ pub trait ChangeSetReader {
&self,
block_number: BlockNumber,
) -> ProviderResult<Vec<AccountBeforeTx>>;

/// Search the block's changesets for the given address, and return the result.
///
/// Returns `None` if the account was not changed in this block.
fn get_account_before_block(
&self,
block_number: BlockNumber,
address: Address,
) -> ProviderResult<Option<AccountBeforeTx>>;
}
8 changes: 8 additions & 0 deletions crates/storage/storage-api/src/noop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,14 @@ impl<C: Send + Sync, N: NodePrimitives> ChangeSetReader for NoopProvider<C, N> {
) -> ProviderResult<Vec<AccountBeforeTx>> {
Ok(Vec::default())
}

fn get_account_before_block(
&self,
_block_number: BlockNumber,
_address: Address,
) -> ProviderResult<Option<AccountBeforeTx>> {
Ok(None)
}
}

impl<C: Send + Sync, N: NodePrimitives> StateRootProvider for NoopProvider<C, N> {
Expand Down
Loading