Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a transports crate & initial Network abstraction #2

Merged
merged 53 commits into from
Aug 24, 2023

Conversation

prestwich
Copy link
Member

Transports crate handles JSON-RPC semantics and is based on past work in ethers-rs

@prestwich prestwich added the enhancement New feature or request label Jul 10, 2023
@prestwich prestwich requested a review from DaniPopes July 10, 2023 21:53
@prestwich prestwich self-assigned this Jul 10, 2023
Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good start - light pass

crates/transports/src/call.rs Outdated Show resolved Hide resolved
crates/transports/src/call.rs Outdated Show resolved Hide resolved
crates/transports/src/call.rs Outdated Show resolved Hide resolved
crates/transports/src/call.rs Outdated Show resolved Hide resolved
crates/transports/src/call.rs Outdated Show resolved Hide resolved
crates/transports/src/error.rs Show resolved Hide resolved
crates/transports/src/error.rs Show resolved Hide resolved
crates/transports/src/error.rs Show resolved Hide resolved
crates/transports/src/transports/http.rs Outdated Show resolved Hide resolved
crates/transports/src/transports/http.rs Outdated Show resolved Hide resolved
crates/transports/src/call.rs Outdated Show resolved Hide resolved
crates/transports/src/transport.rs Outdated Show resolved Hide resolved
@prestwich
Copy link
Member Author

making a few trait changes here

@prestwich prestwich changed the title Add a transports crate Add a transports crate & initial Network abstraction Jul 13, 2023
crates/network/src/lib.rs Outdated Show resolved Hide resolved
fn send_transaction<'s: 'fut, 'a: 'fut, 'fut>(
&'s self,
tx: &'a N::TransactionRequest,
) -> MwareFut<'fut, N::Receipt, TransportError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love the param over the network

Comment on lines +26 to +27
{
fn set_gas(&mut self, gas: alloy_primitives::U256);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to make sense to me - wdyt @mattsse

Comment on lines 108 to 116
/// Middleware use a tower-like Layer abstraction
pub trait MwareLayer<N: Network> {
type Middleware<T: Transport>: Middleware<N, T>;

fn layer<M, T>(&self, inner: M) -> Self::Middleware<T>
where
M: Middleware<N, T>,
T: Transport;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems reasonable, curious to see how this ends up looking for various stacks


/// 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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be worth doing a couple test-drives of this abstraction with an Ethereum mainnet and Celo transaction and having both providers side-by-side

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean ethereum and optimism?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes or that, just recall the Celo txs having more differences, but actually we have the Optimism Deposit Tx which should be a good test too

Comment on lines 21 to 22
&'s self,
tx: &'a N::TransactionRequest,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok so we de-asynctraited the trait, i guess it makes some things easier, but havent fully realized it yet

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll re-async-trait it. just wanted to make sure i understood the inner workings of async-trait so i could make better decisions about it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was optimistic that I would be able to introduce a non-boxpinned future type that would cover mware usecases, but that results in some type complexity explosion and extra indirection for to erase types of boxed fns 😮‍💨


#[inline]
fn call(&mut self, req: Box<RawValue>) -> Self::Future {
let replacement = self.client.clone();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guessing this is ok to do?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the tower-recommended pattern. It's permissible for the clone to not return ready on poll_ready, so you replace to ensure the original is the one filling the service

Comment on lines 84 to 86
let json = resp.text().await?;

RawValue::from_string(json).map_err(|err| TransportError::deser_err(err, ""))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe bytes faster here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RawValue is a wrapper for str (not &str), and should re-use the String's allocation. so it ought to be equivalent

Comment on lines +44 to +50
match to_json_raw_value(&req) {
Ok(raw) => JsonRpcFuture {
state: States::Pending {
fut: client.call(raw),
},
_resp: std::marker::PhantomData,
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really hoping for some giga-unsafe way to get us around these manual conversions

crates/transports/src/transports/mod.rs Outdated Show resolved Hide resolved
Comment on lines +49 to +68
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())
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be simplified by doing trait CloneTransport: Transport + Clone + Send + Sync {} and using that inside of BoxTransport? Or do we need all intermediate traits/should we seal them

Copy link
Member Author

@prestwich prestwich Aug 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

traits with a Clone supertrait are never object safe as Sized is a supertrait of Clone. The CloneTransport trait here is private, and only used by the Clone impl on BoxTransport, so we don't need to seal it. it's just invisible to lib consumers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code is basically a copy of the structure of tower's BoxCloneService setup. which I wanted to use, but doesn't have + Send + Sync

Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also check to see that error handling is "nice" even when stacking many layers.

As-is right now I mainly see that we have removed generics from the providers, but the middleware abstraction looks somewhat similar with before eg fn inner(), beyond using the more Tower-esque functions for stacking?

Comment on lines +29 to +30
/// `true` if the transport is local.
pub(crate) is_local: bool,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between a local and non-local transport?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might write code to slam local nodes and not slam remote nodes

@prestwich
Copy link
Member Author

We should also check to see that error handling is "nice" even when stacking many layers.
As-is right now I mainly see that we have removed generics from the providers, but the middleware abstraction looks somewhat similar with before eg fn inner(), beyond using the more Tower-esque functions for stacking?

Basically the only way to achieve nice error types AND stacking AND useful object-safety is to have a single error type and force all middleware to use it. so that's the current plan

// no generic
pub enum MiddlewareError {
    TransportError(TransportError),
    Custom(Box<...>)
}

We can't have an associated type or a generic type on the middleware trait, as that would break Box<dyn ...> across Mware with different error types. E.g. Box<dyn Middleware<N, Err1>> is different from Box<dyn Middleware<N, Err2>>. Same for Box<dyn Middleware<N, Error=Err1>> vs <N, Error = Err2> This ruins user abstractions unless they have the exact same middleware stack for each instance

@prestwich prestwich merged commit 611a057 into main Aug 24, 2023
12 checks passed
@gakonst gakonst deleted the prestwich/transports branch August 24, 2023 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants