mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): enable historical proofs (#9273)
This commit is contained in:
5
book/cli/reth/node.md
vendored
5
book/cli/reth/node.md
vendored
@ -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
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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| {
|
||||
|
||||
@ -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(_) |
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user