Merge pull request #13 from Quertyy/chore/forward-calls-only-against-latest

chore(rpc): only forward calls to upstream if against latest block state
This commit is contained in:
sprites0
2025-07-06 21:37:51 -04:00
committed by GitHub
2 changed files with 75 additions and 32 deletions

View File

@ -1,6 +1,10 @@
use alloy_eips::BlockId; use alloy_eips::BlockId;
use alloy_primitives::{Bytes, U256}; use alloy_primitives::{Bytes, U256};
use alloy_rpc_types_eth::{state::StateOverride, transaction::TransactionRequest, BlockOverrides}; use alloy_rpc_types_eth::{
state::{EvmOverrides, StateOverride},
transaction::TransactionRequest,
BlockOverrides,
};
use jsonrpsee::{ use jsonrpsee::{
http_client::{HttpClient, HttpClientBuilder}, http_client::{HttpClient, HttpClientBuilder},
proc_macros::rpc, proc_macros::rpc,
@ -8,6 +12,7 @@ use jsonrpsee::{
types::{error::INTERNAL_ERROR_CODE, ErrorObject}, types::{error::INTERNAL_ERROR_CODE, ErrorObject},
}; };
use jsonrpsee_core::{async_trait, client::ClientT, ClientError, RpcResult}; use jsonrpsee_core::{async_trait, client::ClientT, ClientError, RpcResult};
use reth_rpc_eth_api::helpers::EthCall;
#[rpc(server, namespace = "eth")] #[rpc(server, namespace = "eth")]
pub(crate) trait CallForwarderApi { pub(crate) trait CallForwarderApi {
@ -32,21 +37,25 @@ pub(crate) trait CallForwarderApi {
) -> RpcResult<U256>; ) -> RpcResult<U256>;
} }
pub struct CallForwarderExt { pub struct CallForwarderExt<EthApi> {
client: HttpClient, upstream_client: HttpClient,
eth_api: EthApi,
} }
impl CallForwarderExt { impl<EthApi> CallForwarderExt<EthApi> {
pub fn new(upstream_rpc_url: String) -> Self { pub fn new(upstream_rpc_url: String, eth_api: EthApi) -> Self {
let client = let upstream_client =
HttpClientBuilder::default().build(upstream_rpc_url).expect("Failed to build client"); HttpClientBuilder::default().build(upstream_rpc_url).expect("Failed to build client");
Self { client } Self { upstream_client, eth_api }
} }
} }
#[async_trait] #[async_trait]
impl CallForwarderApiServer for CallForwarderExt { impl<EthApi> CallForwarderApiServer for CallForwarderExt<EthApi>
where
EthApi: EthCall + Send + Sync + 'static,
{
async fn call( async fn call(
&self, &self,
request: TransactionRequest, request: TransactionRequest,
@ -54,22 +63,35 @@ impl CallForwarderApiServer for CallForwarderExt {
state_overrides: Option<StateOverride>, state_overrides: Option<StateOverride>,
block_overrides: Option<Box<BlockOverrides>>, block_overrides: Option<Box<BlockOverrides>>,
) -> RpcResult<Bytes> { ) -> RpcResult<Bytes> {
let result = self let is_latest = block_number.as_ref().map(|b| b.is_latest()).unwrap_or(true);
.client let result = if is_latest {
.clone() self.upstream_client
.request( .request(
"eth_call", "eth_call",
rpc_params![request, block_number, state_overrides, block_overrides], rpc_params![request, block_number, state_overrides, block_overrides],
)
.await
.map_err(|e| match e {
ClientError::Call(e) => e,
_ => ErrorObject::owned(
INTERNAL_ERROR_CODE,
format!("Failed to call: {e:?}"),
Some(()),
),
})?
} else {
EthCall::call(
&self.eth_api,
request,
block_number,
EvmOverrides::new(state_overrides, block_overrides),
) )
.await .await
.map_err(|e| match e { .map_err(|e| {
ClientError::Call(e) => e, ErrorObject::owned(INTERNAL_ERROR_CODE, format!("Failed to call: {e:?}"), Some(()))
_ => ErrorObject::owned( })?
INTERNAL_ERROR_CODE, };
format!("Failed to call: {e:?}"),
Some(()),
),
})?;
Ok(result) Ok(result)
} }
@ -79,19 +101,36 @@ impl CallForwarderApiServer for CallForwarderExt {
block_number: Option<BlockId>, block_number: Option<BlockId>,
state_override: Option<StateOverride>, state_override: Option<StateOverride>,
) -> RpcResult<U256> { ) -> RpcResult<U256> {
let result = self let is_latest = block_number.as_ref().map(|b| b.is_latest()).unwrap_or(true);
.client let result = if is_latest {
.clone() self.upstream_client
.request("eth_estimateGas", rpc_params![request, block_number, state_override]) .request("eth_estimateGas", rpc_params![request, block_number, state_override])
.await
.map_err(|e| match e {
ClientError::Call(e) => e,
_ => ErrorObject::owned(
INTERNAL_ERROR_CODE,
format!("Failed to estimate gas: {e:?}"),
Some(()),
),
})?
} else {
EthCall::estimate_gas_at(
&self.eth_api,
request,
block_number.unwrap_or_default(),
state_override,
)
.await .await
.map_err(|e| match e { .map_err(|e| {
ClientError::Call(e) => e, ErrorObject::owned(
_ => ErrorObject::owned(
INTERNAL_ERROR_CODE, INTERNAL_ERROR_CODE,
format!("Failed to estimate gas: {e:?}"), format!("Failed to estimate gas: {e:?}"),
Some(()), Some(()),
), )
})?; })?
};
Ok(result) Ok(result)
} }
} }

View File

@ -45,7 +45,11 @@ fn main() -> eyre::Result<()> {
if ext.forward_call { if ext.forward_call {
ctx.modules.replace_configured( ctx.modules.replace_configured(
call_forwarder::CallForwarderExt::new(upstream_rpc_url.clone()).into_rpc(), call_forwarder::CallForwarderExt::new(
upstream_rpc_url.clone(),
ctx.registry.eth_api().clone(),
)
.into_rpc(),
)?; )?;
info!("Call/gas estimation will be forwarded to {}", upstream_rpc_url); info!("Call/gas estimation will be forwarded to {}", upstream_rpc_url);
} }