feat(rpc): enable historical proofs (#9273)

This commit is contained in:
Roman Krasiuk
2024-07-03 13:43:26 -07:00
committed by GitHub
parent f3fd7e73cc
commit a7caf0d284
11 changed files with 87 additions and 37 deletions

View File

@ -313,6 +313,11 @@ RPC:
[default: 50000000]
--rpc.eth-proof-window <RPC_ETH_PROOF_WINDOW>
The maximum proof window for historical proof generation. This value allows for generating historical proofs up to configured number of blocks from current tip (up to `tip - window`)
[default: 0]
RPC State Cache:
--rpc-cache.max-blocks <MAX_BLOCKS>
Max number of blocks in cache

View File

@ -156,6 +156,16 @@ pub struct RpcServerArgs {
)]
pub rpc_gas_cap: u64,
/// The maximum proof window for historical proof generation.
/// This value allows for generating historical proofs up to
/// configured number of blocks from current tip (up to `tip - window`).
#[arg(
long = "rpc.eth-proof-window",
default_value_t = constants::DEFAULT_ETH_PROOF_WINDOW,
value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
)]
pub rpc_eth_proof_window: u64,
/// State cache configuration.
#[command(flatten)]
pub rpc_state_cache: RpcStateCacheArgs,
@ -286,6 +296,7 @@ impl Default for RpcServerArgs {
rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
gas_price_oracle: GasPriceOracleArgs::default(),
rpc_state_cache: RpcStateCacheArgs::default(),
}

View File

