Skip to content

Commit

Permalink
feat(rpc): port over sequencer forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
refcell committed Aug 26, 2024
1 parent 4290732 commit 99a7313
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 6 deletions.
43 changes: 43 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ tokio = { version = "1.21", default-features = false }

# rpc
jsonrpsee = { version = "0.24", features = ["jsonrpsee-core", "client-core", "server-core", "macros"] }
jsonrpsee-types = "0.24"

# Reth
reth = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
Expand All @@ -153,13 +154,20 @@ reth-provider = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5"
reth-revm = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-evm = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-tracing = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-rpc-types = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }


# Misc
tracing = "0.1.0"
eyre = "0.6.12"
clap = "4"
async-trait = "0.1.81"
url = "2.5.2"
eyre = "0.6.12"
tracing = "0.1.0"
thiserror = "1.0"
hashbrown = "0.14.5"
async-trait = "0.1.81"
parking_lot = "0.12.3"
serde_json = "1.0.94"
reqwest = { version = "0.12", features = ["rustls-tls-native-roots"] }
rand = { version = "0.8.3", features = ["small_rng"], default-features = false }
url = "2.5.2"
23 changes: 23 additions & 0 deletions crates/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,26 @@ alloy-eips.workspace = true
tracing.workspace = true
jsonrpsee.workspace = true
async-trait.workspace = true

# `reth` feature flag dependencies
alloy = { workspace = true, optional = true }
reqwest = { workspace = true, optional = true }
thiserror = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
jsonrpsee-types = { workspace = true, optional = true }
reth-rpc-types = { workspace = true, optional = true }
reth-rpc-eth-types = { workspace = true, optional = true }
reth-rpc-eth-api = { workspace = true, optional = true }

[features]
default = ["reth"]
reth = [
"dep:alloy",
"dep:reqwest",
"dep:serde_json",
"dep:thiserror",
"dep:jsonrpsee-types",
"dep:reth-rpc-types",
"dep:reth-rpc-eth-types",
"dep:reth-rpc-eth-api",
]
5 changes: 4 additions & 1 deletion crates/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]

pub mod rpc;
pub mod rollup;

#[cfg(feature = "reth")]
pub mod sequencer;
2 changes: 1 addition & 1 deletion crates/rpc/src/rpc.rs → crates/rpc/src/rollup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use op_alloy_rpc_jsonrpsee::traits::RollupNodeServer;
use op_alloy_rpc_types::{config::RollupConfig, output::OutputResponse, sync::SyncStatus};
use tracing::trace;

/// An implementation of the [`RollupNode`] trait.
/// An implementation of the [`RollupNodeServer`] trait.
#[derive(Debug, Clone)]
pub struct RollupNodeRpc {
/// The version of the node.
Expand Down
123 changes: 123 additions & 0 deletions crates/rpc/src/sequencer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//! Optimism reth RPC Extension used to forward raw transactions to the sequencer.
use std::sync::{atomic::AtomicUsize, Arc};

use jsonrpsee_types::error::{ErrorObject, INTERNAL_ERROR_CODE};
use reqwest::Client;
use reth_rpc_eth_api::RawTransactionForwarder;
use reth_rpc_eth_types::error::{EthApiError, EthResult};
use reth_rpc_types::ToRpcError;

/// Error type when interacting with the Sequencer
#[derive(Debug, thiserror::Error)]
pub enum SequencerRpcError {
/// 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(INTERNAL_ERROR_CODE, self.to_string(), None::<String>)
}
}

impl From<SequencerRpcError> for EthApiError {
fn from(err: SequencerRpcError) -> Self {
Self::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{}", alloy::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(reqwest::header::CONTENT_TYPE, "application/json")
.body(body)
.send()
.await
.inspect_err(|err| {
tracing::warn!(
target = "rpc::eth",
%err,
"Failed to forward transaction to sequencer",
);
})
.map_err(SequencerRpcError::HttpError)?;

Ok(())
}
}

#[async_trait::async_trait]
impl RawTransactionForwarder for SequencerClient {
async fn forward_raw_transaction(&self, tx: &[u8]) -> EthResult<()> {
Self::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,
}

0 comments on commit 99a7313

Please sign in to comment.