diff --git a/Cargo.lock b/Cargo.lock
index 8fc594c9d5..8cff6fecaf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1931,26 +1931,26 @@ dependencies = [
[[package]]
name = "jsonrpsee"
-version = "0.15.1"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bd0d559d5e679b1ab2f869b486a11182923863b1b3ee8b421763cdd707b783a"
+checksum = "e0ee76536f6a303b67c13a99ecae0002bb388674dbf416094dde808263ea229c"
dependencies = [
+ "jsonrpsee-client-transport",
"jsonrpsee-core",
"jsonrpsee-http-client",
- "jsonrpsee-http-server",
"jsonrpsee-proc-macros",
+ "jsonrpsee-server",
"jsonrpsee-types",
"jsonrpsee-wasm-client",
"jsonrpsee-ws-client",
- "jsonrpsee-ws-server",
"tracing",
]
[[package]]
name = "jsonrpsee-client-transport"
-version = "0.15.1"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8752740ecd374bcbf8b69f3e80b0327942df76f793f8d4e60d3355650c31fb74"
+checksum = "74c8f8f21b684623d23be8b6fcb101594f4e95d8a505ffd0568de863d93668f4"
dependencies = [
"anyhow",
"futures-channel",
@@ -1973,9 +1973,9 @@ dependencies = [
[[package]]
name = "jsonrpsee-core"
-version = "0.15.1"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3dc3e9cf2ba50b7b1d7d76a667619f82846caa39e8e8daa8a4962d74acaddca"
+checksum = "f57020f4c98b6c6e8848fb115e61227fba6993517bee0faa38e4db627a9f7254"
dependencies = [
"anyhow",
"arrayvec",
@@ -1986,10 +1986,8 @@ dependencies = [
"futures-timer",
"futures-util",
"globset",
- "http",
"hyper",
"jsonrpsee-types",
- "lazy_static",
"parking_lot 0.12.1",
"rand 0.8.5",
"rustc-hash",
@@ -1999,16 +1997,14 @@ dependencies = [
"thiserror",
"tokio",
"tracing",
- "tracing-futures",
- "unicase",
"wasm-bindgen-futures",
]
[[package]]
name = "jsonrpsee-http-client"
-version = "0.15.1"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52f7c0e2333ab2115c302eeb4f137c8a4af5ab609762df68bbda8f06496677c9"
+checksum = "7ca71086fd13ad0991cd4a0e50c9f4c59488b1acfac4a528c448c2e10020aa1e"
dependencies = [
"async-trait",
"hyper",
@@ -2021,33 +2017,15 @@ dependencies = [
"thiserror",
"tokio",
"tracing",
- "tracing-futures",
-]
-
-[[package]]
-name = "jsonrpsee-http-server"
-version = "0.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03802f0373a38c2420c70b5144742d800b509e2937edc4afb116434f07120117"
-dependencies = [
- "futures-channel",
- "futures-util",
- "hyper",
- "jsonrpsee-core",
- "jsonrpsee-types",
- "serde",
- "serde_json",
- "tokio",
- "tracing",
- "tracing-futures",
]
[[package]]
name = "jsonrpsee-proc-macros"
-version = "0.15.1"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd67957d4280217247588ac86614ead007b301ca2fa9f19c19f880a536f029e3"
+checksum = "d335519bfe970511318f2780b7716f91d99d67fbf32ac3ea94b5f2f6c9818a4d"
dependencies = [
+ "heck",
"proc-macro-crate",
"proc-macro2",
"quote",
@@ -2055,10 +2033,32 @@ dependencies = [
]
[[package]]
-name = "jsonrpsee-types"
-version = "0.15.1"
+name = "jsonrpsee-server"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e290bba767401b646812f608c099b922d8142603c9e73a50fb192d3ac86f4a0d"
+checksum = "ff5de9e3d6280c5354882e001494bc6ffb3ea31ac7dd81440f997aa380039e39"
+dependencies = [
+ "futures-channel",
+ "futures-util",
+ "http",
+ "hyper",
+ "jsonrpsee-core",
+ "jsonrpsee-types",
+ "serde",
+ "serde_json",
+ "soketto",
+ "tokio",
+ "tokio-stream",
+ "tokio-util 0.7.4",
+ "tower",
+ "tracing",
+]
+
+[[package]]
+name = "jsonrpsee-types"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88c88c764104fef883eb8a832d0d77688a63f67d75b385f5cdae7b3db8925288"
dependencies = [
"anyhow",
"beef",
@@ -2070,9 +2070,9 @@ dependencies = [
[[package]]
name = "jsonrpsee-wasm-client"
-version = "0.15.1"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597b4eb94730e7695d0a2a429bc37a12e6e84d12680fdafb9b8f5f53652aab57"
+checksum = "d11a058951524f3f6e02e94c26d5c189a5df0f2dea81339147c603b9eb7c511d"
dependencies = [
"jsonrpsee-client-transport",
"jsonrpsee-core",
@@ -2081,9 +2081,9 @@ dependencies = [
[[package]]
name = "jsonrpsee-ws-client"
-version = "0.15.1"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ee5feddd5188e62ac08fcf0e56478138e581509d4730f3f7be9b57dd402a4ff"
+checksum = "ea609539b5062f856a43652fd01d8ed8df40cd4d7067be6f6b7ce81d8bbd03be"
dependencies = [
"http",
"jsonrpsee-client-transport",
@@ -2091,26 +2091,6 @@ dependencies = [
"jsonrpsee-types",
]
-[[package]]
-name = "jsonrpsee-ws-server"
-version = "0.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d488ba74fb369e5ab68926feb75a483458b88e768d44319f37e4ecad283c7325"
-dependencies = [
- "futures-channel",
- "futures-util",
- "http",
- "jsonrpsee-core",
- "jsonrpsee-types",
- "serde_json",
- "soketto",
- "tokio",
- "tokio-stream",
- "tokio-util 0.7.4",
- "tracing",
- "tracing-futures",
-]
-
[[package]]
name = "k256"
version = "0.11.6"
@@ -2544,6 +2524,20 @@ dependencies = [
"syn",
]
+[[package]]
+name = "parity-tokio-ipc"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6"
+dependencies = [
+ "futures",
+ "libc",
+ "log",
+ "rand 0.7.3",
+ "tokio",
+ "winapi",
+]
+
[[package]]
name = "parking"
version = "2.0.0"
@@ -3250,6 +3244,25 @@ dependencies = [
"tokio-stream",
]
+[[package]]
+name = "reth-ipc"
+version = "0.1.0"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures",
+ "jsonrpsee",
+ "parity-tokio-ipc",
+ "pin-project",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tokio-util 0.7.4",
+ "tower",
+ "tracing",
+ "tracing-test",
+]
+
[[package]]
name = "reth-libmdbx"
version = "0.1.6"
@@ -4023,6 +4036,7 @@ dependencies = [
"base64",
"bytes",
"futures",
+ "http",
"httparse",
"log",
"rand 0.8.5",
@@ -4440,6 +4454,23 @@ dependencies = [
"walkdir",
]
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
[[package]]
name = "tower-service"
version = "0.3.2"
@@ -4629,15 +4660,6 @@ dependencies = [
"static_assertions",
]
-[[package]]
-name = "unicase"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
-dependencies = [
- "version_check",
-]
-
[[package]]
name = "unicode-bidi"
version = "0.3.8"
diff --git a/Cargo.toml b/Cargo.toml
index 1036ccc0ab..d1fa725587 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ members = [
"crates/net/eth-wire",
"crates/net/discv4",
"crates/net/network",
+ "crates/net/ipc",
"crates/net/rpc",
"crates/net/rpc-api",
"crates/net/rpc-types",
diff --git a/crates/net/ipc/Cargo.toml b/crates/net/ipc/Cargo.toml
new file mode 100644
index 0000000000..490d04abe4
--- /dev/null
+++ b/crates/net/ipc/Cargo.toml
@@ -0,0 +1,34 @@
+[package]
+name = "reth-ipc"
+version = "0.1.0"
+edition = "2021"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/foundry-rs/reth"
+readme = "README.md"
+description = """
+IPC support for reth
+"""
+
+[dependencies]
+
+# async/net
+futures = "0.3"
+parity-tokio-ipc = "0.9.0"
+tokio = { version = "1", features = ["net", "time", "rt-multi-thread"] }
+tokio-util = { version = "0.7", features = ["codec"] }
+async-trait = "0.1"
+pin-project = "1.0"
+tower = "0.4"
+
+# misc
+jsonrpsee = { version = "0.16", features = ["server", "client"] }
+serde_json = "1.0"
+tracing = "0.1.37"
+bytes = "1.2.1"
+thiserror = "1.0.37"
+
+[dev-dependencies]
+tracing-test = "0.2"
+
+[features]
+client = ["jsonrpsee/client", "jsonrpsee/async-client"]
diff --git a/crates/net/ipc/README.md b/crates/net/ipc/README.md
new file mode 100644
index 0000000000..b65fe8ae90
--- /dev/null
+++ b/crates/net/ipc/README.md
@@ -0,0 +1,3 @@
+#
reth-ipc
+
+IPC server and client implementation for [`jsonrpsee`](https://github.com/paritytech/jsonrpsee/).
\ No newline at end of file
diff --git a/crates/net/ipc/src/client.rs b/crates/net/ipc/src/client.rs
new file mode 100644
index 0000000000..a04bdfa23d
--- /dev/null
+++ b/crates/net/ipc/src/client.rs
@@ -0,0 +1,151 @@
+//! [`jsonrpsee`] transport adapter implementation for IPC.
+
+use crate::stream_codec::StreamCodec;
+use futures::StreamExt;
+use jsonrpsee::{
+ async_client::{Client, ClientBuilder},
+ core::client::{ReceivedMessage, TransportReceiverT, TransportSenderT},
+};
+use std::{
+ io,
+ path::{Path, PathBuf},
+};
+use tokio::{io::AsyncWriteExt, net::UnixStream};
+use tokio_util::codec::FramedRead;
+
+/// Builder type for [`Client`]
+#[derive(Clone, Default, Debug)]
+#[non_exhaustive]
+pub struct IpcClientBuilder;
+
+impl IpcClientBuilder {
+ /// Connects to a IPC socket
+ pub async fn build(self, path: impl AsRef) -> Result {
+ let (tx, rx) = IpcTransportClientBuilder::default().build(path).await?;
+ Ok(self.build_with_tokio(tx, rx))
+ }
+
+ /// Uses the sender and receiver channels to connect to the socket.
+ pub fn build_with_tokio(self, sender: S, receiver: R) -> Client
+ where
+ S: TransportSenderT + Send,
+ R: TransportReceiverT + Send,
+ {
+ ClientBuilder::default().build_with_tokio(sender, receiver)
+ }
+}
+
+/// Sending end of IPC transport.
+#[derive(Debug)]
+pub struct Sender {
+ inner: tokio::net::unix::OwnedWriteHalf,
+}
+
+#[async_trait::async_trait]
+impl TransportSenderT for Sender {
+ type Error = IpcError;
+
+ /// Sends out a request. Returns a Future that finishes when the request has been successfully
+ /// sent.
+ async fn send(&mut self, msg: String) -> Result<(), Self::Error> {
+ Ok(self.inner.write_all(msg.as_bytes()).await?)
+ }
+
+ async fn send_ping(&mut self) -> Result<(), Self::Error> {
+ tracing::trace!("send ping - not implemented");
+ Err(IpcError::NotSupported)
+ }
+
+ /// Close the connection.
+ async fn close(&mut self) -> Result<(), Self::Error> {
+ Ok(())
+ }
+}
+
+/// Receiving end of IPC transport.
+#[derive(Debug)]
+pub struct Receiver {
+ inner: FramedRead,
+}
+
+#[async_trait::async_trait]
+impl TransportReceiverT for Receiver {
+ type Error = IpcError;
+
+ /// Returns a Future resolving when the server sent us something back.
+ async fn receive(&mut self) -> Result {
+ match self.inner.next().await {
+ None => Err(IpcError::Closed),
+ Some(val) => Ok(ReceivedMessage::Text(val?)),
+ }
+ }
+}
+
+/// Builder for IPC transport [`Sender`] and ['Receiver`] pair.
+#[derive(Debug, Clone, Default)]
+#[non_exhaustive]
+pub struct IpcTransportClientBuilder;
+
+impl IpcTransportClientBuilder {
+ /// Try to establish the connection.
+ ///
+ /// ```
+ /// use jsonrpsee::rpc_params;
+ /// use reth_ipc::client::IpcClientBuilder;
+ /// use jsonrpsee::core::client::ClientT;
+ /// # async fn run_client() -> Result<(), Box> {
+ /// let client = IpcClientBuilder::default().build("/tmp/my-uds").await?;
+ /// let response: String = client.request("say_hello", rpc_params![]).await?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub async fn build(self, path: impl AsRef) -> Result<(Sender, Receiver), IpcError> {
+ let path = path.as_ref();
+ let stream = UnixStream::connect(path)
+ .await
+ .map_err(|err| IpcError::FailedToConnect { path: path.to_path_buf(), err })?;
+
+ let (rhlf, whlf) = stream.into_split();
+
+ Ok((
+ Sender { inner: whlf },
+ Receiver { inner: FramedRead::new(rhlf, StreamCodec::stream_incoming()) },
+ ))
+ }
+}
+
+/// Error variants that can happen in IPC transport.
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum IpcError {
+ /// Operation not supported
+ #[error("Operation not supported")]
+ NotSupported,
+ /// Stream was closed
+ #[error("Stream closed")]
+ Closed,
+ /// Thrown when failed to establish a socket connection.
+ #[error("Failed to connect to socket {path}: {err}")]
+ FailedToConnect {
+ /// The path of the socket.
+ path: PathBuf,
+ err: io::Error,
+ },
+ #[error(transparent)]
+ Io(#[from] io::Error),
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use parity_tokio_ipc::{dummy_endpoint, Endpoint};
+
+ #[tokio::test]
+ async fn test_connect() {
+ let endpoint = dummy_endpoint();
+ let _incoming = Endpoint::new(endpoint.clone()).incoming().unwrap();
+
+ let (tx, rx) = IpcTransportClientBuilder::default().build(endpoint).await.unwrap();
+ let _ = IpcClientBuilder::default().build_with_tokio(tx, rx);
+ }
+}
diff --git a/crates/net/ipc/src/lib.rs b/crates/net/ipc/src/lib.rs
new file mode 100644
index 0000000000..f8d4086119
--- /dev/null
+++ b/crates/net/ipc/src/lib.rs
@@ -0,0 +1,14 @@
+#![warn(missing_debug_implementations, missing_docs, unreachable_pub, unused_crate_dependencies)]
+#![deny(unused_must_use, rust_2018_idioms)]
+#![doc(test(
+ no_crate_inject,
+ attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
+))]
+
+//! Reth IPC implementation
+
+pub mod client;
+pub mod server;
+
+/// Json codec implementation
+pub mod stream_codec;
diff --git a/crates/net/ipc/src/server/connection.rs b/crates/net/ipc/src/server/connection.rs
new file mode 100644
index 0000000000..ff0bd4c00a
--- /dev/null
+++ b/crates/net/ipc/src/server/connection.rs
@@ -0,0 +1,115 @@
+//! A IPC connection.
+
+use crate::stream_codec::StreamCodec;
+use futures::{ready, Sink, Stream, StreamExt};
+use std::{
+ io,
+ marker::PhantomData,
+ pin::Pin,
+ task::{Context, Poll},
+};
+use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
+use tokio_util::codec::Framed;
+
+pub(crate) type JsonRpcStream = Framed;
+
+/// Wraps a stream of incoming connections.
+#[pin_project::pin_project]
+pub(crate) struct Incoming {
+ #[pin]
+ inner: T,
+ _marker: PhantomData- ,
+}
+impl Incoming
+where
+ T: Stream
- > + Unpin + 'static,
+ Item: AsyncRead + AsyncWrite,
+{
+ /// Create a new instance.
+ pub(crate) fn new(inner: T) -> Self {
+ Self { inner, _marker: Default::default() }
+ }
+
+ /// Polls to accept a new incoming connection to the endpoint.
+ pub(crate) fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll<::Item> {
+ let res = match ready!(self.poll_next_unpin(cx)) {
+ None => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "ipc connection closed")),
+ Some(conn) => conn,
+ };
+ Poll::Ready(res)
+ }
+}
+
+impl Stream for Incoming
+where
+ T: Stream
- > + 'static,
+ Item: AsyncRead + AsyncWrite,
+{
+ type Item = io::Result>>;
+
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll