Skip to content

Commit

Permalink
feature: BoxTransport
Browse files Browse the repository at this point in the history
  • Loading branch information
prestwich committed Aug 12, 2023
1 parent e204269 commit 7a9726e
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 35 deletions.
2 changes: 2 additions & 0 deletions crates/middleware/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ exclude.workspace = true
[dependencies]
alloy-json-rpc.workspace = true
alloy-networks.workspace = true
alloy-primitives.workspace = true
alloy-transports.workspace = true
futures-util = "0.3.28"
72 changes: 61 additions & 11 deletions crates/middleware/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,57 @@
use std::{future::Future, pin::Pin};

use alloy_json_rpc::RpcResult;
use alloy_networks::Network;
use alloy_networks::{Network, Transaction};
use alloy_transports::{BoxTransport, RpcClient, Transport, TransportError};

use std::{future::Future, pin::Pin};

pub type MwareFut<'a, T, E> = Pin<Box<dyn Future<Output = RpcResult<T, E>> + Send + 'a>>;

pub trait Middleware<N: Network, T: Transport = BoxTransport> {
/// Middleware is parameterized with a network and a transport. The default
/// transport is type-erased, but you can do `Middleware<N, Http>`.
pub trait Middleware<N: Network, T: Transport = BoxTransport>: Send + Sync {
fn client(&self) -> &RpcClient<T>;

/// Return a reference to the inner Middleware.
///
/// Middleware are object safe now :)
fn inner(&self) -> &dyn Middleware<N, T>;

fn send_transaction<'a>(
&self,
fn estimate_gas<'s: 'fut, 'a: 'fut, 'fut>(
&'s self,
tx: &'a N::TransactionRequest,
) -> MwareFut<'a, N::Receipt, TransportError> {
) -> MwareFut<'fut, alloy_primitives::U256, TransportError>
where
Self: Sync + 'fut,
{
self.inner().estimate_gas(tx)
}

/// Send a transaction to the network.
///
/// The transaction type is defined by the network.
fn send_transaction<'s: 'fut, 'a: 'fut, 'fut>(
&'s self,
tx: &'a N::TransactionRequest,
) -> MwareFut<'fut, N::Receipt, TransportError> {
self.inner().send_transaction(tx)
}

fn populate_gas<'s: 'fut, 'a: 'fut, 'fut>(
&'s self,
tx: &'a mut N::TransactionRequest,
) -> MwareFut<'fut, (), TransportError>
where
Self: Sync,
{
Box::pin(async move {
let gas = self.estimate_gas(&*tx).await;

gas.map(|gas| tx.set_gas(gas))
})
}
}

impl<N: Network, T: Transport> Middleware<N, T> for RpcClient<T> {
impl<N: Network, T: Transport + Clone> Middleware<N, T> for RpcClient<T> {
fn client(&self) -> &RpcClient<T> {
self
}
Expand All @@ -28,14 +60,22 @@ impl<N: Network, T: Transport> Middleware<N, T> for RpcClient<T> {
panic!("called inner on <RpcClient as Middleware>")
}

fn send_transaction<'a>(
&self,
fn estimate_gas<'s: 'fut, 'a: 'fut, 'fut>(
&'s self,
tx: &'a <N as Network>::TransactionRequest,
) -> MwareFut<'fut, alloy_primitives::U256, TransportError> {
self.prepare("eth_estimateGas", tx).box_pin()
}

fn send_transaction<'s: 'fut, 'a: 'fut, 'fut>(
&'s self,
tx: &'a N::TransactionRequest,
) -> MwareFut<'a, N::Receipt, TransportError> {
) -> MwareFut<'fut, N::Receipt, TransportError> {
self.prepare("eth_sendTransaction", tx).box_pin()
}
}

