Add a transports crate & initial Network abstraction#2
Conversation
|
making a few trait changes here |
| fn send_transaction<'s: 'fut, 'a: 'fut, 'fut>( | ||
| &'s self, | ||
| tx: &'a N::TransactionRequest, | ||
| ) -> MwareFut<'fut, N::Receipt, TransportError> { |
There was a problem hiding this comment.
love the param over the network
| { | ||
| fn set_gas(&mut self, gas: alloy_primitives::U256); |
crates/middleware/src/lib.rs
Outdated
| /// 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; | ||
| } |
There was a problem hiding this comment.
seems reasonable, curious to see how this ends up looking for various stacks
crates/middleware/src/lib.rs
Outdated
|
|
||
| /// 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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
you mean ethereum and optimism?
There was a problem hiding this comment.
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
| &'s self, | ||
| tx: &'a N::TransactionRequest, |
There was a problem hiding this comment.
ok so we de-asynctraited the trait, i guess it makes some things easier, but havent fully realized it yet
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
| let json = resp.text().await?; | ||
|
|
||
| RawValue::from_string(json).map_err(|err| TransportError::deser_err(err, "")) |
There was a problem hiding this comment.
RawValue is a wrapper for str (not &str), and should re-use the String's allocation. so it ought to be equivalent
| match to_json_raw_value(&req) { | ||
| Ok(raw) => JsonRpcFuture { | ||
| state: States::Pending { | ||
| fut: client.call(raw), | ||
| }, | ||
| _resp: std::marker::PhantomData, | ||
| }, |
There was a problem hiding this comment.
really hoping for some giga-unsafe way to get us around these manual conversions
| 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()) | ||
| } | ||
| } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
gakonst
left a comment
There was a problem hiding this comment.
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?
| /// `true` if the transport is local. | ||
| pub(crate) is_local: bool, |
There was a problem hiding this comment.
What's the difference between a local and non-local transport?
There was a problem hiding this comment.
we might write code to slam local nodes and not slam remote nodes
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 We can't have an associated type or a generic type on the middleware trait, as that would break |
Transports crate handles JSON-RPC semantics and is based on past work in ethers-rs