Skip to content

Commit

Permalink
feat(rpc): optimize block validation with state caching (#12299)
Browse files Browse the repository at this point in the history
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
  • Loading branch information
lean-apple and mattsse authored Nov 8, 2024
1 parent 32ebb18 commit 74d7fe3
Showing 1 changed file with 43 additions and 7 deletions.
50 changes: 43 additions & 7 deletions crates/rpc/rpc/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ use reth_provider::{
AccountReader, BlockExecutionInput, BlockExecutionOutput, BlockReaderIdExt, HeaderProvider,
StateProviderFactory, WithdrawalsProvider,
};
use reth_revm::database::StateProviderDatabase;
use reth_revm::{cached::CachedReads, database::StateProviderDatabase};
use reth_rpc_api::BlockSubmissionValidationApiServer;
use reth_rpc_eth_types::EthApiError;
use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
use reth_trie::HashedPostState;
use revm_primitives::{Address, B256, U256};
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, sync::Arc};
use tokio::sync::RwLock;

/// Configuration for validation API.
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -64,7 +65,7 @@ pub enum ValidationApiError {
Execution(#[from] BlockExecutionError),
}

#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct ValidationApiInner<Provider: ChainSpecProvider, E> {
/// The provider that can interact with the chain.
provider: Provider,
Expand All @@ -76,10 +77,15 @@ pub struct ValidationApiInner<Provider: ChainSpecProvider, E> {
executor_provider: E,
/// Set of disallowed addresses
disallow: HashSet<Address>,
/// Cached state reads to avoid redundant disk I/O across multiple validation attempts
/// targeting the same state. Stores a tuple of (`block_hash`, `cached_reads`) for the
/// latest head block state. Uses async `RwLock` to safely handle concurrent validation
/// requests.
cached_state: RwLock<(B256, CachedReads)>,
}

/// The type that implements the `validation` rpc namespace trait
#[derive(Clone, Debug, derive_more::Deref)]
#[derive(Debug, derive_more::Deref)]
pub struct ValidationApi<Provider: ChainSpecProvider, E> {
#[deref]
inner: Arc<ValidationApiInner<Provider, E>>,
Expand All @@ -105,10 +111,31 @@ where
payload_validator,
executor_provider,
disallow,
cached_state: Default::default(),
});

Self { inner }
}

/// Returns the cached reads for the given head hash.
async fn cached_reads(&self, head: B256) -> CachedReads {
let cache = self.inner.cached_state.read().await;
if cache.0 == head {
cache.1.clone()
} else {
Default::default()
}
}

/// Updates the cached state for the given head hash.
async fn update_cached_reads(&self, head: B256, cached_state: CachedReads) {
let mut cache = self.inner.cached_state.write().await;
if cache.0 == head {
cache.1.extend(cached_state);
} else {
*cache = (head, cached_state)
}
}
}

impl<Provider, E> ValidationApi<Provider, E>
Expand All @@ -119,12 +146,11 @@ where
+ HeaderProvider
+ AccountReader
+ WithdrawalsProvider
+ Clone
+ 'static,
E: BlockExecutorProvider,
{
/// Validates the given block and a [`BidTrace`] against it.
pub fn validate_message_against_block(
pub async fn validate_message_against_block(
&self,
block: SealedBlockWithSenders,
message: BidTrace,
Expand Down Expand Up @@ -168,8 +194,13 @@ where
self.consensus.validate_header_against_parent(&block.header, &latest_header)?;
self.validate_gas_limit(registered_gas_limit, &latest_header, &block.header)?;

let state_provider = self.provider.state_by_block_hash(latest_header.hash())?;
let executor = self.executor_provider.executor(StateProviderDatabase::new(&state_provider));
let latest_header_hash = latest_header.hash();
let state_provider = self.provider.state_by_block_hash(latest_header_hash)?;

let mut request_cache = self.cached_reads(latest_header_hash).await;

let cached_db = request_cache.as_db_mut(StateProviderDatabase::new(&state_provider));
let executor = self.executor_provider.executor(cached_db);

let block = block.unseal();
let mut accessed_blacklisted = None;
Expand All @@ -186,6 +217,9 @@ where
},
)?;

// update the cached reads
self.update_cached_reads(latest_header_hash, request_cache).await;

if let Some(account) = accessed_blacklisted {
return Err(ValidationApiError::Blacklist(account))
}
Expand Down Expand Up @@ -413,6 +447,7 @@ where
request.request.message,
request.registered_gas_limit,
)
.await
.map_err(|e| RethError::Other(e.into()))
.to_rpc_result()
}
Expand Down Expand Up @@ -446,6 +481,7 @@ where
request.request.message,
request.registered_gas_limit,
)
.await
.map_err(|e| RethError::Other(e.into()))
.to_rpc_result()
}
Expand Down

0 comments on commit 74d7fe3

Please sign in to comment.