mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-10 15:58:27 -05:00
feat(provider): add get_account_before_block to ChangesetReader (#18898)
This commit is contained in:
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
@@ -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)?)
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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");
|
||||
@@ -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>>();
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>>;
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user