@ -91,6 +91,7 @@ impl RethRpcServerConfig for RpcServerArgs {
.max_tracing_requests(self.rpc_max_tracing_requests)
.max_blocks_per_filter(self.rpc_max_blocks_per_filter.unwrap_or_max())
.max_logs_per_response(self.rpc_max_logs_per_response.unwrap_or_max() as usize)
.eth_proof_window(self.rpc_eth_proof_window)
.rpc_gas_cap(self.rpc_gas_cap)
.state_cache(self.state_cache_config())
.gpo_config(self.gas_price_oracle_config())

View File

@ -13,7 +13,8 @@ use reth_rpc_eth_types::{
GasPriceOracleConfig, RPC_DEFAULT_GAS_CAP,
};
use reth_rpc_server_types::constants::{
default_max_tracing_requests, DEFAULT_MAX_BLOCKS_PER_FILTER, DEFAULT_MAX_LOGS_PER_RESPONSE,
default_max_tracing_requests, DEFAULT_ETH_PROOF_WINDOW, DEFAULT_MAX_BLOCKS_PER_FILTER,
DEFAULT_MAX_LOGS_PER_RESPONSE,
};
use reth_tasks::{pool::BlockingTaskPool, TaskSpawner};
use reth_transaction_pool::TransactionPool;
@ -141,6 +142,8 @@ pub struct EthConfig {
pub cache: EthStateCacheConfig,
/// Settings for the gas price oracle
pub gas_oracle: GasPriceOracleConfig,
/// The maximum number of blocks into the past for generating state proofs.
pub eth_proof_window: u64,
/// The maximum number of tracing calls that can be executed in concurrently.
pub max_tracing_requests: usize,
/// Maximum number of blocks that could be scanned per filter request in `eth_getLogs` calls.
@ -173,6 +176,7 @@ impl Default for EthConfig {
Self {
cache: EthStateCacheConfig::default(),
gas_oracle: GasPriceOracleConfig::default(),
eth_proof_window: DEFAULT_ETH_PROOF_WINDOW,
max_tracing_requests: default_max_tracing_requests(),
max_blocks_per_filter: DEFAULT_MAX_BLOCKS_PER_FILTER,
max_logs_per_response: DEFAULT_MAX_LOGS_PER_RESPONSE,
@ -219,6 +223,12 @@ impl EthConfig {
self.rpc_gas_cap = rpc_gas_cap;
self
}
/// Configures the maximum proof window for historical proof generation.
pub const fn eth_proof_window(mut self, window: u64) -> Self {
self.eth_proof_window = window;
self
}
}
/// Context for building the `eth` namespace API.
@ -269,6 +279,7 @@ impl EthApiBuild {
ctx.cache.clone(),
gas_oracle,
ctx.config.rpc_gas_cap,
ctx.config.eth_proof_window,
Box::new(ctx.executor.clone()),
BlockingTaskPool::build().expect("failed to build blocking task pool"),
fee_history_cache,

View File

@ -4,7 +4,6 @@
use alloy_dyn_abi::TypedData;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use reth_primitives::{Address, BlockId, BlockNumberOrTag, Bytes, B256, B64, U256, U64};
use reth_rpc_eth_types::EthApiError;
use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
use reth_rpc_types::{
serde_helpers::JsonStorageKey,
@ -715,13 +714,6 @@ where
block_number: Option<BlockId>,
) -> RpcResult<EIP1186AccountProofResponse> {
trace!(target: "rpc::eth", ?address, ?keys, ?block_number, "Serving eth_getProof");
let res = EthState::get_proof(self, address, keys, block_number)?.await;
Ok(res.map_err(|e| match e {
EthApiError::InvalidBlockRange => {
internal_rpc_err("eth_getProof is unimplemented for historical blocks")
}
_ => e.into(),
})?)
Ok(EthState::get_proof(self, address, keys, block_number)?.await?)
}
}

View File

@ -3,7 +3,7 @@
use futures::Future;
use reth_evm::ConfigureEvmEnv;
use reth_primitives::{Address, BlockId, BlockNumberOrTag, Bytes, Header, B256, U256};
use reth_primitives::{Address, BlockId, Bytes, Header, B256, U256};
use reth_provider::{
BlockIdReader, ChainSpecProvider, StateProvider, StateProviderBox, StateProviderFactory,
};
@ -19,6 +19,9 @@ use super::{EthApiSpec, LoadPendingBlock, SpawnBlocking};
/// Helper methods for `eth_` methods relating to state (accounts).
pub trait EthState: LoadState + SpawnBlocking {
/// Returns the maximum number of blocks into the past for generating state proofs.
fn max_proof_window(&self) -> u64;
/// Returns the number of transactions sent from an address at the given block identifier.
///
/// If this is [`BlockNumberOrTag::Pending`](reth_primitives::BlockNumberOrTag) then this will
@ -90,19 +93,14 @@ pub trait EthState: LoadState + SpawnBlocking {
let chain_info = self.chain_info()?;
let block_id = block_id.unwrap_or_default();
// if we are trying to create a proof for the latest block, but have a BlockId as input
// that is not BlockNumberOrTag::Latest, then we need to figure out whether or not the
// BlockId corresponds to the latest block
let is_latest_block = match block_id {
BlockId::Number(BlockNumberOrTag::Number(num)) => num == chain_info.best_number,
BlockId::Hash(hash) => hash == chain_info.best_hash.into(),
BlockId::Number(BlockNumberOrTag::Latest) => true,
_ => false,
};
// TODO: remove when HistoricalStateProviderRef::proof is implemented
if !is_latest_block {
return Err(EthApiError::InvalidBlockRange)
// Check whether the distance to the block exceeds the maximum configured window.
let block_number = self
.provider()
.block_number_for_id(block_id)?
.ok_or(EthApiError::UnknownBlockNumber)?;
let max_window = self.max_proof_window();
if chain_info.best_number.saturating_sub(block_number) > max_window {
return Err(EthApiError::ExceedsMaxProofWindow)
}
Ok(self.spawn_tracing(move |this| {

View File

@ -54,6 +54,9 @@ pub enum EthApiError {
/// When an invalid block range is provided
#[error("invalid block range")]
InvalidBlockRange,
/// Thrown when the target block for proof computation exceeds the maximum configured window.
#[error("distance to target block exceeds maximum proof window")]
ExceedsMaxProofWindow,
/// An internal error where prevrandao is not set in the evm's environment
#[error("prevrandao not in the EVM's environment after merge")]
PrevrandaoNotSet,
@ -143,6 +146,7 @@ impl From<EthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
EthApiError::InvalidTransactionSignature |
EthApiError::EmptyRawTransactionData |
EthApiError::InvalidBlockRange |
EthApiError::ExceedsMaxProofWindow |
EthApiError::ConflictingFeeFieldsInRequest |
EthApiError::Signing(_) |
EthApiError::BothStateAndStateDiffInOverride(_) |

View File

@ -42,6 +42,12 @@ pub const DEFAULT_ENGINE_API_IPC_ENDPOINT: &str = r"\\.\pipe\reth_engine_api.ipc
#[cfg(not(windows))]
pub const DEFAULT_ENGINE_API_IPC_ENDPOINT: &str = "/tmp/reth_engine_api.ipc";
/// The default eth historical proof window.
pub const DEFAULT_ETH_PROOF_WINDOW: u64 = 0;
/// Maximum eth historical proof window. Equivalent to roughly one month of data.
pub const MAX_ETH_PROOF_WINDOW: u64 = 216_000;
/// GPO specific constants
pub mod gas_oracle {
use alloy_primitives::U256;

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use derive_more::Deref;
use reth_primitives::{BlockNumberOrTag, U256};
use reth_provider::{BlockReaderIdExt, ChainSpecProvider};
use reth_provider::BlockReaderIdExt;
use reth_rpc_eth_api::{
helpers::{transaction::UpdateRawTxForwarder, EthSigner, SpawnBlocking},
RawTransactionForwarder,
@ -31,18 +31,9 @@ pub struct EthApi<Provider, Pool, Network, EvmConfig> {
pub(super) inner: Arc<EthApiInner<Provider, Pool, Network, EvmConfig>>,
}
impl<Provider, Pool, Network, EvmConfig> EthApi<Provider, Pool, Network, EvmConfig> {
/// Sets a forwarder for `eth_sendRawTransaction`
///
/// Note: this might be removed in the future in favor of a more generic approach.
pub fn set_eth_raw_transaction_forwarder(&self, forwarder: Arc<dyn RawTransactionForwarder>) {
self.inner.raw_transaction_forwarder.write().replace(forwarder);
}
}
impl<Provider, Pool, Network, EvmConfig> EthApi<Provider, Pool, Network, EvmConfig>
where
Provider: BlockReaderIdExt + ChainSpecProvider,
Provider: BlockReaderIdExt,
{
/// Creates a new, shareable instance using the default tokio task spawner.
#[allow(clippy::too_many_arguments)]
@ -53,6 +44,7 @@ where
eth_cache: EthStateCache,
gas_oracle: GasPriceOracle<Provider>,
gas_cap: impl Into<GasCap>,
eth_proof_window: u64,
blocking_task_pool: BlockingTaskPool,
fee_history_cache: FeeHistoryCache,
evm_config: EvmConfig,
@ -65,6 +57,7 @@ where
eth_cache,
gas_oracle,
gas_cap.into().into(),
eth_proof_window,
Box::<TokioTaskExecutor>::default(),
blocking_task_pool,
fee_history_cache,
@ -82,6 +75,7 @@ where
eth_cache: EthStateCache,
gas_oracle: GasPriceOracle<Provider>,
gas_cap: u64,
eth_proof_window: u64,
task_spawner: Box<dyn TaskSpawner>,
blocking_task_pool: BlockingTaskPool,
fee_history_cache: FeeHistoryCache,
@ -104,6 +98,7 @@ where
eth_cache,
gas_oracle,
gas_cap,
eth_proof_window,
starting_block: U256::from(latest_block),
task_spawner,
pending_block: Default::default(),
@ -115,6 +110,15 @@ where
Self { inner: Arc::new(inner) }
}
}
impl<Provider, Pool, Network, EvmConfig> EthApi<Provider, Pool, Network, EvmConfig> {
/// Sets a forwarder for `eth_sendRawTransaction`
///
/// Note: this might be removed in the future in favor of a more generic approach.
pub fn set_eth_raw_transaction_forwarder(&self, forwarder: Arc<dyn RawTransactionForwarder>) {
self.inner.raw_transaction_forwarder.write().replace(forwarder);
}
/// Returns the state cache frontend
pub fn cache(&self) -> &EthStateCache {
@ -131,6 +135,11 @@ where
self.inner.gas_cap
}
/// The maximum number of blocks into the past for generating state proofs.
pub fn eth_proof_window(&self) -> u64 {
self.inner.eth_proof_window
}
/// Returns the inner `Provider`
pub fn provider(&self) -> &Provider {
&self.inner.provider
@ -208,6 +217,8 @@ pub struct EthApiInner<Provider, Pool, Network, EvmConfig> {
gas_oracle: GasPriceOracle<Provider>,
/// Maximum gas limit for `eth_call` and call tracing RPC methods.
gas_cap: u64,
/// The maximum number of blocks into the past for generating state proofs.
eth_proof_window: u64,
/// The block number at which the node started
starting_block: U256,
/// The type that can spawn tasks which would otherwise block.
@ -330,6 +341,7 @@ mod tests {
use reth_rpc_eth_types::{
EthStateCache, FeeHistoryCache, FeeHistoryCacheConfig, GasPriceOracle,
};
use reth_rpc_server_types::constants::DEFAULT_ETH_PROOF_WINDOW;
use reth_rpc_types::FeeHistory;
use reth_tasks::pool::BlockingTaskPool;
use reth_testing_utils::{generators, generators::Rng};
@ -361,6 +373,7 @@ mod tests {
cache.clone(),
GasPriceOracle::new(provider, Default::default(), cache),
ETHEREUM_BLOCK_GAS_LIMIT,
DEFAULT_ETH_PROOF_WINDOW,
BlockingTaskPool::build().expect("failed to build tracing pool"),
fee_history_cache,
evm_config,

View File

@ -8,9 +8,13 @@ use reth_rpc_eth_types::EthStateCache;
use crate::EthApi;
impl<Provider, Pool, Network, EvmConfig> EthState for EthApi<Provider, Pool, Network, EvmConfig> where
Self: LoadState + SpawnBlocking
impl<Provider, Pool, Network, EvmConfig> EthState for EthApi<Provider, Pool, Network, EvmConfig>
where
Self: LoadState + SpawnBlocking,
{
fn max_proof_window(&self) -> u64 {
self.eth_proof_window()
}
}
impl<Provider, Pool, Network, EvmConfig> LoadState for EthApi<Provider, Pool, Network, EvmConfig>
@ -47,6 +51,7 @@ mod tests {
use reth_rpc_eth_types::{
EthStateCache, FeeHistoryCache, FeeHistoryCacheConfig, GasPriceOracle,
};
use reth_rpc_server_types::constants::DEFAULT_ETH_PROOF_WINDOW;
use reth_tasks::pool::BlockingTaskPool;
use reth_transaction_pool::test_utils::testing_pool;
@ -66,6 +71,7 @@ mod tests {
cache.clone(),
GasPriceOracle::new(NoopProvider::default(), Default::default(), cache.clone()),
ETHEREUM_BLOCK_GAS_LIMIT,
DEFAULT_ETH_PROOF_WINDOW,
BlockingTaskPool::build().expect("failed to build tracing pool"),
FeeHistoryCache::new(cache, FeeHistoryCacheConfig::default()),
evm_config,
@ -91,6 +97,7 @@ mod tests {
cache.clone(),
GasPriceOracle::new(mock_provider, Default::default(), cache.clone()),
ETHEREUM_BLOCK_GAS_LIMIT,
DEFAULT_ETH_PROOF_WINDOW,
BlockingTaskPool::build().expect("failed to build tracing pool"),
FeeHistoryCache::new(cache, FeeHistoryCacheConfig::default()),
evm_config,

View File

@ -68,6 +68,7 @@ mod tests {
use reth_rpc_eth_types::{
EthStateCache, FeeHistoryCache, FeeHistoryCacheConfig, GasPriceOracle,
};
use reth_rpc_server_types::constants::DEFAULT_ETH_PROOF_WINDOW;
use reth_tasks::pool::BlockingTaskPool;
use reth_transaction_pool::{test_utils::testing_pool, TransactionPool};
@ -91,6 +92,7 @@ mod tests {
cache.clone(),
GasPriceOracle::new(noop_provider, Default::default(), cache.clone()),
ETHEREUM_BLOCK_GAS_LIMIT,
DEFAULT_ETH_PROOF_WINDOW,
BlockingTaskPool::build().expect("failed to build tracing pool"),
fee_history_cache,
evm_config,