feat: Add call forwarder

4b793c496b
This commit is contained in:
sprites0
2025-07-05 03:10:26 +00:00
parent 2943ba03a7
commit ba33d9e8ac
4 changed files with 112 additions and 0 deletions

97
src/call_forwarder.rs Normal file
View File

@ -0,0 +1,97 @@
use alloy_eips::BlockId;
use alloy_primitives::{Bytes, U256};
use alloy_rpc_types_eth::{state::StateOverride, transaction::TransactionRequest, BlockOverrides};
use jsonrpsee::{
http_client::{HttpClient, HttpClientBuilder},
proc_macros::rpc,
rpc_params,
types::{error::INTERNAL_ERROR_CODE, ErrorObject},
};
use jsonrpsee_core::{async_trait, client::ClientT, ClientError, RpcResult};
#[rpc(server, namespace = "eth")]
pub(crate) trait CallForwarderApi {
/// Executes a new message call immediately without creating a transaction on the block chain.
#[method(name = "call")]
async fn call(
&self,
request: TransactionRequest,
block_number: Option<BlockId>,
state_overrides: Option<StateOverride>,
block_overrides: Option<Box<BlockOverrides>>,
) -> RpcResult<Bytes>;
/// Generates and returns an estimate of how much gas is necessary to allow the transaction to
/// complete.
#[method(name = "estimateGas")]
async fn estimate_gas(
&self,
request: TransactionRequest,
block_number: Option<BlockId>,
state_override: Option<StateOverride>,
) -> RpcResult<U256>;
}
pub struct CallForwarderExt {
client: HttpClient,
}
impl CallForwarderExt {
pub fn new(upstream_rpc_url: String) -> Self {
let client =
HttpClientBuilder::default().build(upstream_rpc_url).expect("Failed to build client");
Self { client }
}
}
#[async_trait]
impl CallForwarderApiServer for CallForwarderExt {
async fn call(
&self,
request: TransactionRequest,
block_number: Option<BlockId>,
state_overrides: Option<StateOverride>,
block_overrides: Option<Box<BlockOverrides>>,
) -> RpcResult<Bytes> {
let result = self
.client
.clone()
.request(
"eth_call",
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(()),
),
})?;
Ok(result)
}
async fn estimate_gas(
&self,
request: TransactionRequest,
block_number: Option<BlockId>,
state_override: Option<StateOverride>,
) -> RpcResult<U256> {
let result = self
.client
.clone()
.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(()),
),
})?;
Ok(result)
}
}

View File

@ -6,5 +6,6 @@ pub mod hl_node_compliance;
pub mod node; pub mod node;
pub mod pseudo_peer; pub mod pseudo_peer;
pub mod tx_forwarder; pub mod tx_forwarder;
pub mod call_forwarder;
pub use node::primitives::{HlBlock, HlBlockBody, HlPrimitives}; pub use node::primitives::{HlBlock, HlBlockBody, HlPrimitives};

View File

@ -1,6 +1,7 @@
use clap::Parser; use clap::Parser;
use reth::builder::NodeHandle; use reth::builder::NodeHandle;
use reth_hl::{ use reth_hl::{
call_forwarder::{self, CallForwarderApiServer},
chainspec::parser::HlChainSpecParser, chainspec::parser::HlChainSpecParser,
hl_node_compliance::install_hl_node_compliance, hl_node_compliance::install_hl_node_compliance,
node::{ node::{
@ -42,6 +43,13 @@ fn main() -> eyre::Result<()> {
)?; )?;
info!("Transaction will be forwarded to {}", upstream_rpc_url); info!("Transaction will be forwarded to {}", upstream_rpc_url);
if ext.forward_call {
ctx.modules.replace_configured(
call_forwarder::CallForwarderExt::new(upstream_rpc_url.clone()).into_rpc(),
)?;
info!("Call/gas estimation will be forwarded to {}", upstream_rpc_url);
}
if ext.hl_node_compliant { if ext.hl_node_compliant {
install_hl_node_compliance(ctx)?; install_hl_node_compliance(ctx)?;
info!("hl-node compliant mode enabled"); info!("hl-node compliant mode enabled");

View File

@ -46,6 +46,12 @@ pub struct HlNodeArgs {
/// 3. filters out logs and transactions from subscription. /// 3. filters out logs and transactions from subscription.
#[arg(long, env = "HL_NODE_COMPLIANT")] #[arg(long, env = "HL_NODE_COMPLIANT")]
pub hl_node_compliant: bool, pub hl_node_compliant: bool,
/// Forward eth_call and eth_estimateGas to the upstream RPC.
///
/// This is useful when read precompile is needed for gas estimation.
#[arg(long, env = "FORWARD_CALL")]
pub forward_call: bool,
} }
/// The main reth_hl cli interface. /// The main reth_hl cli interface.