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

feat(evm-adapters): transport agnostic forking #1003

Conversation

meetmangukiya
Copy link
Contributor

@meetmangukiya meetmangukiya commented Mar 21, 2022

Motivation

Run tests on forked mainnet will probably gain some speed when using WebSocket / IPC which will prevent opening and closing connections for each RPC requests(account data, storage slots).

P.S.: Have not tested IPC

Solution

@meetmangukiya meetmangukiya marked this pull request as draft March 21, 2022 13:10
@mattsse
Copy link
Member

mattsse commented Mar 21, 2022

thanks, good point!
since we're transitioning to revm I wonder whether it's even worth it to add this for sputnik or only add this to the revm branch

@@ -378,14 +379,16 @@ impl SharedBackend {
cache: SharedCache<MemCache>,
vicinity: MemoryVicinity,
pin_block: Option<BlockId>,
runtime: Option<Runtime>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Had to pass runtime along because WebSocket connection would be closed when the runtime that it was created on drops.

Copy link
Contributor

Choose a reason for hiding this comment

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

btw, i don't think RuntimeOrHandle was good idea here (i'm to blame) - it would be better to have clean async constructor with native spawn & wrap it in sync version.

@meetmangukiya
Copy link
Contributor Author

since we're transitioning to revm I wonder whether it's even worth it to add this for sputnik or only add this to the revm branch

Not sure, do we plan to keep and support the sputnik evm alongside revm? I was thinking that's the plan since we have --evm-type flag

@meetmangukiya meetmangukiya marked this pull request as ready for review March 21, 2022 14:26
@gakonst
Copy link
Member

gakonst commented Mar 21, 2022

Supportive of the PR in principle, we're getting rid of other evm types so could you give a stab maybe at integrating this against https://github.com/gakonst/foundry/tree/revm?

@onbjerg
Copy link
Member

onbjerg commented Mar 22, 2022

since we're transitioning to revm I wonder whether it's even worth it to add this for sputnik or only add this to the revm branch

Not sure, do we plan to keep and support the sputnik evm alongside revm? I was thinking that's the plan since we have --evm-type flag

No sir, we will not keep Sputnik. The multi-EVM thing was an early design thought but we're moving away from that. The relevant files in the revm branch is in evm/src/executor/fork and it should be pretty similar to the one in evm-adapters, so porting should not prove too difficult. Let me know if you have questions 😄

@onbjerg onbjerg added the T-feature Type: feature label Mar 22, 2022
@meetmangukiya meetmangukiya marked this pull request as draft March 23, 2022 20:09
@meetmangukiya meetmangukiya marked this pull request as ready for review March 29, 2022 06:58
Copy link
Member

@onbjerg onbjerg left a comment

Choose a reason for hiding this comment

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

lgtm cc @mattsse to double check

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

thanks a lot,
couple of nits

@@ -11,7 +11,7 @@ foundry-utils = { path = "./../utils" }
serde_json = "1.0.67"
serde = "1.0.130"
hex = "0.4.3"
ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["solc-full"] }
ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["solc-full", "ws", "rustls", "ipc"] }
Copy link
Member

Choose a reason for hiding this comment

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

ipc is only available on unix.
and enabling this will definitely not compile on windows
so we need to feature gate this here as well
perhaps fine as ipc module should be ignored even if enabled on non linux
it should be ignored:

https://github.com/gakonst/ethers-rs/blob/367f3444ec627c6afa3bd9613644a6f903b3cb86/ethers-providers/src/transports/mod.rs#L22-L25

Comment on lines +83 to +85
let provider = Arc::new(
rt.block_on(Provider::connect_ipc(url)).expect("Failed to establish provider"),
);
Copy link
Member

Choose a reason for hiding this comment

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

needs a cfg for unix, otherwise should panic

provider: M,
db: BlockchainDb,
pin_block: Option<BlockId>,
runtime: Option<Runtime>,
Copy link
Member

Choose a reason for hiding this comment

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

hmm not a big fan of supplying the runtime here.
I'd consider creating a new runtime negligible additional effort

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, however, I am establishing the IPC and WebSocket connections. You cannot use those in different runtimes or after the runtime that it was created on ceases to exist.

Copy link
Contributor

Choose a reason for hiding this comment

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

imho this function should be async

Comment on lines +51 to +55
match rt {
RuntimeOrHandle::Runtime(runtime) => runtime.block_on(fut),
RuntimeOrHandle::Handle(handle) => handle.block_on(fut),
}
.expect("could not instantiate forked environment")
Copy link
Member

