feat: extract optimism sequencer client to node-optimism (#7482)

This commit is contained in:
Matthias Seitz
2024-04-05 23:35:02 +02:00
committed by GitHub
parent 71c404d9cb
commit b89af430e2
27 changed files with 221 additions and 163 deletions

View File

@ -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)

View File

@ -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

View File

@ -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(),

View File

@ -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",
]

View File

@ -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>>,
}

View File

@ -486,6 +486,7 @@ mod tests {
BlockingTaskPool::build().expect("failed to build tracing pool"),
fee_history_cache,
evm_config,
None,
)
}

View File

@ -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();

View File

@ -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

View File

@ -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")]

View File

@ -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 }
}
}

View 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<()>;
}

View File

@ -159,7 +159,6 @@ where
#[cfg(test)]
mod tests {
use http::{header, Method, Request, StatusCode};
use hyper::{body, Body};
use jsonrpsee::{