/// Middleware use a tower-like Layer abstraction
pub trait MwareLayer<N: Network> {
type Middleware<T: Transport>: Middleware<N, T>;

Expand All @@ -44,3 +84,13 @@ pub trait MwareLayer<N: Network> {
M: Middleware<N, T>,
T: Transport;
}

#[cfg(test)]
mod test {
use crate::Middleware;
use alloy_networks::Network;

fn __compile_check<N: Network>() -> Box<dyn Middleware<N>> {
unimplemented!()
}
}
1 change: 1 addition & 0 deletions crates/networks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exclude.workspace = true

[dependencies]
alloy-json-rpc.workspace = true
alloy-primitives.workspace = true
alloy-rlp.workspace = true
alloy-transports.workspace = true
pin-project = "1.1.2"
Expand Down
16 changes: 13 additions & 3 deletions crates/networks/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
use alloy_json_rpc::RpcObject;

pub trait Network {
/// Captures type info for network-specific RPC requests/responses
pub trait Network: Sized + Send + Sync + 'static {
#[doc(hidden)]
/// Asserts that this trait can only be implemented on a ZST.
const __ASSERT_ZST: bool = {
assert!(std::mem::size_of::<Self>() == 0, "Network must by a ZST");
true
};

/// The JSON body of a transaction request.
type TransactionRequest: Transaction;

Expand All @@ -11,10 +19,12 @@ pub trait Network {
type TransactionResponse: Transaction;
}

/// Captures getters and setters common across transactions across all networks
/// Captures getters and setters common across transactions and
/// transaction-like objects across all networks.
pub trait Transaction:
alloy_rlp::Encodable + alloy_rlp::Decodable + RpcObject + Sized + 'static
alloy_rlp::Encodable + alloy_rlp::Decodable + RpcObject + Clone + Sized + 'static
{
fn set_gas(&mut self, gas: alloy_primitives::U256);
}

/// Captures getters and setters common across EIP-1559 transactions across all networks
Expand Down
10 changes: 5 additions & 5 deletions crates/transports/src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ where
#[pin_project::pin_project(project = CallStateProj)]
pub enum BatchFuture<Conn>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
{
Prepared {
Expand Down Expand Up @@ -110,7 +110,7 @@ impl<'a, T> BatchRequest<'a, T> {

impl<'a, Conn> BatchRequest<'a, Conn>
where
Conn: Transport,
Conn: Transport + Clone,
{
#[must_use = "Waiters do nothing unless polled. A Waiter will never resolve unless its batch is sent."]
/// Add a call to the batch.
Expand Down Expand Up @@ -139,7 +139,7 @@ where

impl<'a, T> IntoFuture for BatchRequest<'a, T>
where
T: Transport,
T: Transport + Clone,
{
type Output = <BatchFuture<T> as Future>::Output;
type IntoFuture = BatchFuture<T>;
Expand All @@ -151,7 +151,7 @@ where

impl<T> BatchFuture<T>
where
T: Transport,
T: Transport + Clone,
{
fn poll_prepared(
mut self: Pin<&mut Self>,
Expand Down Expand Up @@ -248,7 +248,7 @@ where

impl<T> Future for BatchFuture<T>
where
T: Transport,
T: Transport + Clone,
T::Future: Send,
{
type Output = Result<(), TransportError>;
Expand Down
14 changes: 7 additions & 7 deletions crates/transports/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tower::{Layer, Service};
#[pin_project::pin_project(project = CallStateProj)]
enum CallState<Params, Conn>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
Expand All @@ -29,7 +29,7 @@ where

impl<Params, Conn> CallState<Params, Conn>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
Expand Down Expand Up @@ -78,7 +78,7 @@ where

impl<Params, Conn> Future for CallState<Params, Conn>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
Expand All @@ -101,7 +101,7 @@ where
#[pin_project::pin_project]
pub struct RpcCall<Conn, Params, Resp>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
Expand All @@ -112,7 +112,7 @@ where

impl<Conn, Params, Resp> RpcCall<Conn, Params, Resp>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
Expand All @@ -129,7 +129,7 @@ where

impl<'a, Conn, Params, Resp> RpcCall<Conn, Params, Resp>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam + 'a,
Resp: RpcReturn,
Expand All @@ -143,7 +143,7 @@ where

impl<Conn, Params, Resp> Future for RpcCall<Conn, Params, Resp>
where
Conn: Transport,
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
Resp: RpcReturn,
Expand Down
2 changes: 1 addition & 1 deletion crates/transports/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl<T> RpcClient<T> {

impl<T> RpcClient<T>
where
T: Transport,
T: Transport + Clone,
T::Future: Send,
{
#[inline]
Expand Down
2 changes: 1 addition & 1 deletion crates/transports/src/transports/json_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl<S> tower::Layer<S> for JsonRpcLayer {

impl<S, Param> Service<JsonRpcRequest<Param>> for JsonRpcService<S>
where
S: Transport,
S: Transport + Clone,
Param: RpcParam,
{
type Response = JsonRpcResponse;
Expand Down
72 changes: 65 additions & 7 deletions crates/transports/src/transports/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ pub use json_service::{JsonRpcFuture, JsonRpcLayer, JsonRpcService};

use serde_json::value::RawValue;
use std::{future::Future, pin::Pin};
use tower::{util::BoxCloneService, Service};
use tower::Service;

use crate::TransportError;

pub type BoxTransport = BoxCloneService<Box<RawValue>, Box<RawValue>, TransportError>;

pub trait Transport:
Service<
Box<RawValue>,
Response = Box<RawValue>,
Error = TransportError,
Future = Pin<Box<dyn Future<Output = Result<Box<RawValue>, TransportError>> + Send>>,
> + Clone
+ Send
> + Send
+ Sync
+ 'static
{
fn erased(self) -> BoxTransport
where
Self: Sized + Clone + Send + Sync + 'static,
{
BoxTransport {
inner: Box::new(self),
}
}
}

impl<T> Transport for T where
Expand All @@ -30,8 +36,60 @@ impl<T> Transport for T where
Response = Box<RawValue>,
Error = TransportError,
Future = Pin<Box<dyn Future<Output = Result<Box<RawValue>, TransportError>> + Send>>,
> + Clone
+ Send
> + Send
+ Sync
+ 'static
{
}

pub struct BoxTransport {
inner: Box<dyn CloneTransport + Send + Sync>,
}

impl Clone for BoxTransport {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone_box(),
}
}
}

trait CloneTransport: Transport {
fn clone_box(&self) -> Box<dyn CloneTransport + Send + Sync>;
}

impl<T> CloneTransport for T
where
T: Transport + Clone + Send + Sync,
{
fn clone_box(&self) -> Box<dyn CloneTransport + Send + Sync> {
Box::new(self.clone())
}
}

impl Service<Box<RawValue>> for BoxTransport {
type Response = Box<RawValue>;

type Error = TransportError;

type Future = Pin<Box<dyn Future<Output = Result<Box<RawValue>, TransportError>> + Send>>;

fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}

fn call(&mut self, req: Box<RawValue>) -> Self::Future {
self.inner.call(req)
}
}

/// checks trait + send + sync + 'static
fn __compile_check() {
fn inner<T: Transport>() {
todo!()
}
inner::<BoxTransport>();
}

0 comments on commit 7a9726e

Please sign in to comment.