Choose a reason for hiding this comment

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

this getting a bit messy.
we have already a block_on in cli/utils I believe.

let's either replicate that for this crate or better make this function async so we only have to do it once for the whole evm_env function.

Comment on lines +70 to +76
let fut = async {
let provider = Provider::connect_ipc(fork_url.as_str())
.await
.expect("could not instantiated provider");
environment(&provider, self.env.chain_id, self.fork_block_number, self.sender)
.await
};
Copy link
Member

Choose a reason for hiding this comment

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

also needs cfg

@gakonst
Copy link
Member

gakonst commented Mar 29, 2022

Feels like ethers provider is getting to the point where we should do an enum over the transports and abstract it HTTP/WS/IPC away from the user..

I just tested this and weirdly enough when I run it with WSS twice caching works, but when I switch URL to be back to the https it reruns w/o caching. Didn't expect that, sounds like a bug?

Separately, here's a WS vs HTTPS comparison on performance on same Alchemy API key without cache:

WSS Time:

[PASS] testShutdownCost() (gas: 17127724)
Test result: ok. 1 passed; 0 failed; finished in 793.41s

HTTPS Time:

Running 1 test for src/Convex.t.sol:ConvexTest
[PASS] testShutdownCost() (gas: 17127724)
Test result: ok. 1 passed; 0 failed; finished in 348.97s

Seems like WS is slower?

@mattsse
Copy link
Member

mattsse commented Mar 29, 2022

I just tested this and weirdly enough when I run it with WSS twice caching works, but when I switch URL to be back to the https it reruns w/o caching. Didn't expect that, sounds like a bug?

same host?
we're storing the host (Url::host) in the cache file and if they don't match we treat it as invalid and discard the cache.
perhaps we should to overthink that, and perhaps ignore that?

@gakonst
Copy link
Member

gakonst commented Mar 29, 2022

Aha! It's https://eth-mainnet.alchemyapi.io/v2/<...> vs wss://eth-mainnet.ws.alchemyapi.io/v2/<..>. Maybe we ignore the full URL?

@mattsse
Copy link
Member

mattsse commented Mar 29, 2022

yeah the host differs, there's a .ws. in the wss host.

let's ignore the endpoint in comparison but we can keep them as Vec<Url> instead?

@onbjerg
Copy link
Member

onbjerg commented Mar 30, 2022

Sorry to pile on here, but one last stretch goal (feel free to skip it!) would be to change the ETH_URL we use in our test workflow to use the websocket endpoint instead of HTTP 😄

@meetmangukiya
Copy link
Contributor Author

I will try to look into why ws is so much slower. I suspect there might be something off in how we are handling the requests in SharedBackend maybe. Putting the PR on draft for now since I expect not to be able to look into this soon.

@gakonst
Copy link
Member

gakonst commented Apr 18, 2022

@meetmangukiya just trying to keep things clean, any plans to resume this PR? else can I close as stale, and we re-visit later?

@meetmangukiya
Copy link
Contributor Author

meetmangukiya commented Apr 18, 2022

Can close for now. Might pick again when frustrated with lack of ws support. I actually couldn't think any rational reason why websocket is 2x slower. Thought might have to do something with the websocket server implementation in ethers or with the backend implementation in foundry. However, don't see anything obvious there that could lead to such unexpected results.

Are we open to merge ws and ipc provider support even though they may be slower or have no perfomance gains? If yes, i will address the review comments and can get this merged.

@gakonst
Copy link
Member

gakonst commented Apr 18, 2022

I would say let's only merge if we figure out the performance overheads. It's possible it's in the ethers websocket impl, I have never tried benchmarking it.

@gakonst gakonst closed this Apr 18, 2022
@meetmangukiya
Copy link
Contributor Author

I had tested ethersjs and ethers-rs websocket. There weren't significant differences in response times. But it was just simple looping of eth_gasPrice and logging response times so not the best way to benchmark.

@sambacha
Copy link
Contributor

sambacha commented Sep 8, 2022

I had tested ethersjs and ethers-rs websocket. There weren't significant differences in response times. But it was just simple looping of eth_gasPrice and logging response times so not the best way to benchmark.

ethersjs just recently (as in after your comment) properly implemented WS FYI

@meetmangukiya
Copy link
Contributor Author

@sambacha got a link to the PR?

@sambacha
Copy link
Contributor

sambacha commented Sep 8, 2022

@sambacha got a link to the PR?

Here is the relevant issue which has a long thread about websockets and the various PRs ethers-io/ethers.js#1053

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-feature Type: feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants