feat: tx forwarder

eth_sendRawTransaction will forward transactions into --upstream-rpc-url, if specified in cli args.
This commit is contained in:
sprites0
2025-07-01 02:31:29 +00:00
parent 4136d9d50a
commit 39ae8ae9f8
5 changed files with 72 additions and 5 deletions

View File

@ -5,10 +5,10 @@ Heavily inspired by [reth-bsc](https://github.com/loocapro/reth-bsc).
## TODOs
- [ ] Make it compilable
- [x] Make it compilable
- [x] EVM
- [x] Storage
- [ ] TX forwarder API
- [x] TX forwarder API
- [x] Decide whether to include system txs, receipts in block or not
- [x] Downloader
- [x] S3 format (file)

View File

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

View File

@ -1,9 +1,15 @@
use clap::Parser;
use reth::builder::NodeHandle;
use reth_hl::tx_forwarder::{self, EthForwarderApiServer};
use reth_hl::{
chainspec::parser::HlChainSpecParser,
node::{cli::{Cli, HlNodeArgs}, storage::tables::Tables, HlNode},
node::{
cli::{Cli, HlNodeArgs},
storage::tables::Tables,
HlNode,
},
};
use tracing::info;
// We use jemalloc for performance reasons
#[cfg(all(feature = "jemalloc", unix))]
@ -21,8 +27,21 @@ fn main() -> eyre::Result<()> {
Cli::<HlChainSpecParser, HlNodeArgs>::parse().run(|builder, ext| async move {
builder.builder.database.create_tables_for::<Tables>()?;
let (node, engine_handle_tx) = HlNode::new(ext.block_source_args.parse().await?);
let NodeHandle { node, node_exit_future: exit_future } =
builder.node(node).launch().await?;
let NodeHandle { node, node_exit_future: exit_future } = builder
.node(node)
.extend_rpc_modules(|ctx| {
let upstream_rpc_url = ext.upstream_rpc_url;
if let Some(upstream_rpc_url) = upstream_rpc_url {
ctx.modules.replace_configured(
tx_forwarder::EthForwarderExt::new(upstream_rpc_url.clone()).into_rpc(),
)?;
info!("Transaction forwarding enabled");
}
Ok(())
})
.launch()
.await?;
engine_handle_tx.send(node.beacon_engine_handle.clone()).unwrap();

View File

@ -31,6 +31,9 @@ use tracing::info;
pub struct HlNodeArgs {
#[command(flatten)]
pub block_source_args: BlockSourceArgs,
#[arg(long, env = "UPSTREAM_RPC_URL")]
pub upstream_rpc_url: Option<String>,
}
/// The main reth_hl cli interface.

44
src/tx_forwarder.rs Normal file
View File

@ -0,0 +1,44 @@
use alloy_primitives::{Bytes, B256};
use jsonrpsee::{
http_client::{HttpClient, HttpClientBuilder},
proc_macros::rpc,
types::{error::INTERNAL_ERROR_CODE, ErrorObject},
};
use jsonrpsee_core::{async_trait, client::ClientT, ClientError, RpcResult};
#[rpc(server, namespace = "eth")]
pub trait EthForwarderApi {
#[method(name = "sendRawTransaction")]
async fn send_raw_transaction(&self, tx: Bytes) -> RpcResult<B256>;
}
pub struct EthForwarderExt {
client: HttpClient,
}
impl EthForwarderExt {
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 EthForwarderApiServer for EthForwarderExt {
async fn send_raw_transaction(&self, tx: Bytes) -> RpcResult<B256> {
let txhash =
self.client.clone().request("eth_sendRawTransaction", vec![tx]).await.map_err(|e| {
match e {
ClientError::Call(e) => e,
_ => ErrorObject::owned(
INTERNAL_ERROR_CODE,
format!("Failed to send transaction: {e:?}"),
Some(()),
),
}
})?;
Ok(txhash)
}
}