Skip to content

Commit b9210c1

Browse files
authored
feat(evm): MultiFork generic over BlockEnv (foundry-rs#14033)
1 parent 3a66371 commit b9210c1

2 files changed

Lines changed: 79 additions & 44 deletions

File tree

crates/evm/core/src/backend/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ struct _ObjectSafe(dyn DatabaseExt);
441441
#[must_use]
442442
pub struct Backend<N: Network = AnyNetwork> {
443443
/// The access point for managing forks
444-
forks: MultiFork<N, SpecId>,
444+
forks: MultiFork<N, SpecId, BlockEnv>,
445445
// The default in memory db
446446
mem_db: FoundryEvmInMemoryDB,
447447
/// The journaled_state to use to initialize new forks with
@@ -478,7 +478,7 @@ where
478478
/// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory
479479
/// database.
480480
pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
481-
Self::new(MultiFork::<N, SpecId>::spawn(), fork)
481+
Self::new(MultiFork::<N, SpecId, BlockEnv>::spawn(), fork)
482482
}
483483

484484
/// Creates a new instance of `Backend`
@@ -487,7 +487,10 @@ where
487487
/// database.
488488
///
489489
/// Prefer using [`spawn`](Self::spawn) instead.
490-
pub fn new(forks: MultiFork<N, SpecId>, fork: Option<CreateFork>) -> eyre::Result<Self> {
490+
pub fn new(
491+
forks: MultiFork<N, SpecId, BlockEnv>,
492+
fork: Option<CreateFork>,
493+
) -> eyre::Result<Self> {
491494
trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
492495
// Note: this will take of registering the `fork`
493496
let inner = BackendInner {

crates/evm/core/src/fork/multi.rs

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@
44
//! concurrently active pairs at once.
55
66
use super::CreateFork;
7+
use crate::FoundryBlock;
78
use alloy_evm::EvmEnv;
89
use alloy_network::Network;
910
use alloy_primitives::{U256, map::HashMap};
1011
use foundry_config::Config;
11-
use foundry_fork_db::{BackendHandler, BlockchainDb, SharedBackend, cache::BlockchainDbMeta};
12+
use foundry_fork_db::{
13+
BackendHandler, BlockchainDb, ForkBlockEnv, SharedBackend, cache::BlockchainDbMeta,
14+
};
1215
use futures::{
1316
FutureExt, StreamExt,
1417
channel::mpsc::{Receiver, Sender, channel},
1518
stream::Fuse,
1619
task::{Context, Poll},
1720
};
18-
use revm::{context::BlockEnv, primitives::hardfork::SpecId};
21+
use revm::primitives::hardfork::SpecId;
1922
use std::{
2023
fmt::{self, Write},
2124
pin::Pin,
@@ -66,15 +69,18 @@ impl<T: Into<String>> From<T> for ForkId {
6669
/// Can send requests to the `MultiForkHandler` to create forks.
6770
#[derive(Clone, Debug)]
6871
#[must_use]
69-
pub struct MultiFork<N: Network, SPEC, BLOCK = BlockEnv> {
72+
pub struct MultiFork<N: Network, SPEC, BLOCK: ForkBlockEnv> {
7073
/// Channel to send `Request`s to the handler.
7174
handler: Sender<Request<N, SPEC, BLOCK>>,
7275
/// Ensures that all rpc resources get flushed properly.
7376
_shutdown: Arc<ShutDownMultiFork<N, SPEC, BLOCK>>,
7477
}
7578

76-
impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 'static>
77-
MultiFork<N, SPEC>
79+
impl<
80+
N: Network,
81+
SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 'static,
82+
BLOCK: FoundryBlock + ForkBlockEnv + Default + Unpin,
83+
> MultiFork<N, SPEC, BLOCK>
7884
{
7985
/// Creates a new pair and spawns the `MultiForkHandler` on a background thread.
8086
pub fn spawn() -> Self {
@@ -117,7 +123,7 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 's
117123
///
118124
/// Use [`spawn`](Self::spawn) instead.
119125
#[doc(hidden)]
120-
pub fn new() -> (Self, MultiForkHandler<N, SPEC>) {
126+
pub fn new() -> (Self, MultiForkHandler<N, SPEC, BLOCK>) {
121127
let (handler, handler_rx) = channel(1);
122128
let _shutdown = Arc::new(ShutDownMultiFork { handler: Some(handler.clone()) });
123129
(Self { handler, _shutdown }, MultiForkHandler::new(handler_rx))
@@ -126,10 +132,11 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 's
126132
/// Returns a fork backend.
127133
///
128134
/// If no matching fork backend exists it will be created.
135+
#[allow(clippy::type_complexity)]
129136
pub fn create_fork(
130137
&self,
131138
fork: CreateFork,
132-
) -> eyre::Result<(ForkId, SharedBackend<N>, EvmEnv<SPEC>)> {
139+
) -> eyre::Result<(ForkId, SharedBackend<N, BLOCK>, EvmEnv<SPEC, BLOCK>)> {
133140
trace!("Creating new fork, url={}, block={:?}", fork.url, fork.evm_opts.fork_block_number);
134141
let (sender, rx) = oneshot_channel();
135142
let req = Request::CreateFork(Box::new(fork), sender);
@@ -140,11 +147,12 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 's
140147
/// Rolls the block of the fork.
141148
///
142149
/// If no matching fork backend exists it will be created.
150+
#[allow(clippy::type_complexity)]
143151
pub fn roll_fork(
144152
&self,
145153
fork: ForkId,
146154
block: u64,
147-
) -> eyre::Result<(ForkId, SharedBackend<N>, EvmEnv<SPEC>)> {
155+
) -> eyre::Result<(ForkId, SharedBackend<N, BLOCK>, EvmEnv<SPEC, BLOCK>)> {
148156
trace!(?fork, ?block, "rolling fork");
149157
let (sender, rx) = oneshot_channel();
150158
let req = Request::RollFork(fork, block, sender);
@@ -153,7 +161,7 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 's
153161
}
154162

155163
/// Returns the `EvmEnv` of the given fork, if any.
156-
pub fn get_evm_env(&self, fork: ForkId) -> eyre::Result<Option<EvmEnv<SPEC>>> {
164+
pub fn get_evm_env(&self, fork: ForkId) -> eyre::Result<Option<EvmEnv<SPEC, BLOCK>>> {
157165
trace!(?fork, "getting env config");
158166
let (sender, rx) = oneshot_channel();
159167
let req = Request::GetEvmEnv(fork, sender);
@@ -174,7 +182,10 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 's
174182
///
175183
/// This is required for tx level forking where we need to fork off the `block - 1` state but
176184
/// still need use env settings for `env`.
177-
pub fn update_block_env(&self, fork: ForkId, env: BlockEnv) -> eyre::Result<()> {
185+
pub fn update_block_env(&self, fork: ForkId, env: BLOCK) -> eyre::Result<()>
186+
where
187+
BLOCK: fmt::Debug,
188+
{
178189
trace!(?fork, ?env, "update fork block");
179190
self.handler
180191
.clone()
@@ -185,7 +196,7 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 's
185196
/// Returns the corresponding fork if it exists.
186197
///
187198
/// Returns `None` if no matching fork backend is available.
188-
pub fn get_fork(&self, id: impl Into<ForkId>) -> eyre::Result<Option<SharedBackend<N>>> {
199+
pub fn get_fork(&self, id: impl Into<ForkId>) -> eyre::Result<Option<SharedBackend<N, BLOCK>>> {
189200
let id = id.into();
190201
trace!(?id, "get fork backend");
191202
let (sender, rx) = oneshot_channel();
@@ -207,21 +218,26 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + Send + 's
207218

208219
type CreateFuture<N, SPEC, BLOCK> = Pin<
209220
Box<
210-
dyn Future<Output = eyre::Result<(ForkId, CreatedFork<N, SPEC, BLOCK>, BackendHandler<N>)>>
211-
+ Send,
221+
dyn Future<
222+
Output = eyre::Result<(
223+
ForkId,
224+
CreatedFork<N, SPEC, BLOCK>,
225+
BackendHandler<N, BLOCK>,
226+
)>,
227+
> + Send,
212228
>,
213229
>;
214230
type CreateSender<N, SPEC, BLOCK> =
215-
OneshotSender<eyre::Result<(ForkId, SharedBackend<N>, EvmEnv<SPEC, BLOCK>)>>;
231+
OneshotSender<eyre::Result<(ForkId, SharedBackend<N, BLOCK>, EvmEnv<SPEC, BLOCK>)>>;
216232
type GetEvmEnvSender<SPEC, BLOCK> = OneshotSender<Option<EvmEnv<SPEC, BLOCK>>>;
217233

218234
/// Request that's send to the handler.
219235
#[derive(Debug)]
220-
enum Request<N: Network, SPEC, BLOCK = BlockEnv> {
236+
enum Request<N: Network, SPEC, BLOCK: ForkBlockEnv> {
221237
/// Creates a new ForkBackend.
222238
CreateFork(Box<CreateFork>, CreateSender<N, SPEC, BLOCK>),
223239
/// Returns the Fork backend for the `ForkId` if it exists.
224-
GetFork(ForkId, OneshotSender<Option<SharedBackend<N>>>),
240+
GetFork(ForkId, OneshotSender<Option<SharedBackend<N, BLOCK>>>),
225241
/// Adjusts the block that's being forked, by creating a new fork at the new block.
226242
RollFork(ForkId, u64, CreateSender<N, SPEC, BLOCK>),
227243
/// Returns the environment of the fork.
@@ -236,7 +252,7 @@ enum Request<N: Network, SPEC, BLOCK = BlockEnv> {
236252
GetForkUrl(ForkId, OneshotSender<Option<String>>),
237253
}
238254

239-
enum ForkTask<N: Network, SPEC, BLOCK = BlockEnv> {
255+
enum ForkTask<N: Network, SPEC, BLOCK: ForkBlockEnv> {
240256
/// Contains the future that will establish a new fork.
241257
Create(
242258
CreateFuture<N, SPEC, BLOCK>,
@@ -248,14 +264,14 @@ enum ForkTask<N: Network, SPEC, BLOCK = BlockEnv> {
248264

249265
/// The type that manages connections in the background.
250266
#[must_use = "futures do nothing unless polled"]
251-
pub struct MultiForkHandler<N: Network, SPEC, BLOCK = BlockEnv> {
267+
pub struct MultiForkHandler<N: Network, SPEC, BLOCK: ForkBlockEnv> {
252268
/// Incoming requests from the `MultiFork`.
253269
incoming: Fuse<Receiver<Request<N, SPEC, BLOCK>>>,
254270

255271
/// All active handlers.
256272
///
257273
/// It's expected that this list will be rather small (<10).
258-
handlers: Vec<(ForkId, BackendHandler<N>)>,
274+
handlers: Vec<(ForkId, BackendHandler<N, BLOCK>)>,
259275

260276
// tasks currently in progress
261277
pending_tasks: Vec<ForkTask<N, SPEC, BLOCK>>,
@@ -270,8 +286,13 @@ pub struct MultiForkHandler<N: Network, SPEC, BLOCK = BlockEnv> {
270286
flush_cache_interval: Option<tokio::time::Interval>,
271287
}
272288

273-
impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + 'static> MultiForkHandler<N, SPEC> {
274-
fn new(incoming: Receiver<Request<N, SPEC>>) -> Self {
289+
impl<
290+
N: Network,
291+
SPEC: Into<SpecId> + Default + Copy + Clone + 'static,
292+
BLOCK: FoundryBlock + ForkBlockEnv + Default,
293+
> MultiForkHandler<N, SPEC, BLOCK>
294+
{
295+
fn new(incoming: Receiver<Request<N, SPEC, BLOCK>>) -> Self {
275296
Self {
276297
incoming: incoming.fuse(),
277298
handlers: Default::default(),
@@ -292,7 +313,7 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + 'static> MultiFor
292313
fn find_in_progress_task(
293314
&mut self,
294315
id: &ForkId,
295-
) -> Option<&mut Vec<CreateSender<N, SPEC, BlockEnv>>> {
316+
) -> Option<&mut Vec<CreateSender<N, SPEC, BLOCK>>> {
296317
for ForkTask::Create(_, in_progress, _, additional) in &mut self.pending_tasks {
297318
if in_progress == id {
298319
return Some(additional);
@@ -301,7 +322,7 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + 'static> MultiFor
301322
None
302323
}
303324

304-
fn create_fork(&mut self, fork: CreateFork, sender: CreateSender<N, SPEC, BlockEnv>) {
325+
fn create_fork(&mut self, fork: CreateFork, sender: CreateSender<N, SPEC, BLOCK>) {
305326
let fork_id = ForkId::new(&fork.url, fork.evm_opts.fork_block_number);
306327
trace!(?fork_id, "created new forkId");
307328

@@ -319,9 +340,9 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + 'static> MultiFor
319340
fn insert_new_fork(
320341
&mut self,
321342
fork_id: ForkId,
322-
fork: CreatedFork<N, SPEC>,
323-
sender: CreateSender<N, SPEC, BlockEnv>,
324-
additional_senders: Vec<CreateSender<N, SPEC, BlockEnv>>,
343+
fork: CreatedFork<N, SPEC, BLOCK>,
344+
sender: CreateSender<N, SPEC, BLOCK>,
345+
additional_senders: Vec<CreateSender<N, SPEC, BLOCK>>,
325346
) {
326347
self.forks.insert(fork_id.clone(), fork.clone());
327348
let _ = sender.send(Ok((fork_id.clone(), fork.backend.clone(), fork.evm_env.clone())));
@@ -335,7 +356,7 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + 'static> MultiFor
335356
}
336357

337358
/// Update the fork's block entire env
338-
fn update_env(&mut self, fork_id: ForkId, env: BlockEnv) {
359+
fn update_env(&mut self, fork_id: ForkId, env: BLOCK) {
339360
if let Some(fork) = self.forks.get_mut(&fork_id) {
340361
fork.evm_env.block_env = env;
341362
}
@@ -344,12 +365,12 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + 'static> MultiFor
344365
/// cheatcodes when new fork selected.
345366
fn update_block(&mut self, fork_id: ForkId, block_number: U256, block_timestamp: U256) {
346367
if let Some(fork) = self.forks.get_mut(&fork_id) {
347-
fork.evm_env.block_env.number = block_number;
348-
fork.evm_env.block_env.timestamp = block_timestamp;
368+
fork.evm_env.block_env.set_number(block_number);
369+
fork.evm_env.block_env.set_timestamp(block_timestamp);
349370
}
350371
}
351372

352-
fn on_request(&mut self, req: Request<N, SPEC>) {
373+
fn on_request(&mut self, req: Request<N, SPEC, BLOCK>) {
353374
match req {
354375
Request::CreateFork(fork, sender) => self.create_fork(*fork, sender),
355376
Request::GetFork(fork_id, sender) => {
@@ -393,8 +414,11 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + 'static> MultiFor
393414

394415
// Drives all handler to completion.
395416
// This future will finish once all underlying BackendHandler are completed.
396-
impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + 'static> Future
397-
for MultiForkHandler<N, SPEC>
417+
impl<
418+
N: Network,
419+
SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + 'static,
420+
BLOCK: FoundryBlock + ForkBlockEnv + Default + Unpin,
421+
> Future for MultiForkHandler<N, SPEC, BLOCK>
398422
{
399423
type Output = ();
400424

@@ -496,20 +520,24 @@ impl<N: Network, SPEC: Into<SpecId> + Default + Copy + Clone + Unpin + 'static>
496520

497521
/// Tracks the created Fork
498522
#[derive(Debug, Clone)]
499-
struct CreatedFork<N: Network, SPEC, BLOCK = BlockEnv> {
523+
struct CreatedFork<N: Network, SPEC, BLOCK: ForkBlockEnv> {
500524
/// How the fork was initially created.
501525
opts: CreateFork,
502526
/// The resolved EVM environment (fetched from the provider).
503527
evm_env: EvmEnv<SPEC, BLOCK>,
504528
/// Copy of the sender.
505-
backend: SharedBackend<N>,
529+
backend: SharedBackend<N, BLOCK>,
506530
/// How many consumers there are, since a `SharedBacked` can be used by multiple
507531
/// consumers.
508532
num_senders: Arc<AtomicUsize>,
509533
}
510534

511-
impl<N: Network, SPEC> CreatedFork<N, SPEC> {
512-
pub fn new(opts: CreateFork, evm_env: EvmEnv<SPEC>, backend: SharedBackend<N>) -> Self {
535+
impl<N: Network, SPEC, BLOCK: ForkBlockEnv> CreatedFork<N, SPEC, BLOCK> {
536+
pub fn new(
537+
opts: CreateFork,
538+
evm_env: EvmEnv<SPEC, BLOCK>,
539+
backend: SharedBackend<N, BLOCK>,
540+
) -> Self {
513541
Self { opts, evm_env, backend, num_senders: Arc::new(AtomicUsize::new(1)) }
514542
}
515543

@@ -531,11 +559,11 @@ impl<N: Network, SPEC> CreatedFork<N, SPEC> {
531559
/// This type intentionally does not implement `Clone` since it's intended that there's only once
532560
/// instance.
533561
#[derive(Debug)]
534-
struct ShutDownMultiFork<N: Network, SPEC, BLOCK = BlockEnv> {
562+
struct ShutDownMultiFork<N: Network, SPEC, BLOCK: ForkBlockEnv> {
535563
handler: Option<Sender<Request<N, SPEC, BLOCK>>>,
536564
}
537565

538-
impl<N: Network, SPEC, BLOCK> Drop for ShutDownMultiFork<N, SPEC, BLOCK> {
566+
impl<N: Network, SPEC, BLOCK: ForkBlockEnv> Drop for ShutDownMultiFork<N, SPEC, BLOCK> {
539567
fn drop(&mut self) {
540568
trace!(target: "fork::multi", "initiating shutdown");
541569
let (sender, rx) = oneshot_channel();
@@ -552,17 +580,21 @@ impl<N: Network, SPEC, BLOCK> Drop for ShutDownMultiFork<N, SPEC, BLOCK> {
552580
/// Creates a new fork.
553581
///
554582
/// This will establish a new `Provider` to the endpoint and return the Fork Backend.
555-
async fn create_fork<N: Network, SPEC: Into<SpecId> + Default + Copy>(
583+
async fn create_fork<
584+
N: Network,
585+
SPEC: Into<SpecId> + Default + Copy,
586+
BLOCK: FoundryBlock + ForkBlockEnv + Default,
587+
>(
556588
mut fork: CreateFork,
557-
) -> eyre::Result<(ForkId, CreatedFork<N, SPEC>, BackendHandler<N>)> {
589+
) -> eyre::Result<(ForkId, CreatedFork<N, SPEC, BLOCK>, BackendHandler<N, BLOCK>)> {
558590
// Ensure evm_opts reflects the fork URL (may differ from the resolved CreateFork url when
559591
// created via cheatcodes, where evm_opts is cloned from the base config).
560592
fork.evm_opts.fork_url = Some(fork.url.clone());
561593

562594
let provider = fork.evm_opts.fork_provider_with_url::<N>(&fork.url)?;
563595

564596
// Initialise the fork environment.
565-
let (evm_env, number) = fork.evm_opts.fork_evm_env::<_, BlockEnv, _, _>(&provider).await?;
597+
let (evm_env, number) = fork.evm_opts.fork_evm_env::<_, BLOCK, _, _>(&provider).await?;
566598
let meta = BlockchainDbMeta::new(evm_env.block_env.clone(), fork.url.clone());
567599

568600
// Determine the cache path if caching is enabled.

0 commit comments

Comments
 (0)