mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: extract optimism sequencer client to node-optimism (#7482)
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -6795,9 +6795,15 @@ dependencies = [
|
||||
name = "reth-node-optimism"
|
||||
version = "0.2.0-beta.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap",
|
||||
"eyre",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"jsonrpsee",
|
||||
"parking_lot 0.12.1",
|
||||
"reqwest",
|
||||
"reth-basic-payload-builder",
|
||||
"reth-db",
|
||||
"reth-network",
|
||||
@ -6815,6 +6821,8 @@ dependencies = [
|
||||
"reth-transaction-pool",
|
||||
"revm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7014,7 +7022,6 @@ dependencies = [
|
||||
"parking_lot 0.12.1",
|
||||
"pin-project",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"reth-consensus-common",
|
||||
"reth-evm",
|
||||
"reth-evm-ethereum",
|
||||
|
||||
@ -125,8 +125,6 @@ optimism = [
|
||||
"reth-provider/optimism",
|
||||
"reth-beacon-consensus/optimism",
|
||||
"reth-auto-seal-consensus/optimism",
|
||||
"reth-network/optimism",
|
||||
"reth-network-api/optimism",
|
||||
"reth-blockchain-tree/optimism",
|
||||
"dep:reth-node-optimism",
|
||||
"reth-node-core/optimism"
|
||||
|
||||
@ -3,8 +3,11 @@
|
||||
use clap::Parser;
|
||||
use reth::cli::Cli;
|
||||
use reth_node_builder::NodeHandle;
|
||||
use reth_node_optimism::{args::RollupArgs, OptimismEngineTypes, OptimismNode};
|
||||
use reth_node_optimism::{
|
||||
args::RollupArgs, rpc::SequencerClient, OptimismEngineTypes, OptimismNode,
|
||||
};
|
||||
use reth_provider::BlockReaderIdExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
// We use jemalloc for performance reasons
|
||||
#[cfg(all(feature = "jemalloc", unix))]
|
||||
@ -24,8 +27,20 @@ fn main() {
|
||||
}
|
||||
|
||||
if let Err(err) = Cli::<RollupArgs>::parse().run(|builder, rollup_args| async move {
|
||||
let NodeHandle { node, node_exit_future } =
|
||||
builder.launch_node(OptimismNode::new(rollup_args.clone())).await?;
|
||||
let NodeHandle { node, node_exit_future } = builder
|
||||
.node(OptimismNode::new(rollup_args.clone()))
|
||||
.extend_rpc_modules(move |ctx| {
|
||||
// register sequencer tx forwarder
|
||||
if let Some(sequencer_http) = rollup_args.sequencer_http.clone() {
|
||||
ctx.registry.set_eth_raw_transaction_forwarder(Arc::new(SequencerClient::new(
|
||||
sequencer_http,
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.launch()
|
||||
.await?;
|
||||
|
||||
// If `enable_genesis_walkback` is set to true, the rollup client will need to
|
||||
// perform the derivation pipeline from genesis, validating the data dir.
|
||||
|
||||
@ -27,5 +27,4 @@ tokio = { workspace = true, features = ["sync"] }
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
||||
serde = ["dep:serde"]
|
||||
optimism = []
|
||||
serde = ["dep:serde"]
|
||||
@ -46,10 +46,6 @@ pub trait NetworkInfo: Send + Sync {
|
||||
|
||||
/// Returns `true` when the node is undergoing the very first Pipeline sync.
|
||||
fn is_initially_syncing(&self) -> bool;
|
||||
|
||||
/// Returns the sequencer HTTP endpoint, if set.
|
||||
#[cfg(feature = "optimism")]
|
||||
fn sequencer_endpoint(&self) -> Option<&str>;
|
||||
}
|
||||
|
||||
/// Provides general purpose information about Peers in the network.
|
||||
|
||||
@ -51,11 +51,6 @@ impl NetworkInfo for NoopNetwork {
|
||||
fn is_initially_syncing(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
fn sequencer_endpoint(&self) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PeersInfo for NoopNetwork {
|
||||
|
||||
@ -101,11 +101,6 @@ test-utils = [
|
||||
"reth-transaction-pool/test-utils",
|
||||
]
|
||||
geth-tests = []
|
||||
optimism = [
|
||||
"reth-primitives/optimism",
|
||||
"reth-provider/optimism",
|
||||
"reth-network-api/optimism",
|
||||
]
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
|
||||
@ -80,17 +80,6 @@ pub struct NetworkConfig<C> {
|
||||
pub tx_gossip_disabled: bool,
|
||||
/// How to instantiate transactions manager.
|
||||
pub transactions_manager_config: TransactionsManagerConfig,
|
||||
/// Optimism Network Config
|
||||
#[cfg(feature = "optimism")]
|
||||
pub optimism_network_config: OptimismNetworkConfig,
|
||||
}
|
||||
|
||||
/// Optimism Network Config
|
||||
#[cfg(feature = "optimism")]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OptimismNetworkConfig {
|
||||
/// The sequencer HTTP endpoint, if provided via CLI flag
|
||||
pub sequencer_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
// === impl NetworkConfig ===
|
||||
@ -227,18 +216,6 @@ pub struct NetworkConfigBuilder {
|
||||
block_import: Option<Box<dyn BlockImport>>,
|
||||
/// How to instantiate transactions manager.
|
||||
transactions_manager_config: TransactionsManagerConfig,
|
||||
/// Optimism Network Config Builder
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_network_config: OptimismNetworkConfigBuilder,
|
||||
}
|
||||
|
||||
/// Optimism Network Config Builder
|
||||
#[cfg(feature = "optimism")]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OptimismNetworkConfigBuilder {
|
||||
/// The sequencer HTTP endpoint, if provided via CLI flag
|
||||
sequencer_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
// === impl NetworkConfigBuilder ===
|
||||
@ -264,8 +241,6 @@ impl NetworkConfigBuilder {
|
||||
head: None,
|
||||
tx_gossip_disabled: false,
|
||||
block_import: None,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_network_config: OptimismNetworkConfigBuilder::default(),
|
||||
transactions_manager_config: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -481,13 +456,6 @@ impl NetworkConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the sequencer HTTP endpoint.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn sequencer_endpoint(mut self, endpoint: Option<String>) -> Self {
|
||||
self.optimism_network_config.sequencer_endpoint = endpoint;
|
||||
self
|
||||
}
|
||||
|
||||
/// Convenience function for creating a [NetworkConfig] with a noop provider that does nothing.
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub fn build_with_noop_provider(
|
||||
@ -522,8 +490,6 @@ impl NetworkConfigBuilder {
|
||||
head,
|
||||
tx_gossip_disabled,
|
||||
block_import,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_network_config: OptimismNetworkConfigBuilder { sequencer_endpoint },
|
||||
transactions_manager_config,
|
||||
} = self;
|
||||
|
||||
@ -578,8 +544,6 @@ impl NetworkConfigBuilder {
|
||||
extra_protocols,
|
||||
fork_filter,
|
||||
tx_gossip_disabled,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_network_config: OptimismNetworkConfig { sequencer_endpoint },
|
||||
transactions_manager_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,8 +194,6 @@ where
|
||||
dns_discovery_config,
|
||||
extra_protocols,
|
||||
tx_gossip_disabled,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism_network_config: crate::config::OptimismNetworkConfig { sequencer_endpoint },
|
||||
transactions_manager_config: _,
|
||||
} = config;
|
||||
|
||||
@ -258,8 +256,6 @@ where
|
||||
bandwidth_meter,
|
||||
Arc::new(AtomicU64::new(chain_spec.chain.id())),
|
||||
tx_gossip_disabled,
|
||||
#[cfg(feature = "optimism")]
|
||||
sequencer_endpoint,
|
||||
discv4,
|
||||
);
|
||||
|
||||
|
||||
@ -50,7 +50,6 @@ impl NetworkHandle {
|
||||
bandwidth_meter: BandwidthMeter,
|
||||
chain_id: Arc<AtomicU64>,
|
||||
tx_gossip_disabled: bool,
|
||||
#[cfg(feature = "optimism")] sequencer_endpoint: Option<String>,
|
||||
discv4: Option<Discv4>,
|
||||
) -> Self {
|
||||
let inner = NetworkInner {
|
||||
@ -66,8 +65,6 @@ impl NetworkHandle {
|
||||
initial_sync_done: Arc::new(AtomicBool::new(false)),
|
||||
chain_id,
|
||||
tx_gossip_disabled,
|
||||
#[cfg(feature = "optimism")]
|
||||
sequencer_endpoint,
|
||||
discv4,
|
||||
};
|
||||
Self { inner: Arc::new(inner) }
|
||||
@ -329,11 +326,6 @@ impl NetworkInfo for NetworkHandle {
|
||||
fn is_initially_syncing(&self) -> bool {
|
||||
SyncStateProvider::is_initially_syncing(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
fn sequencer_endpoint(&self) -> Option<&str> {
|
||||
self.inner.sequencer_endpoint.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl SyncStateProvider for NetworkHandle {
|
||||
@ -391,9 +383,6 @@ struct NetworkInner {
|
||||
chain_id: Arc<AtomicU64>,
|
||||
/// Whether to disable transaction gossip
|
||||
tx_gossip_disabled: bool,
|
||||
/// The sequencer HTTP Endpoint
|
||||
#[cfg(feature = "optimism")]
|
||||
sequencer_endpoint: Option<String>,
|
||||
/// The instance of the discv4 service
|
||||
discv4: Option<Discv4>,
|
||||
}
|
||||
|
||||
@ -103,8 +103,6 @@ optimism = [
|
||||
"reth-rpc/optimism",
|
||||
"reth-rpc-engine-api/optimism",
|
||||
"reth-provider/optimism",
|
||||
"reth-network/optimism",
|
||||
"reth-network-api/optimism",
|
||||
"reth-rpc-types-compat/optimism",
|
||||
"reth-auto-seal-consensus/optimism",
|
||||
"reth-consensus-common/optimism",
|
||||
|
||||
@ -28,18 +28,29 @@ reth-network.workspace = true
|
||||
reth-revm.workspace = true
|
||||
revm.workspace = true
|
||||
|
||||
# async
|
||||
async-trait.workspace = true
|
||||
hyper.workspace = true
|
||||
http = "0.2.8"
|
||||
http-body = "0.4.5"
|
||||
reqwest = { version = "0.11", default-features = false, features = [
|
||||
"rustls-tls",
|
||||
]}
|
||||
|
||||
# misc
|
||||
clap.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
eyre.workspace = true
|
||||
parking_lot.workspace = true
|
||||
thiserror.workspace = true
|
||||
jsonrpsee.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
reth-db.workspace = true
|
||||
|
||||
[features]
|
||||
optimism = [
|
||||
"reth-network/optimism",
|
||||
"reth-primitives/optimism",
|
||||
"reth-provider/optimism",
|
||||
"reth-rpc-types-compat/optimism",
|
||||
|
||||
@ -27,6 +27,8 @@ pub use node::OptimismNode;
|
||||
|
||||
pub mod txpool;
|
||||
|
||||
pub mod rpc;
|
||||
|
||||
pub use reth_optimism_payload_builder::{
|
||||
OptimismBuiltPayload, OptimismPayloadBuilder, OptimismPayloadBuilderAttributes,
|
||||
};
|
||||
|
||||
@ -41,12 +41,12 @@ impl OptimismNode {
|
||||
where
|
||||
Node: FullNodeTypes<Engine = OptimismEngineTypes>,
|
||||
{
|
||||
let RollupArgs { sequencer_http, disable_txpool_gossip, compute_pending_block, .. } = args;
|
||||
let RollupArgs { disable_txpool_gossip, compute_pending_block, .. } = args;
|
||||
ComponentsBuilder::default()
|
||||
.node_types::<Node>()
|
||||
.pool(OptimismPoolBuilder::default())
|
||||
.payload(OptimismPayloadBuilder::new(compute_pending_block))
|
||||
.network(OptimismNetworkBuilder { sequencer_http, disable_txpool_gossip })
|
||||
.network(OptimismNetworkBuilder { disable_txpool_gossip })
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,8 +215,6 @@ where
|
||||
/// A basic optimism network builder.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct OptimismNetworkBuilder {
|
||||
/// HTTP endpoint for the sequencer mempool
|
||||
pub sequencer_http: Option<String>,
|
||||
/// Disable transaction pool gossip
|
||||
pub disable_txpool_gossip: bool,
|
||||
}
|
||||
@ -231,14 +229,13 @@ where
|
||||
ctx: &BuilderContext<Node>,
|
||||
pool: Pool,
|
||||
) -> eyre::Result<NetworkHandle> {
|
||||
let Self { sequencer_http, disable_txpool_gossip } = self;
|
||||
let Self { disable_txpool_gossip } = self;
|
||||
let mut network_config = ctx.network_config()?;
|
||||
|
||||
// When `sequencer_endpoint` is configured, the node will forward all transactions to a
|
||||
// Sequencer node for execution and inclusion on L1, and disable its own txpool
|
||||
// gossip to prevent other parties in the network from learning about them.
|
||||
network_config.tx_gossip_disabled = disable_txpool_gossip;
|
||||
network_config.optimism_network_config.sequencer_endpoint = sequencer_http;
|
||||
|
||||
let network = NetworkManager::builder(network_config).await?;
|
||||
|
||||
|
||||
124
crates/node-optimism/src/rpc.rs
Normal file
124
crates/node-optimism/src/rpc.rs
Normal file
@ -0,0 +1,124 @@
|
||||
//! Helpers for optimism specific RPC implementations.
|
||||
|
||||
use jsonrpsee::types::ErrorObject;
|
||||
use reqwest::Client;
|
||||
use reth_rpc::eth::{
|
||||
error::{EthApiError, EthResult, ToRpcError},
|
||||
traits::RawTransactionForwarder,
|
||||
};
|
||||
use reth_tracing::tracing;
|
||||
use std::sync::{atomic::AtomicUsize, Arc};
|
||||
|
||||
/// Error type when interacting with the Sequencer
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SequencerRpcError {
|
||||
/// Wrapper around a [hyper::Error].
|
||||
#[error(transparent)]
|
||||
HyperError(#[from] hyper::Error),
|
||||
/// Wrapper around an [reqwest::Error].
|
||||
#[error(transparent)]
|
||||
HttpError(#[from] reqwest::Error),
|
||||
/// Thrown when serializing transaction to forward to sequencer
|
||||
#[error("invalid sequencer transaction")]
|
||||
InvalidSequencerTransaction,
|
||||
}
|
||||
|
||||
impl ToRpcError for SequencerRpcError {
|
||||
fn to_rpc_error(&self) -> ErrorObject<'static> {
|
||||
ErrorObject::owned(
|
||||
jsonrpsee::types::error::INTERNAL_ERROR_CODE,
|
||||
self.to_string(),
|
||||
None::<String>,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SequencerRpcError> for EthApiError {
|
||||
fn from(err: SequencerRpcError) -> Self {
|
||||
EthApiError::other(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// A client to interact with a Sequencer
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SequencerClient {
|
||||
inner: Arc<SequencerClientInner>,
|
||||
}
|
||||
|
||||
impl SequencerClient {
|
||||
/// Creates a new [SequencerClient].
|
||||
pub fn new(sequencer_endpoint: impl Into<String>) -> Self {
|
||||
let client = Client::builder().use_rustls_tls().build().unwrap();
|
||||
Self::with_client(sequencer_endpoint, client)
|
||||
}
|
||||
|
||||
/// Creates a new [SequencerClient].
|
||||
pub fn with_client(sequencer_endpoint: impl Into<String>, http_client: Client) -> Self {
|
||||
let inner = SequencerClientInner {
|
||||
sequencer_endpoint: sequencer_endpoint.into(),
|
||||
http_client,
|
||||
id: AtomicUsize::new(0),
|
||||
};
|
||||
Self { inner: Arc::new(inner) }
|
||||
}
|
||||
|
||||
/// Returns the network of the client
|
||||
pub fn endpoint(&self) -> &str {
|
||||
&self.inner.sequencer_endpoint
|
||||
}
|
||||
|
||||
/// Returns the client
|
||||
pub fn http_client(&self) -> &Client {
|
||||
&self.inner.http_client
|
||||
}
|
||||
|
||||
/// Returns the next id for the request
|
||||
fn next_request_id(&self) -> usize {
|
||||
self.inner.id.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Forwards a transaction to the sequencer endpoint.
|
||||
pub async fn forward_raw_transaction(&self, tx: &[u8]) -> Result<(), SequencerRpcError> {
|
||||
let body = serde_json::to_string(&serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendRawTransaction",
|
||||
"params": [format!("0x{}", reth_primitives::hex::encode(tx))],
|
||||
"id": self.next_request_id()
|
||||
}))
|
||||
.map_err(|_| {
|
||||
tracing::warn!(
|
||||
target = "rpc::eth",
|
||||
"Failed to serialize transaction for forwarding to sequencer"
|
||||
);
|
||||
SequencerRpcError::InvalidSequencerTransaction
|
||||
})?;
|
||||
|
||||
self.http_client()
|
||||
.post(self.endpoint())
|
||||
.header(http::header::CONTENT_TYPE, "application/json")
|
||||
.body(body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(SequencerRpcError::HttpError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RawTransactionForwarder for SequencerClient {
|
||||
async fn forward_raw_transaction(&self, tx: &[u8]) -> EthResult<()> {
|
||||
SequencerClient::forward_raw_transaction(self, tx).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct SequencerClientInner {
|
||||
/// The endpoint of the sequencer
|
||||
sequencer_endpoint: String,
|
||||
/// The HTTP client
|
||||
http_client: Client,
|
||||
/// Keeps track of unique request ids
|
||||
id: AtomicUsize,
|
||||
}
|
||||
@ -87,6 +87,7 @@ where
|
||||
BlockingTaskPool::build().expect("failed to build tracing pool"),
|
||||
fee_history_cache,
|
||||
evm_config,
|
||||
None,
|
||||
);
|
||||
let config = EthFilterConfig::default()
|
||||
.max_logs_per_response(DEFAULT_MAX_LOGS_PER_RESPONSE)
|
||||
|
||||
@ -27,7 +27,7 @@ pub struct EthHandlers<Provider, Pool, Network, Events, EvmConfig> {
|
||||
pub blocking_task_pool: BlockingTaskPool,
|
||||
}
|
||||
|
||||
/// Additional config values for the eth namespace
|
||||
/// Additional config values for the eth namespace.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct EthConfig {
|
||||
/// Settings for the caching layer
|
||||
|
||||
@ -180,6 +180,7 @@ use reth_rpc::{
|
||||
cache::{cache_new_blocks_task, EthStateCache},
|
||||
fee_history_cache_new_blocks_task,
|
||||
gas_oracle::GasPriceOracle,
|
||||
traits::RawTransactionForwarder,
|
||||
EthBundle, FeeHistoryCache,
|
||||
},
|
||||
AdminApi, AuthLayer, Claims, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub,
|
||||
@ -198,6 +199,7 @@ use std::{
|
||||
fmt,
|
||||
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use strum::{AsRefStr, EnumIter, IntoStaticStr, ParseError, VariantArray, VariantNames};
|
||||
@ -949,6 +951,9 @@ pub struct RethModuleRegistry<Provider, Pool, Network, Tasks, Events, EvmConfig>
|
||||
blocking_pool_guard: BlockingTaskGuard,
|
||||
/// Contains the [Methods] of a module
|
||||
modules: HashMap<RethRpcModule, Methods>,
|
||||
/// Optional forwarder for `eth_sendRawTransaction`
|
||||
// TODO(mattsse): find a more ergonomic way to configure eth/rpc customizations
|
||||
eth_raw_transaction_forwarder: Option<Arc<dyn RawTransactionForwarder>>,
|
||||
}
|
||||
|
||||
// === impl RethModuleRegistry ===
|
||||
@ -977,9 +982,20 @@ impl<Provider, Pool, Network, Tasks, Events, EvmConfig>
|
||||
blocking_pool_guard: BlockingTaskGuard::new(config.eth.max_tracing_requests),
|
||||
config,
|
||||
events,
|
||||
eth_raw_transaction_forwarder: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a forwarder for `eth_sendRawTransaction`
|
||||
///
|
||||
/// Note: this might be removed in the future in favor of a more generic approach.
|
||||
pub fn set_eth_raw_transaction_forwarder(
|
||||
&mut self,
|
||||
forwarder: Arc<dyn RawTransactionForwarder>,
|
||||
) {
|
||||
self.eth_raw_transaction_forwarder = Some(forwarder);
|
||||
}
|
||||
|
||||
/// Returns a reference to the pool
|
||||
pub fn pool(&self) -> &Pool {
|
||||
&self.pool
|
||||
@ -1331,6 +1347,7 @@ where
|
||||
blocking_task_pool.clone(),
|
||||
fee_history_cache,
|
||||
self.evm_config.clone(),
|
||||
self.eth_raw_transaction_forwarder.clone(),
|
||||
);
|
||||
let filter = EthFilter::new(
|
||||
self.provider.clone(),
|
||||
|
||||
@ -47,11 +47,6 @@ http-body = "0.4.5"
|
||||
hyper.workspace = true
|
||||
jsonwebtoken = "8"
|
||||
|
||||
## required for optimism sequencer delegation
|
||||
reqwest = { version = "0.11", default-features = false, features = [
|
||||
"rustls-tls",
|
||||
], optional = true }
|
||||
|
||||
# async
|
||||
async-trait.workspace = true
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
@ -90,9 +85,7 @@ reth-interfaces = { workspace = true, features = ["test-utils"] }
|
||||
|
||||
[features]
|
||||
optimism = [
|
||||
"dep:reqwest",
|
||||
"reth-primitives/optimism",
|
||||
"reth-rpc-types-compat/optimism",
|
||||
"reth-network-api/optimism",
|
||||
"reth-provider/optimism",
|
||||
]
|
||||
|
||||
@ -49,6 +49,7 @@ mod sign;
|
||||
mod state;
|
||||
mod transactions;
|
||||
|
||||
use crate::eth::traits::RawTransactionForwarder;
|
||||
pub use transactions::{EthTransactions, TransactionSource};
|
||||
|
||||
/// `Eth` API trait.
|
||||
@ -104,6 +105,7 @@ where
|
||||
blocking_task_pool: BlockingTaskPool,
|
||||
fee_history_cache: FeeHistoryCache,
|
||||
evm_config: EvmConfig,
|
||||
raw_transaction_forwarder: Option<Arc<dyn RawTransactionForwarder>>,
|
||||
) -> Self {
|
||||
Self::with_spawner(
|
||||
provider,
|
||||
@ -116,6 +118,7 @@ where
|
||||
blocking_task_pool,
|
||||
fee_history_cache,
|
||||
evm_config,
|
||||
raw_transaction_forwarder,
|
||||
)
|
||||
}
|
||||
|
||||
@ -132,6 +135,7 @@ where
|
||||
blocking_task_pool: BlockingTaskPool,
|
||||
fee_history_cache: FeeHistoryCache,
|
||||
evm_config: EvmConfig,
|
||||
raw_transaction_forwarder: Option<Arc<dyn RawTransactionForwarder>>,
|
||||
) -> Self {
|
||||
// get the block number of the latest block
|
||||
let latest_block = provider
|
||||
@ -155,8 +159,7 @@ where
|
||||
blocking_task_pool,
|
||||
fee_history_cache,
|
||||
evm_config,
|
||||
#[cfg(feature = "optimism")]
|
||||
http_client: reqwest::Client::builder().use_rustls_tls().build().unwrap(),
|
||||
raw_transaction_forwarder,
|
||||
};
|
||||
|
||||
Self { inner: Arc::new(inner) }
|
||||
@ -486,7 +489,6 @@ struct EthApiInner<Provider, Pool, Network, EvmConfig> {
|
||||
fee_history_cache: FeeHistoryCache,
|
||||
/// The type that defines how to configure the EVM
|
||||
evm_config: EvmConfig,
|
||||
/// An http client for communicating with sequencers.
|
||||
#[cfg(feature = "optimism")]
|
||||
http_client: reqwest::Client,
|
||||
/// Allows forwarding received raw transactions
|
||||
raw_transaction_forwarder: Option<Arc<dyn RawTransactionForwarder>>,
|
||||
}
|
||||
|
||||
@ -486,6 +486,7 @@ mod tests {
|
||||
BlockingTaskPool::build().expect("failed to build tracing pool"),
|
||||
fee_history_cache,
|
||||
evm_config,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -145,6 +145,7 @@ mod tests {
|
||||
BlockingTaskPool::build().expect("failed to build tracing pool"),
|
||||
FeeHistoryCache::new(cache, FeeHistoryCacheConfig::default()),
|
||||
evm_config,
|
||||
None,
|
||||
);
|
||||
let address = Address::random();
|
||||
let storage = eth_api.storage_at(address, U256::ZERO.into(), None).unwrap();
|
||||
@ -169,6 +170,7 @@ mod tests {
|
||||
BlockingTaskPool::build().expect("failed to build tracing pool"),
|
||||
FeeHistoryCache::new(cache, FeeHistoryCacheConfig::default()),
|
||||
evm_config,
|
||||
None,
|
||||
);
|
||||
|
||||
let storage_key: U256 = storage_key.into();
|
||||
|
||||
@ -848,8 +848,10 @@ where
|
||||
async fn send_raw_transaction(&self, tx: Bytes) -> EthResult<B256> {
|
||||
// On optimism, transactions are forwarded directly to the sequencer to be included in
|
||||
// blocks that it builds.
|
||||
#[cfg(feature = "optimism")]
|
||||
self.forward_to_sequencer(&tx).await?;
|
||||
if let Some(client) = self.inner.raw_transaction_forwarder.as_ref() {
|
||||
tracing::debug!( target: "rpc::eth", "forwarding raw transaction to");
|
||||
client.forward_raw_transaction(&tx).await?;
|
||||
}
|
||||
|
||||
let recovered = recover_raw_transaction(tx)?;
|
||||
let pool_transaction = <Pool::Transaction>::from_recovered_pooled_transaction(recovered);
|
||||
@ -1506,39 +1508,6 @@ where
|
||||
|
||||
Ok(OptimismTxMeta::new(Some(l1_block_info), l1_fee, l1_data_gas))
|
||||
}
|
||||
|
||||
/// Helper function for `eth_sendRawTransaction` for Optimism.
|
||||
///
|
||||
/// Forwards the raw transaction bytes to the configured sequencer endpoint.
|
||||
/// This is a no-op if the sequencer endpoint is not configured.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub async fn forward_to_sequencer(&self, tx: &Bytes) -> EthResult<()> {
|
||||
if let Some(endpoint) = self.network().sequencer_endpoint() {
|
||||
let body = serde_json::to_string(&serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendRawTransaction",
|
||||
"params": [format!("0x{}", alloy_primitives::hex::encode(tx))],
|
||||
"id": self.network().chain_id()
|
||||
}))
|
||||
.map_err(|_| {
|
||||
tracing::warn!(
|
||||
target = "rpc::eth",
|
||||
"Failed to serialize transaction for forwarding to sequencer"
|
||||
);
|
||||
OptimismEthApiError::InvalidSequencerTransaction
|
||||
})?;
|
||||
|
||||
self.inner
|
||||
.http_client
|
||||
.post(endpoint)
|
||||
.header(http::header::CONTENT_TYPE, "application/json")
|
||||
.body(body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(OptimismEthApiError::HttpError)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Provider, Pool, Network, EvmConfig> EthApi<Provider, Pool, Network, EvmConfig>
|
||||
@ -1843,6 +1812,7 @@ mod tests {
|
||||
BlockingTaskPool::build().expect("failed to build tracing pool"),
|
||||
fee_history_cache,
|
||||
evm_config,
|
||||
None,
|
||||
);
|
||||
|
||||
// https://etherscan.io/tx/0xa694b71e6c128a2ed8e2e0f6770bddbe52e3bb8f10e8472f9a79ab81497a8b5d
|
||||
|
||||
@ -11,6 +11,7 @@ mod logs_utils;
|
||||
mod pubsub;
|
||||
pub mod revm_utils;
|
||||
mod signer;
|
||||
pub mod traits;
|
||||
pub(crate) mod utils;
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
|
||||
@ -1,25 +1,16 @@
|
||||
//! Optimism specific types.
|
||||
|
||||
use jsonrpsee::types::ErrorObject;
|
||||
|
||||
use crate::{
|
||||
eth::error::{EthApiError, ToRpcError},
|
||||
result::internal_rpc_err,
|
||||
};
|
||||
use jsonrpsee::types::ErrorObject;
|
||||
use reqwest::Client;
|
||||
|
||||
/// Eth Optimism Api Error
|
||||
#[cfg(feature = "optimism")]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum OptimismEthApiError {
|
||||
/// Wrapper around a [hyper::Error].
|
||||
#[error(transparent)]
|
||||
HyperError(#[from] hyper::Error),
|
||||
/// Wrapper around an [reqwest::Error].
|
||||
#[error(transparent)]
|
||||
HttpError(#[from] reqwest::Error),
|
||||
/// Thrown when serializing transaction to forward to sequencer
|
||||
#[error("invalid sequencer transaction")]
|
||||
InvalidSequencerTransaction,
|
||||
/// Thrown when calculating L1 gas fee
|
||||
#[error("failed to calculate l1 gas fee")]
|
||||
L1BlockFeeError,
|
||||
@ -31,11 +22,9 @@ pub enum OptimismEthApiError {
|
||||
impl ToRpcError for OptimismEthApiError {
|
||||
fn to_rpc_error(&self) -> ErrorObject<'static> {
|
||||
match self {
|
||||
OptimismEthApiError::HyperError(err) => internal_rpc_err(err.to_string()),
|
||||
OptimismEthApiError::HttpError(err) => internal_rpc_err(err.to_string()),
|
||||
OptimismEthApiError::InvalidSequencerTransaction |
|
||||
OptimismEthApiError::L1BlockFeeError |
|
||||
OptimismEthApiError::L1BlockGasError => internal_rpc_err(self.to_string()),
|
||||
OptimismEthApiError::L1BlockFeeError | OptimismEthApiError::L1BlockGasError => {
|
||||
internal_rpc_err(self.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,19 +34,3 @@ impl From<OptimismEthApiError> for EthApiError {
|
||||
EthApiError::other(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// A client to interact with a Sequencer
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct SequencerClient {
|
||||
/// The endpoint of the sequencer
|
||||
pub sequencer_endpoint: String,
|
||||
/// The HTTP client
|
||||
pub http_client: Client,
|
||||
}
|
||||
|
||||
impl SequencerClient {
|
||||
/// Creates a new [SequencerClient].
|
||||
pub fn new(sequencer_endpoint: impl Into<String>, http_client: Client) -> Self {
|
||||
Self { sequencer_endpoint: sequencer_endpoint.into(), http_client }
|
||||
}
|
||||
}
|
||||
|
||||
13
crates/rpc/rpc/src/eth/traits.rs
Normal file
13
crates/rpc/rpc/src/eth/traits.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! Additional helper traits that allow for more customization.
|
||||
|
||||
use crate::eth::error::EthResult;
|
||||
use std::fmt;
|
||||
|
||||
/// A trait that allows for forwarding raw transactions.
|
||||
///
|
||||
/// For example to a sequencer.
|
||||
#[async_trait::async_trait]
|
||||
pub trait RawTransactionForwarder: fmt::Debug + Send + Sync + 'static {
|
||||
/// Forwards raw transaction bytes for `eth_sendRawTransaction`
|
||||
async fn forward_raw_transaction(&self, raw: &[u8]) -> EthResult<()>;
|
||||
}
|
||||
@ -159,7 +159,6 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use http::{header, Method, Request, StatusCode};
|
||||
use hyper::{body, Body};
|
||||
use jsonrpsee::{
|
||||
|
||||
Reference in New Issue
Block a user