mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): create revm env on demand (#13017)
Co-authored-by: dkathiriya <lakshya-sky@users.noreply.github.com> Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
committed by
GitHub
parent
3dc6f506b0
commit
890f082453
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -9028,7 +9028,6 @@ dependencies = [
|
|||||||
"reth-chain-state",
|
"reth-chain-state",
|
||||||
"reth-chainspec",
|
"reth-chainspec",
|
||||||
"reth-errors",
|
"reth-errors",
|
||||||
"reth-evm",
|
|
||||||
"reth-execution-types",
|
"reth-execution-types",
|
||||||
"reth-metrics",
|
"reth-metrics",
|
||||||
"reth-primitives",
|
"reth-primitives",
|
||||||
|
|||||||
4
book/cli/reth/node.md
vendored
4
book/cli/reth/node.md
vendored
@ -381,8 +381,8 @@ RPC State Cache:
|
|||||||
|
|
||||||
[default: 2000]
|
[default: 2000]
|
||||||
|
|
||||||
--rpc-cache.max-envs <MAX_ENVS>
|
--rpc-cache.max-envs <MAX_HEADERS>
|
||||||
Max number of bytes for cached env data
|
Max number of headers in cache
|
||||||
|
|
||||||
[default: 1000]
|
[default: 1000]
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use clap::Args;
|
use clap::Args;
|
||||||
use reth_rpc_server_types::constants::cache::{
|
use reth_rpc_server_types::constants::cache::{
|
||||||
DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_CONCURRENT_DB_REQUESTS, DEFAULT_ENV_CACHE_MAX_LEN,
|
DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_CONCURRENT_DB_REQUESTS, DEFAULT_HEADER_CACHE_MAX_LEN,
|
||||||
DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,12 +22,12 @@ pub struct RpcStateCacheArgs {
|
|||||||
)]
|
)]
|
||||||
pub max_receipts: u32,
|
pub max_receipts: u32,
|
||||||
|
|
||||||
/// Max number of bytes for cached env data.
|
/// Max number of headers in cache.
|
||||||
#[arg(
|
#[arg(
|
||||||
long = "rpc-cache.max-envs",
|
long = "rpc-cache.max-envs",
|
||||||
default_value_t = DEFAULT_ENV_CACHE_MAX_LEN,
|
default_value_t = DEFAULT_HEADER_CACHE_MAX_LEN,
|
||||||
)]
|
)]
|
||||||
pub max_envs: u32,
|
pub max_headers: u32,
|
||||||
|
|
||||||
/// Max number of concurrent database requests.
|
/// Max number of concurrent database requests.
|
||||||
#[arg(
|
#[arg(
|
||||||
@ -42,7 +42,7 @@ impl Default for RpcStateCacheArgs {
|
|||||||
Self {
|
Self {
|
||||||
max_blocks: DEFAULT_BLOCK_CACHE_MAX_LEN,
|
max_blocks: DEFAULT_BLOCK_CACHE_MAX_LEN,
|
||||||
max_receipts: DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
max_receipts: DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
||||||
max_envs: DEFAULT_ENV_CACHE_MAX_LEN,
|
max_headers: DEFAULT_HEADER_CACHE_MAX_LEN,
|
||||||
max_concurrent_db_requests: DEFAULT_CONCURRENT_DB_REQUESTS,
|
max_concurrent_db_requests: DEFAULT_CONCURRENT_DB_REQUESTS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,7 +113,7 @@ impl RethRpcServerConfig for RpcServerArgs {
|
|||||||
EthStateCacheConfig {
|
EthStateCacheConfig {
|
||||||
max_blocks: self.rpc_state_cache.max_blocks,
|
max_blocks: self.rpc_state_cache.max_blocks,
|
||||||
max_receipts: self.rpc_state_cache.max_receipts,
|
max_receipts: self.rpc_state_cache.max_receipts,
|
||||||
max_envs: self.rpc_state_cache.max_envs,
|
max_headers: self.rpc_state_cache.max_headers,
|
||||||
max_concurrent_db_requests: self.rpc_state_cache.max_concurrent_db_requests,
|
max_concurrent_db_requests: self.rpc_state_cache.max_concurrent_db_requests,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,12 +65,7 @@ where
|
|||||||
EvmConfig: ConfigureEvm<Header = Header>,
|
EvmConfig: ConfigureEvm<Header = Header>,
|
||||||
Tasks: TaskSpawner + Clone + 'static,
|
Tasks: TaskSpawner + Clone + 'static,
|
||||||
{
|
{
|
||||||
let cache = EthStateCache::spawn_with(
|
let cache = EthStateCache::spawn_with(provider.clone(), config.cache, executor.clone());
|
||||||
provider.clone(),
|
|
||||||
config.cache,
|
|
||||||
executor.clone(),
|
|
||||||
evm_config.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_canonical_blocks = events.canonical_state_stream();
|
let new_canonical_blocks = events.canonical_state_stream();
|
||||||
let c = cache.clone();
|
let c = cache.clone();
|
||||||
|
|||||||
@ -259,7 +259,7 @@ where
|
|||||||
Some(block_number.into()),
|
Some(block_number.into()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap_err();
|
||||||
EthApiClient::<Transaction, Block, Receipt>::estimate_gas(
|
EthApiClient::<Transaction, Block, Receipt>::estimate_gas(
|
||||||
client,
|
client,
|
||||||
call_request.clone(),
|
call_request.clone(),
|
||||||
@ -267,7 +267,7 @@ where
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap_err();
|
||||||
EthApiClient::<Transaction, Block, Receipt>::call(
|
EthApiClient::<Transaction, Block, Receipt>::call(
|
||||||
client,
|
client,
|
||||||
call_request.clone(),
|
call_request.clone(),
|
||||||
@ -276,7 +276,7 @@ where
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap_err();
|
||||||
EthApiClient::<Transaction, Block, Receipt>::syncing(client).await.unwrap();
|
EthApiClient::<Transaction, Block, Receipt>::syncing(client).await.unwrap();
|
||||||
EthApiClient::<Transaction, Block, Receipt>::send_transaction(
|
EthApiClient::<Transaction, Block, Receipt>::send_transaction(
|
||||||
client,
|
client,
|
||||||
@ -368,13 +368,15 @@ where
|
|||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
TraceApiClient::trace_call_many(client, vec![], Some(BlockNumberOrTag::Latest.into()))
|
TraceApiClient::trace_call_many(client, vec![], Some(BlockNumberOrTag::Latest.into()))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap_err();
|
||||||
TraceApiClient::replay_transaction(client, B256::default(), HashSet::default())
|
TraceApiClient::replay_transaction(client, B256::default(), HashSet::default())
|
||||||
.await
|
.await
|
||||||
.err()
|
.err()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
TraceApiClient::trace_block(client, block_id).await.unwrap();
|
TraceApiClient::trace_block(client, block_id).await.unwrap_err();
|
||||||
TraceApiClient::replay_block_transactions(client, block_id, HashSet::default()).await.unwrap();
|
TraceApiClient::replay_block_transactions(client, block_id, HashSet::default())
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
|
||||||
TraceApiClient::trace_filter(client, trace_filter).await.unwrap();
|
TraceApiClient::trace_filter(client, trace_filter).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,8 +12,8 @@ use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
|||||||
use reth_errors::RethError;
|
use reth_errors::RethError;
|
||||||
use reth_evm::ConfigureEvmEnv;
|
use reth_evm::ConfigureEvmEnv;
|
||||||
use reth_provider::{
|
use reth_provider::{
|
||||||
BlockIdReader, BlockNumReader, ChainSpecProvider, StateProvider, StateProviderBox,
|
BlockIdReader, BlockNumReader, ChainSpecProvider, EvmEnvProvider as _, StateProvider,
|
||||||
StateProviderFactory,
|
StateProviderBox, StateProviderFactory,
|
||||||
};
|
};
|
||||||
use reth_rpc_eth_types::{EthApiError, PendingBlockEnv, RpcInvalidTransactionError};
|
use reth_rpc_eth_types::{EthApiError, PendingBlockEnv, RpcInvalidTransactionError};
|
||||||
use reth_transaction_pool::TransactionPool;
|
use reth_transaction_pool::TransactionPool;
|
||||||
@ -229,12 +229,15 @@ pub trait LoadState:
|
|||||||
.block_hash_for_id(at)
|
.block_hash_for_id(at)
|
||||||
.map_err(Self::Error::from_eth_err)?
|
.map_err(Self::Error::from_eth_err)?
|
||||||
.ok_or(EthApiError::HeaderNotFound(at))?;
|
.ok_or(EthApiError::HeaderNotFound(at))?;
|
||||||
let (cfg, env) = self
|
|
||||||
.cache()
|
let header =
|
||||||
.get_evm_env(block_hash)
|
self.cache().get_header(block_hash).await.map_err(Self::Error::from_eth_err)?;
|
||||||
.await
|
let evm_config = self.evm_config().clone();
|
||||||
|
let (cfg, block_env) = self
|
||||||
|
.provider()
|
||||||
|
.env_with_header(&header, evm_config)
|
||||||
.map_err(Self::Error::from_eth_err)?;
|
.map_err(Self::Error::from_eth_err)?;
|
||||||
Ok((cfg, env, block_hash.into()))
|
Ok((cfg, block_env, block_hash.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,6 @@ workspace = true
|
|||||||
reth-chainspec.workspace = true
|
reth-chainspec.workspace = true
|
||||||
reth-chain-state.workspace = true
|
reth-chain-state.workspace = true
|
||||||
reth-errors.workspace = true
|
reth-errors.workspace = true
|
||||||
reth-evm.workspace = true
|
|
||||||
reth-execution-types.workspace = true
|
reth-execution-types.workspace = true
|
||||||
reth-metrics.workspace = true
|
reth-metrics.workspace = true
|
||||||
reth-primitives = { workspace = true, features = ["secp256k1"] }
|
reth-primitives = { workspace = true, features = ["secp256k1"] }
|
||||||
|
|||||||
8
crates/rpc/rpc-eth-types/src/cache/config.rs
vendored
8
crates/rpc/rpc-eth-types/src/cache/config.rs
vendored
@ -3,7 +3,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use reth_rpc_server_types::constants::cache::{
|
use reth_rpc_server_types::constants::cache::{
|
||||||
DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_CONCURRENT_DB_REQUESTS, DEFAULT_ENV_CACHE_MAX_LEN,
|
DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_CONCURRENT_DB_REQUESTS, DEFAULT_HEADER_CACHE_MAX_LEN,
|
||||||
DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,10 +19,10 @@ pub struct EthStateCacheConfig {
|
|||||||
///
|
///
|
||||||
/// Default is 2000.
|
/// Default is 2000.
|
||||||
pub max_receipts: u32,
|
pub max_receipts: u32,
|
||||||
/// Max number of bytes for cached env data.
|
/// Max number of headers in cache.
|
||||||
///
|
///
|
||||||
/// Default is 1000.
|
/// Default is 1000.
|
||||||
pub max_envs: u32,
|
pub max_headers: u32,
|
||||||
/// Max number of concurrent database requests.
|
/// Max number of concurrent database requests.
|
||||||
///
|
///
|
||||||
/// Default is 512.
|
/// Default is 512.
|
||||||
@ -34,7 +34,7 @@ impl Default for EthStateCacheConfig {
|
|||||||
Self {
|
Self {
|
||||||
max_blocks: DEFAULT_BLOCK_CACHE_MAX_LEN,
|
max_blocks: DEFAULT_BLOCK_CACHE_MAX_LEN,
|
||||||
max_receipts: DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
max_receipts: DEFAULT_RECEIPT_CACHE_MAX_LEN,
|
||||||
max_envs: DEFAULT_ENV_CACHE_MAX_LEN,
|
max_headers: DEFAULT_HEADER_CACHE_MAX_LEN,
|
||||||
max_concurrent_db_requests: DEFAULT_CONCURRENT_DB_REQUESTS,
|
max_concurrent_db_requests: DEFAULT_CONCURRENT_DB_REQUESTS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
136
crates/rpc/rpc-eth-types/src/cache/mod.rs
vendored
136
crates/rpc/rpc-eth-types/src/cache/mod.rs
vendored
@ -1,17 +1,16 @@
|
|||||||
//! Async caching support for eth RPC
|
//! Async caching support for eth RPC
|
||||||
|
|
||||||
|
use super::{EthStateCacheConfig, MultiConsumerLruCache};
|
||||||
use alloy_consensus::Header;
|
use alloy_consensus::Header;
|
||||||
use alloy_eips::BlockHashOrNumber;
|
use alloy_eips::BlockHashOrNumber;
|
||||||
use alloy_primitives::B256;
|
use alloy_primitives::B256;
|
||||||
use futures::{future::Either, Stream, StreamExt};
|
use futures::{future::Either, Stream, StreamExt};
|
||||||
use reth_chain_state::CanonStateNotification;
|
use reth_chain_state::CanonStateNotification;
|
||||||
use reth_errors::{ProviderError, ProviderResult};
|
use reth_errors::{ProviderError, ProviderResult};
|
||||||
use reth_evm::{provider::EvmEnvProvider, ConfigureEvm};
|
|
||||||
use reth_execution_types::Chain;
|
use reth_execution_types::Chain;
|
||||||
use reth_primitives::{Receipt, SealedBlockWithSenders, TransactionSigned};
|
use reth_primitives::{Receipt, SealedBlockWithSenders, TransactionSigned};
|
||||||
use reth_storage_api::{BlockReader, StateProviderFactory, TransactionVariant};
|
use reth_storage_api::{BlockReader, StateProviderFactory, TransactionVariant};
|
||||||
use reth_tasks::{TaskSpawner, TokioTaskExecutor};
|
use reth_tasks::{TaskSpawner, TokioTaskExecutor};
|
||||||
use revm::primitives::{BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, SpecId};
|
|
||||||
use schnellru::{ByLength, Limiter};
|
use schnellru::{ByLength, Limiter};
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
@ -25,8 +24,6 @@ use tokio::sync::{
|
|||||||
};
|
};
|
||||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
|
|
||||||
use super::{EthStateCacheConfig, MultiConsumerLruCache};
|
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod metrics;
|
pub mod metrics;
|
||||||
@ -43,8 +40,8 @@ type BlockWithSendersResponseSender =
|
|||||||
/// The type that can send the response to the requested receipts of a block.
|
/// The type that can send the response to the requested receipts of a block.
|
||||||
type ReceiptsResponseSender = oneshot::Sender<ProviderResult<Option<Arc<Vec<Receipt>>>>>;
|
type ReceiptsResponseSender = oneshot::Sender<ProviderResult<Option<Arc<Vec<Receipt>>>>>;
|
||||||
|
|
||||||
/// The type that can send the response to a requested env
|
/// The type that can send the response to a requested header
|
||||||
type EnvResponseSender = oneshot::Sender<ProviderResult<(CfgEnvWithHandlerCfg, BlockEnv)>>;
|
type HeaderResponseSender = oneshot::Sender<ProviderResult<Header>>;
|
||||||
|
|
||||||
type BlockLruCache<L> = MultiConsumerLruCache<
|
type BlockLruCache<L> = MultiConsumerLruCache<
|
||||||
B256,
|
B256,
|
||||||
@ -56,8 +53,7 @@ type BlockLruCache<L> = MultiConsumerLruCache<
|
|||||||
type ReceiptsLruCache<L> =
|
type ReceiptsLruCache<L> =
|
||||||
MultiConsumerLruCache<B256, Arc<Vec<Receipt>>, L, ReceiptsResponseSender>;
|
MultiConsumerLruCache<B256, Arc<Vec<Receipt>>, L, ReceiptsResponseSender>;
|
||||||
|
|
||||||
type EnvLruCache<L> =
|
type HeaderLruCache<L> = MultiConsumerLruCache<B256, Header, L, HeaderResponseSender>;
|
||||||
MultiConsumerLruCache<B256, (CfgEnvWithHandlerCfg, BlockEnv), L, EnvResponseSender>;
|
|
||||||
|
|
||||||
/// Provides async access to cached eth data
|
/// Provides async access to cached eth data
|
||||||
///
|
///
|
||||||
@ -70,26 +66,24 @@ pub struct EthStateCache {
|
|||||||
|
|
||||||
impl EthStateCache {
|
impl EthStateCache {
|
||||||
/// Creates and returns both [`EthStateCache`] frontend and the memory bound service.
|
/// Creates and returns both [`EthStateCache`] frontend and the memory bound service.
|
||||||
fn create<Provider, Tasks, EvmConfig>(
|
fn create<Provider, Tasks>(
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
action_task_spawner: Tasks,
|
action_task_spawner: Tasks,
|
||||||
evm_config: EvmConfig,
|
|
||||||
max_blocks: u32,
|
max_blocks: u32,
|
||||||
max_receipts: u32,
|
max_receipts: u32,
|
||||||
max_envs: u32,
|
max_headers: u32,
|
||||||
max_concurrent_db_operations: usize,
|
max_concurrent_db_operations: usize,
|
||||||
) -> (Self, EthStateCacheService<Provider, Tasks, EvmConfig>) {
|
) -> (Self, EthStateCacheService<Provider, Tasks>) {
|
||||||
let (to_service, rx) = unbounded_channel();
|
let (to_service, rx) = unbounded_channel();
|
||||||
let service = EthStateCacheService {
|
let service = EthStateCacheService {
|
||||||
provider,
|
provider,
|
||||||
full_block_cache: BlockLruCache::new(max_blocks, "blocks"),
|
full_block_cache: BlockLruCache::new(max_blocks, "blocks"),
|
||||||
receipts_cache: ReceiptsLruCache::new(max_receipts, "receipts"),
|
receipts_cache: ReceiptsLruCache::new(max_receipts, "receipts"),
|
||||||
evm_env_cache: EnvLruCache::new(max_envs, "evm_env"),
|
headers_cache: HeaderLruCache::new(max_headers, "headers"),
|
||||||
action_tx: to_service.clone(),
|
action_tx: to_service.clone(),
|
||||||
action_rx: UnboundedReceiverStream::new(rx),
|
action_rx: UnboundedReceiverStream::new(rx),
|
||||||
action_task_spawner,
|
action_task_spawner,
|
||||||
rate_limiter: Arc::new(Semaphore::new(max_concurrent_db_operations)),
|
rate_limiter: Arc::new(Semaphore::new(max_concurrent_db_operations)),
|
||||||
evm_config,
|
|
||||||
};
|
};
|
||||||
let cache = Self { to_service };
|
let cache = Self { to_service };
|
||||||
(cache, service)
|
(cache, service)
|
||||||
@ -99,52 +93,46 @@ impl EthStateCache {
|
|||||||
/// [`tokio::spawn`].
|
/// [`tokio::spawn`].
|
||||||
///
|
///
|
||||||
/// See also [`Self::spawn_with`]
|
/// See also [`Self::spawn_with`]
|
||||||
pub fn spawn<Provider, EvmConfig>(
|
pub fn spawn<Provider>(provider: Provider, config: EthStateCacheConfig) -> Self
|
||||||
provider: Provider,
|
|
||||||
config: EthStateCacheConfig,
|
|
||||||
evm_config: EvmConfig,
|
|
||||||
) -> Self
|
|
||||||
where
|
where
|
||||||
Provider: StateProviderFactory
|
Provider: StateProviderFactory
|
||||||
+ BlockReader<Block = reth_primitives::Block, Receipt = reth_primitives::Receipt>
|
+ BlockReader<Block = reth_primitives::Block, Receipt = reth_primitives::Receipt>
|
||||||
+ EvmEnvProvider
|
|
||||||
+ Clone
|
+ Clone
|
||||||
+ Unpin
|
+ Unpin
|
||||||
+ 'static,
|
+ 'static,
|
||||||
EvmConfig: ConfigureEvm<Header = Header>,
|
|
||||||
{
|
{
|
||||||
Self::spawn_with(provider, config, TokioTaskExecutor::default(), evm_config)
|
Self::spawn_with(provider, config, TokioTaskExecutor::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new async LRU backed cache service task and spawns it to a new task via the given
|
/// Creates a new async LRU backed cache service task and spawns it to a new task via the given
|
||||||
/// spawner.
|
/// spawner.
|
||||||
///
|
///
|
||||||
/// The cache is memory limited by the given max bytes values.
|
/// The cache is memory limited by the given max bytes values.
|
||||||
pub fn spawn_with<Provider, Tasks, EvmConfig>(
|
pub fn spawn_with<Provider, Tasks>(
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
config: EthStateCacheConfig,
|
config: EthStateCacheConfig,
|
||||||
executor: Tasks,
|
executor: Tasks,
|
||||||
evm_config: EvmConfig,
|
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
Provider: StateProviderFactory
|
Provider: StateProviderFactory
|
||||||
+ BlockReader<Block = reth_primitives::Block, Receipt = reth_primitives::Receipt>
|
+ BlockReader<Block = reth_primitives::Block, Receipt = reth_primitives::Receipt>
|
||||||
+ EvmEnvProvider
|
|
||||||
+ Clone
|
+ Clone
|
||||||
+ Unpin
|
+ Unpin
|
||||||
+ 'static,
|
+ 'static,
|
||||||
Tasks: TaskSpawner + Clone + 'static,
|
Tasks: TaskSpawner + Clone + 'static,
|
||||||
EvmConfig: ConfigureEvm<Header = Header>,
|
|
||||||
{
|
{
|
||||||
let EthStateCacheConfig { max_blocks, max_receipts, max_envs, max_concurrent_db_requests } =
|
let EthStateCacheConfig {
|
||||||
config;
|
max_blocks,
|
||||||
|
max_receipts,
|
||||||
|
max_headers,
|
||||||
|
max_concurrent_db_requests,
|
||||||
|
} = config;
|
||||||
let (this, service) = Self::create(
|
let (this, service) = Self::create(
|
||||||
provider,
|
provider,
|
||||||
executor.clone(),
|
executor.clone(),
|
||||||
evm_config,
|
|
||||||
max_blocks,
|
max_blocks,
|
||||||
max_receipts,
|
max_receipts,
|
||||||
max_envs,
|
max_headers,
|
||||||
max_concurrent_db_requests,
|
max_concurrent_db_requests,
|
||||||
);
|
);
|
||||||
executor.spawn_critical("eth state cache", Box::pin(service));
|
executor.spawn_critical("eth state cache", Box::pin(service));
|
||||||
@ -188,16 +176,12 @@ impl EthStateCache {
|
|||||||
Ok(block.zip(receipts))
|
Ok(block.zip(receipts))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests the evm env config for the block hash.
|
/// Requests the header for the given hash.
|
||||||
///
|
///
|
||||||
/// Returns an error if the corresponding header (required for populating the envs) was not
|
/// Returns an error if the header is not found.
|
||||||
/// found.
|
pub async fn get_header(&self, block_hash: B256) -> ProviderResult<Header> {
|
||||||
pub async fn get_evm_env(
|
|
||||||
&self,
|
|
||||||
block_hash: B256,
|
|
||||||
) -> ProviderResult<(CfgEnvWithHandlerCfg, BlockEnv)> {
|
|
||||||
let (response_tx, rx) = oneshot::channel();
|
let (response_tx, rx) = oneshot::channel();
|
||||||
let _ = self.to_service.send(CacheAction::GetEnv { block_hash, response_tx });
|
let _ = self.to_service.send(CacheAction::GetHeader { block_hash, response_tx });
|
||||||
rx.await.map_err(|_| ProviderError::CacheServiceUnavailable)?
|
rx.await.map_err(|_| ProviderError::CacheServiceUnavailable)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,14 +206,13 @@ impl EthStateCache {
|
|||||||
pub(crate) struct EthStateCacheService<
|
pub(crate) struct EthStateCacheService<
|
||||||
Provider,
|
Provider,
|
||||||
Tasks,
|
Tasks,
|
||||||
EvmConfig,
|
|
||||||
LimitBlocks = ByLength,
|
LimitBlocks = ByLength,
|
||||||
LimitReceipts = ByLength,
|
LimitReceipts = ByLength,
|
||||||
LimitEnvs = ByLength,
|
LimitHeaders = ByLength,
|
||||||
> where
|
> where
|
||||||
LimitBlocks: Limiter<B256, Arc<SealedBlockWithSenders>>,
|
LimitBlocks: Limiter<B256, Arc<SealedBlockWithSenders>>,
|
||||||
LimitReceipts: Limiter<B256, Arc<Vec<Receipt>>>,
|
LimitReceipts: Limiter<B256, Arc<Vec<Receipt>>>,
|
||||||
LimitEnvs: Limiter<B256, (CfgEnvWithHandlerCfg, BlockEnv)>,
|
LimitHeaders: Limiter<B256, Header>,
|
||||||
{
|
{
|
||||||
/// The type used to lookup data from disk
|
/// The type used to lookup data from disk
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
@ -237,8 +220,11 @@ pub(crate) struct EthStateCacheService<
|
|||||||
full_block_cache: BlockLruCache<LimitBlocks>,
|
full_block_cache: BlockLruCache<LimitBlocks>,
|
||||||
/// The LRU cache for full blocks grouped by their hash.
|
/// The LRU cache for full blocks grouped by their hash.
|
||||||
receipts_cache: ReceiptsLruCache<LimitReceipts>,
|
receipts_cache: ReceiptsLruCache<LimitReceipts>,
|
||||||
/// The LRU cache for revm environments
|
/// The LRU cache for headers.
|
||||||
evm_env_cache: EnvLruCache<LimitEnvs>,
|
///
|
||||||
|
/// Headers are cached because they are required to populate the environment for execution
|
||||||
|
/// (evm).
|
||||||
|
headers_cache: HeaderLruCache<LimitHeaders>,
|
||||||
/// Sender half of the action channel.
|
/// Sender half of the action channel.
|
||||||
action_tx: UnboundedSender<CacheAction>,
|
action_tx: UnboundedSender<CacheAction>,
|
||||||
/// Receiver half of the action channel.
|
/// Receiver half of the action channel.
|
||||||
@ -247,15 +233,12 @@ pub(crate) struct EthStateCacheService<
|
|||||||
action_task_spawner: Tasks,
|
action_task_spawner: Tasks,
|
||||||
/// Rate limiter
|
/// Rate limiter
|
||||||
rate_limiter: Arc<Semaphore>,
|
rate_limiter: Arc<Semaphore>,
|
||||||
/// The type that determines how to configure the EVM.
|
|
||||||
evm_config: EvmConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Provider, Tasks, EvmConfig> EthStateCacheService<Provider, Tasks, EvmConfig>
|
impl<Provider, Tasks> EthStateCacheService<Provider, Tasks>
|
||||||
where
|
where
|
||||||
Provider: StateProviderFactory + BlockReader + EvmEnvProvider + Clone + Unpin + 'static,
|
Provider: StateProviderFactory + BlockReader + Clone + Unpin + 'static,
|
||||||
Tasks: TaskSpawner + Clone + 'static,
|
Tasks: TaskSpawner + Clone + 'static,
|
||||||
EvmConfig: ConfigureEvm<Header = Header>,
|
|
||||||
{
|
{
|
||||||
fn on_new_block(
|
fn on_new_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -341,20 +324,18 @@ where
|
|||||||
fn update_cached_metrics(&self) {
|
fn update_cached_metrics(&self) {
|
||||||
self.full_block_cache.update_cached_metrics();
|
self.full_block_cache.update_cached_metrics();
|
||||||
self.receipts_cache.update_cached_metrics();
|
self.receipts_cache.update_cached_metrics();
|
||||||
self.evm_env_cache.update_cached_metrics();
|
self.headers_cache.update_cached_metrics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Provider, Tasks, EvmConfig> Future for EthStateCacheService<Provider, Tasks, EvmConfig>
|
impl<Provider, Tasks> Future for EthStateCacheService<Provider, Tasks>
|
||||||
where
|
where
|
||||||
Provider: StateProviderFactory
|
Provider: StateProviderFactory
|
||||||
+ BlockReader<Block = reth_primitives::Block, Receipt = reth_primitives::Receipt>
|
+ BlockReader<Block = reth_primitives::Block, Receipt = reth_primitives::Receipt>
|
||||||
+ EvmEnvProvider
|
|
||||||
+ Clone
|
+ Clone
|
||||||
+ Unpin
|
+ Unpin
|
||||||
+ 'static,
|
+ 'static,
|
||||||
Tasks: TaskSpawner + Clone + 'static,
|
Tasks: TaskSpawner + Clone + 'static,
|
||||||
EvmConfig: ConfigureEvm<Header = Header>,
|
|
||||||
{
|
{
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
@ -421,39 +402,30 @@ where
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CacheAction::GetEnv { block_hash, response_tx } => {
|
CacheAction::GetHeader { block_hash, response_tx } => {
|
||||||
// check if env data is cached
|
// check if the header is cached
|
||||||
if let Some(env) = this.evm_env_cache.get(&block_hash).cloned() {
|
if let Some(header) = this.headers_cache.get(&block_hash).cloned() {
|
||||||
let _ = response_tx.send(Ok(env));
|
let _ = response_tx.send(Ok(header));
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// env data is not in the cache, request it if this is the first
|
// header is not in the cache, request it if this is the first
|
||||||
// consumer
|
// consumer
|
||||||
if this.evm_env_cache.queue(block_hash, response_tx) {
|
if this.headers_cache.queue(block_hash, response_tx) {
|
||||||
let provider = this.provider.clone();
|
let provider = this.provider.clone();
|
||||||
let action_tx = this.action_tx.clone();
|
let action_tx = this.action_tx.clone();
|
||||||
let rate_limiter = this.rate_limiter.clone();
|
let rate_limiter = this.rate_limiter.clone();
|
||||||
let evm_config = this.evm_config.clone();
|
|
||||||
this.action_task_spawner.spawn_blocking(Box::pin(async move {
|
this.action_task_spawner.spawn_blocking(Box::pin(async move {
|
||||||
// Acquire permit
|
// Acquire permit
|
||||||
let _permit = rate_limiter.acquire().await;
|
let _permit = rate_limiter.acquire().await;
|
||||||
let mut cfg = CfgEnvWithHandlerCfg::new_with_spec_id(
|
let header = provider.header(&block_hash).and_then(|header| {
|
||||||
CfgEnv::default(),
|
header.ok_or_else(|| {
|
||||||
SpecId::LATEST,
|
ProviderError::HeaderNotFound(block_hash.into())
|
||||||
);
|
})
|
||||||
let mut block_env = BlockEnv::default();
|
});
|
||||||
let res = provider
|
let _ = action_tx.send(CacheAction::HeaderResult {
|
||||||
.fill_env_at(
|
|
||||||
&mut cfg,
|
|
||||||
&mut block_env,
|
|
||||||
block_hash.into(),
|
|
||||||
evm_config,
|
|
||||||
)
|
|
||||||
.map(|_| (cfg, block_env));
|
|
||||||
let _ = action_tx.send(CacheAction::EnvResult {
|
|
||||||
block_hash,
|
block_hash,
|
||||||
res: Box::new(res),
|
res: Box::new(header),
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -472,18 +444,18 @@ where
|
|||||||
this.on_new_block(block_hash, Err(e));
|
this.on_new_block(block_hash, Err(e));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CacheAction::EnvResult { block_hash, res } => {
|
CacheAction::HeaderResult { block_hash, res } => {
|
||||||
let res = *res;
|
let res = *res;
|
||||||
if let Some(queued) = this.evm_env_cache.remove(&block_hash) {
|
if let Some(queued) = this.headers_cache.remove(&block_hash) {
|
||||||
// send the response to queued senders
|
// send the response to queued senders
|
||||||
for tx in queued {
|
for tx in queued {
|
||||||
let _ = tx.send(res.clone());
|
let _ = tx.send(res.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache good env data
|
// cache good header
|
||||||
if let Ok(data) = res {
|
if let Ok(data) = res {
|
||||||
this.evm_env_cache.insert(block_hash, data);
|
this.headers_cache.insert(block_hash, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CacheAction::CacheNewCanonicalChain { chain_change } => {
|
CacheAction::CacheNewCanonicalChain { chain_change } => {
|
||||||
@ -528,9 +500,9 @@ enum CacheAction {
|
|||||||
block_hash: B256,
|
block_hash: B256,
|
||||||
response_tx: BlockWithSendersResponseSender,
|
response_tx: BlockWithSendersResponseSender,
|
||||||
},
|
},
|
||||||
GetEnv {
|
GetHeader {
|
||||||
block_hash: B256,
|
block_hash: B256,
|
||||||
response_tx: EnvResponseSender,
|
response_tx: HeaderResponseSender,
|
||||||
},
|
},
|
||||||
GetReceipts {
|
GetReceipts {
|
||||||
block_hash: B256,
|
block_hash: B256,
|
||||||
@ -544,9 +516,9 @@ enum CacheAction {
|
|||||||
block_hash: B256,
|
block_hash: B256,
|
||||||
res: ProviderResult<Option<Arc<Vec<Receipt>>>>,
|
res: ProviderResult<Option<Arc<Vec<Receipt>>>>,
|
||||||
},
|
},
|
||||||
EnvResult {
|
HeaderResult {
|
||||||
block_hash: B256,
|
block_hash: B256,
|
||||||
res: Box<ProviderResult<(CfgEnvWithHandlerCfg, BlockEnv)>>,
|
res: Box<ProviderResult<Header>>,
|
||||||
},
|
},
|
||||||
CacheNewCanonicalChain {
|
CacheNewCanonicalChain {
|
||||||
chain_change: ChainChange,
|
chain_change: ChainChange,
|
||||||
|
|||||||
@ -113,8 +113,8 @@ pub mod cache {
|
|||||||
/// Default cache size for the receipts cache: 2000 receipts.
|
/// Default cache size for the receipts cache: 2000 receipts.
|
||||||
pub const DEFAULT_RECEIPT_CACHE_MAX_LEN: u32 = 2000;
|
pub const DEFAULT_RECEIPT_CACHE_MAX_LEN: u32 = 2000;
|
||||||
|
|
||||||
/// Default cache size for the env cache: 1000 envs.
|
/// Default cache size for the header cache: 1000 headers.
|
||||||
pub const DEFAULT_ENV_CACHE_MAX_LEN: u32 = 1000;
|
pub const DEFAULT_HEADER_CACHE_MAX_LEN: u32 = 1000;
|
||||||
|
|
||||||
/// Default number of concurrent database requests.
|
/// Default number of concurrent database requests.
|
||||||
pub const DEFAULT_CONCURRENT_DB_REQUESTS: usize = 512;
|
pub const DEFAULT_CONCURRENT_DB_REQUESTS: usize = 512;
|
||||||
|
|||||||
@ -450,7 +450,7 @@ mod tests {
|
|||||||
provider: P,
|
provider: P,
|
||||||
) -> EthApi<P, TestPool, NoopNetwork, EthEvmConfig> {
|
) -> EthApi<P, TestPool, NoopNetwork, EthEvmConfig> {
|
||||||
let evm_config = EthEvmConfig::new(provider.chain_spec());
|
let evm_config = EthEvmConfig::new(provider.chain_spec());
|
||||||
let cache = EthStateCache::spawn(provider.clone(), Default::default(), evm_config.clone());
|
let cache = EthStateCache::spawn(provider.clone(), Default::default());
|
||||||
let fee_history_cache =
|
let fee_history_cache =
|
||||||
FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default());
|
FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default());
|
||||||
|
|
||||||
|
|||||||
@ -52,8 +52,7 @@ mod tests {
|
|||||||
let pool = testing_pool();
|
let pool = testing_pool();
|
||||||
let evm_config = EthEvmConfig::new(MAINNET.clone());
|
let evm_config = EthEvmConfig::new(MAINNET.clone());
|
||||||
|
|
||||||
let cache =
|
let cache = EthStateCache::spawn(NoopProvider::default(), Default::default());
|
||||||
EthStateCache::spawn(NoopProvider::default(), Default::default(), evm_config.clone());
|
|
||||||
EthApi::new(
|
EthApi::new(
|
||||||
NoopProvider::default(),
|
NoopProvider::default(),
|
||||||
pool,
|
pool,
|
||||||
@ -79,8 +78,7 @@ mod tests {
|
|||||||
let evm_config = EthEvmConfig::new(mock_provider.chain_spec());
|
let evm_config = EthEvmConfig::new(mock_provider.chain_spec());
|
||||||
mock_provider.extend_accounts(accounts);
|
mock_provider.extend_accounts(accounts);
|
||||||
|
|
||||||
let cache =
|
let cache = EthStateCache::spawn(mock_provider.clone(), Default::default());
|
||||||
EthStateCache::spawn(mock_provider.clone(), Default::default(), evm_config.clone());
|
|
||||||
EthApi::new(
|
EthApi::new(
|
||||||
mock_provider.clone(),
|
mock_provider.clone(),
|
||||||
pool,
|
pool,
|
||||||
|
|||||||
@ -57,7 +57,7 @@ mod tests {
|
|||||||
let pool = testing_pool();
|
let pool = testing_pool();
|
||||||
|
|
||||||
let evm_config = EthEvmConfig::new(noop_provider.chain_spec());
|
let evm_config = EthEvmConfig::new(noop_provider.chain_spec());
|
||||||
let cache = EthStateCache::spawn(noop_provider, Default::default(), evm_config.clone());
|
let cache = EthStateCache::spawn(noop_provider, Default::default());
|
||||||
let fee_history_cache =
|
let fee_history_cache =
|
||||||
FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default());
|
FeeHistoryCache::new(cache.clone(), FeeHistoryCacheConfig::default());
|
||||||
let eth_api = EthApi::new(
|
let eth_api = EthApi::new(
|
||||||
|
|||||||
Reference in New Issue
Block a user