Skip to main content

foundry_evm_core/fork/
multi.rs

1//! Support for running multiple fork backends.
2//!
3//! The design is similar to the single `SharedBackend`, `BackendHandler` but supports multiple
4//! concurrently active pairs at once.
5
6use super::{CreateFork, ResolvedFork};
7use crate::{FoundryBlock, opts::ForkContext};
8use alloy_eips::BlockNumHash;
9use alloy_evm::EvmEnv;
10use alloy_network::{AnyNetwork, Network};
11use alloy_primitives::{U256, map::HashMap};
12use foundry_config::Config;
13use foundry_fork_db::{
14    BackendHandler, BlockchainDb, ForkBlock, ForkBlockEnv, SharedBackend, cache::BlockchainDbMeta,
15};
16use futures::{
17    FutureExt, StreamExt,
18    channel::mpsc::{Receiver, Sender, channel},
19    stream::Fuse,
20    task::{Context, Poll},
21};
22use revm::primitives::hardfork::SpecId;
23use std::{
24    fmt::{self, Write},
25    pin::Pin,
26    sync::{
27        Arc,
28        atomic::AtomicUsize,
29        mpsc::{Sender as OneshotSender, channel as oneshot_channel},
30    },
31    time::Duration,
32};
33
34/// The _unique_ identifier for a specific fork, this could be the name of the network a custom
35/// descriptive name.
36#[derive(Clone, Debug, PartialEq, Eq, Hash)]
37pub struct ForkId(pub String);
38
39impl ForkId {
40    /// Returns the identifier for a Fork from a URL and block number.
41    pub fn new(url: &str, num: Option<u64>) -> Self {
42        Self::new_with_context(url, num, None)
43    }
44
45    fn new_with_context(
46        url: &str,
47        num: Option<u64>,
48        context: Option<&crate::opts::ForkContext>,
49    ) -> Self {
50        let mut id = url.to_string();
51        if let Some(context) = context {
52            write!(
53                id,
54                "#{}:{}:{}:{}:{:?}:{:?}:{:?}:{:?}",
55                context.execution_chain_id,
56                context.source_chain_id,
57                context.network,
58                context.network_profile.execution_profile_name(),
59                context.hardfork,
60                context.instance_id,
61                context.source_fork_block_number,
62                context.source_fork_block_hash
63            )
64            .unwrap();
65        }
66        id.push('@');
67        match num {
68            Some(n) => write!(id, "{n:#x}").unwrap(),
69            None => id.push_str("latest"),
70        }
71        Self(id)
72    }
73
74    /// Returns the identifier for an exactly resolved fork.
75    fn resolved(url: &str, fork: &ResolvedFork) -> Self {
76        let mut id = Self::new_with_context(url, Some(fork.number()), Some(&fork.context())).0;
77        write!(id, "#{}:{}", fork.hash(), fork.source_id()).unwrap();
78        Self(id)
79    }
80
81    /// Returns the identifier of the fork.
82    pub fn as_str(&self) -> &str {
83        &self.0
84    }
85}
86
87impl fmt::Display for ForkId {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        self.0.fmt(f)
90    }
91}
92
93impl<T: Into<String>> From<T> for ForkId {
94    fn from(id: T) -> Self {
95        Self(id.into())
96    }
97}
98
99/// Backend, environment, and identity returned after creating or rolling a fork.
100pub struct ForkResult<N: Network, SPEC, BLOCK: ForkBlockEnv> {
101    /// Identifier assigned to the fork.
102    pub id: ForkId,
103    /// Backend pinned to the resolved fork block.
104    pub backend: SharedBackend<N, BLOCK>,
105    /// EVM environment reconstructed from the resolved fork block.
106    pub env: EvmEnv<SPEC, BLOCK>,
107    /// Exact source and block identity used to construct the backend.
108    pub resolved: ResolvedFork,
109}
110
111/// The Sender half of multi fork pair.
112/// Can send requests to the `MultiForkHandler` to create forks.
113#[derive(Clone, Debug)]
114#[must_use]
115pub struct MultiFork<N: Network, SPEC, BLOCK: ForkBlockEnv> {
116    /// Channel to send `Request`s to the handler.
117    handler: Sender<Request<N, SPEC, BLOCK>>,
118    /// Ensures that all rpc resources get flushed properly.
119    _shutdown: Arc<ShutDownMultiFork<N, SPEC, BLOCK>>,
120}
121
122impl<
123    N: Network,
124    SPEC: Into<SpecId> + Default + Copy + Unpin + Send + 'static,
125    BLOCK: FoundryBlock + ForkBlockEnv + Default + Unpin,
126> MultiFork<N, SPEC, BLOCK>
127{
128    /// Creates a new pair and spawns the `MultiForkHandler` on a background thread.
129    pub fn spawn() -> Self {
130        trace!(target: "fork::multi", "spawning multifork");
131
132        let (fork, mut handler) = Self::new();
133
134        // Spawn a light-weight thread just for sending and receiving data from the remote
135        // client(s).
136        let fut = async move {
137            // Flush cache every 60s, this ensures that long-running fork tests get their
138            // cache flushed from time to time.
139            // NOTE: we install the interval here because the `tokio::timer::Interval`
140            // requires a rt.
141            handler.set_flush_cache_interval(Duration::from_secs(60));
142            handler.await
143        };
144        match tokio::runtime::Handle::try_current() {
145            Ok(rt) => _ = rt.spawn(fut),
146            Err(_) => {
147                trace!(target: "fork::multi", "spawning multifork backend thread");
148                _ = std::thread::Builder::new()
149                    .name("multi-fork-backend".into())
150                    .spawn(move || {
151                        tokio::runtime::Builder::new_current_thread()
152                            .enable_all()
153                            .build()
154                            .expect("failed to build tokio runtime")
155                            .block_on(fut)
156                    })
157                    .expect("failed to spawn thread")
158            }
159        }
160
161        trace!(target: "fork::multi", "spawned MultiForkHandler thread");
162        fork
163    }
164
165    /// Creates a new pair multi fork pair.
166    ///
167    /// Use [`spawn`](Self::spawn) instead.
168    #[doc(hidden)]
169    pub fn new() -> (Self, MultiForkHandler<N, SPEC, BLOCK>) {
170        let (handler, handler_rx) = channel(1);
171        let _shutdown = Arc::new(ShutDownMultiFork { handler: Some(handler.clone()) });
172        (Self { handler, _shutdown }, MultiForkHandler::new(handler_rx))
173    }
174
175    /// Returns a fork backend.
176    ///
177    /// If no matching fork backend exists it will be created.
178    pub fn create_fork(&self, fork: CreateFork) -> eyre::Result<ForkResult<N, SPEC, BLOCK>> {
179        trace!("Creating new fork, url={}, block={:?}", fork.url, fork.evm_opts.fork_block_number);
180        let (sender, rx) = oneshot_channel();
181        let req = Request::CreateFork(Box::new(fork), sender);
182        self.handler.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?;
183        rx.recv()?
184    }
185
186    /// Rolls the block of the fork.
187    ///
188    /// If no matching fork backend exists it will be created.
189    pub fn roll_fork(&self, fork: ForkId, block: u64) -> eyre::Result<ForkResult<N, SPEC, BLOCK>> {
190        trace!(?fork, ?block, "rolling fork");
191        let (sender, rx) = oneshot_channel();
192        let req = Request::RollFork(fork, block, sender);
193        self.handler.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?;
194        rx.recv()?
195    }
196
197    /// Rolls a fork to an already resolved exact block.
198    pub fn roll_fork_exact(
199        &self,
200        fork: ForkId,
201        block: BlockNumHash,
202    ) -> eyre::Result<ForkResult<N, SPEC, BLOCK>> {
203        trace!(?fork, ?block, "rolling fork to exact block");
204        let (sender, rx) = oneshot_channel();
205        let req = Request::RollForkExact(fork, block, sender);
206        self.handler.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?;
207        rx.recv()?
208    }
209
210    /// Returns the `EvmEnv` of the given fork, if any.
211    pub fn get_evm_env(&self, fork: ForkId) -> eyre::Result<Option<EvmEnv<SPEC, BLOCK>>> {
212        trace!(?fork, "getting env config");
213        let (sender, rx) = oneshot_channel();
214        let req = Request::GetEvmEnv(fork, sender);
215        self.handler.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?;
216        Ok(rx.recv()?)
217    }
218
219    /// Updates block number and timestamp of given fork with new values.
220    pub fn update_block(&self, fork: ForkId, number: U256, timestamp: U256) -> eyre::Result<()> {
221        trace!(?fork, ?number, ?timestamp, "update fork block");
222        self.handler
223            .clone()
224            .try_send(Request::UpdateBlock(fork, number, timestamp))
225            .map_err(|e| eyre::eyre!("{:?}", e))
226    }
227
228    /// Updates the fork's entire env
229    ///
230    /// This is required for tx level forking where we need to fork off the `block - 1` state but
231    /// still need use env settings for `env`.
232    pub fn update_block_env(&self, fork: ForkId, env: BLOCK) -> eyre::Result<()>
233    where
234        BLOCK: fmt::Debug,
235    {
236        trace!(?fork, ?env, "update fork block");
237        self.handler
238            .clone()
239            .try_send(Request::UpdateEnv(fork, env))
240            .map_err(|e| eyre::eyre!("{:?}", e))
241    }
242
243    /// Returns the corresponding fork if it exists.
244    ///
245    /// Returns `None` if no matching fork backend is available.
246    pub fn get_fork(&self, id: impl Into<ForkId>) -> eyre::Result<Option<SharedBackend<N, BLOCK>>> {
247        let id = id.into();
248        trace!(?id, "get fork backend");
249        let (sender, rx) = oneshot_channel();
250        let req = Request::GetFork(id, sender);
251        self.handler.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?;
252        Ok(rx.recv()?)
253    }
254
255    /// Returns the corresponding fork url if it exists.
256    ///
257    /// Returns `None` if no matching fork is available.
258    pub fn get_fork_url(&self, id: impl Into<ForkId>) -> eyre::Result<Option<String>> {
259        let (sender, rx) = oneshot_channel();
260        let req = Request::GetForkUrl(id.into(), sender);
261        self.handler.clone().try_send(req).map_err(|e| eyre::eyre!("{:?}", e))?;
262        Ok(rx.recv()?)
263    }
264}
265
266type CreateFuture<N, SPEC, BLOCK> = Pin<
267    Box<
268        dyn Future<
269                Output = eyre::Result<(
270                    ForkId,
271                    CreatedFork<N, SPEC, BLOCK>,
272                    BackendHandler<N, BLOCK>,
273                )>,
274            > + Send,
275    >,
276>;
277type CreateSender<N, SPEC, BLOCK> = OneshotSender<eyre::Result<ForkResult<N, SPEC, BLOCK>>>;
278type GetEvmEnvSender<SPEC, BLOCK> = OneshotSender<Option<EvmEnv<SPEC, BLOCK>>>;
279
280/// Request that's send to the handler.
281#[derive(Debug)]
282enum Request<N: Network, SPEC, BLOCK: ForkBlockEnv> {
283    /// Creates a new ForkBackend.
284    CreateFork(Box<CreateFork>, CreateSender<N, SPEC, BLOCK>),
285    /// Returns the Fork backend for the `ForkId` if it exists.
286    GetFork(ForkId, OneshotSender<Option<SharedBackend<N, BLOCK>>>),
287    /// Adjusts the block that's being forked, by creating a new fork at the new block.
288    RollFork(ForkId, u64, CreateSender<N, SPEC, BLOCK>),
289    /// Adjusts the fork to an already resolved exact block.
290    RollForkExact(ForkId, BlockNumHash, CreateSender<N, SPEC, BLOCK>),
291    /// Returns the environment of the fork.
292    GetEvmEnv(ForkId, GetEvmEnvSender<SPEC, BLOCK>),
293    /// Updates the block number and timestamp of the fork.
294    UpdateBlock(ForkId, U256, U256),
295    /// Updates the block the entire block env,
296    UpdateEnv(ForkId, BLOCK),
297    /// Shutdowns the entire `MultiForkHandler`, see `ShutDownMultiFork`
298    ShutDown(OneshotSender<()>),
299    /// Returns the Fork Url for the `ForkId` if it exists.
300    GetForkUrl(ForkId, OneshotSender<Option<String>>),
301}
302
303enum ForkTask<N: Network, SPEC, BLOCK: ForkBlockEnv> {
304    /// Contains the future that will establish a new fork.
305    Create(
306        CreateFuture<N, SPEC, BLOCK>,
307        ForkId,
308        CreateSender<N, SPEC, BLOCK>,
309        Vec<CreateSender<N, SPEC, BLOCK>>,
310    ),
311}
312
313/// The type that manages connections in the background.
314#[must_use = "futures do nothing unless polled"]
315pub struct MultiForkHandler<N: Network, SPEC, BLOCK: ForkBlockEnv> {
316    /// Incoming requests from the `MultiFork`.
317    incoming: Fuse<Receiver<Request<N, SPEC, BLOCK>>>,
318
319    /// All active handlers.
320    ///
321    /// It's expected that this list will be rather small (<10).
322    handlers: Vec<(ForkId, BackendHandler<N, BLOCK>)>,
323
324    // tasks currently in progress
325    pending_tasks: Vec<ForkTask<N, SPEC, BLOCK>>,
326
327    /// All _unique_ forkids mapped to their corresponding backend.
328    ///
329    /// Note: The backend can be shared by multiple ForkIds if the target the same provider and
330    /// block number.
331    forks: HashMap<ForkId, CreatedFork<N, SPEC, BLOCK>>,
332
333    /// Optional periodic interval to flush rpc cache.
334    flush_cache_interval: Option<tokio::time::Interval>,
335}
336
337impl<
338    N: Network,
339    SPEC: Into<SpecId> + Default + Copy + 'static,
340    BLOCK: FoundryBlock + ForkBlockEnv + Default,
341> MultiForkHandler<N, SPEC, BLOCK>
342{
343    fn new(incoming: Receiver<Request<N, SPEC, BLOCK>>) -> Self {
344        Self {
345            incoming: incoming.fuse(),
346            handlers: Default::default(),
347            pending_tasks: Default::default(),
348            forks: Default::default(),
349            flush_cache_interval: None,
350        }
351    }
352
353    /// Sets the interval after which all rpc caches should be flushed periodically.
354    pub fn set_flush_cache_interval(&mut self, period: Duration) -> &mut Self {
355        self.flush_cache_interval =
356            Some(tokio::time::interval_at(tokio::time::Instant::now() + period, period));
357        self
358    }
359
360    /// Returns the list of additional senders of a matching task for the given id, if any.
361    fn find_in_progress_task(
362        &mut self,
363        id: &ForkId,
364    ) -> Option<&mut Vec<CreateSender<N, SPEC, BLOCK>>> {
365        for ForkTask::Create(_, in_progress, _, additional) in &mut self.pending_tasks {
366            if in_progress == id {
367                return Some(additional);
368            }
369        }
370        None
371    }
372
373    fn create_fork(&mut self, fork: CreateFork, sender: CreateSender<N, SPEC, BLOCK>) {
374        self.create_fork_with_identity(fork, None, sender);
375    }
376
377    fn create_fork_with_identity(
378        &mut self,
379        fork: CreateFork,
380        expected_identity: Option<ForkContext>,
381        sender: CreateSender<N, SPEC, BLOCK>,
382    ) {
383        let resolved_id =
384            fork.resolved.as_ref().map(|resolved| ForkId::resolved(&fork.url, resolved));
385        trace!(?resolved_id, "creating fork");
386
387        // Only deduplicate requests that already carry an exact identity. Unresolved requests at
388        // the same URL and height can resolve to different blocks across a reorganization.
389        if let Some(fork_id) = &resolved_id
390            && let Some(in_progress) = self.find_in_progress_task(fork_id)
391        {
392            in_progress.push(sender);
393            return;
394        }
395
396        // Need to create a new fork.
397        let task_id =
398            resolved_id.unwrap_or_else(|| ForkId::new(&fork.url, fork.evm_opts.fork_block_number));
399        let task = Box::pin(create_fork(fork, expected_identity));
400        self.pending_tasks.push(ForkTask::Create(task, task_id, sender, Vec::new()));
401    }
402
403    fn insert_new_fork(
404        &mut self,
405        fork_id: ForkId,
406        fork: CreatedFork<N, SPEC, BLOCK>,
407        sender: CreateSender<N, SPEC, BLOCK>,
408        additional_senders: Vec<CreateSender<N, SPEC, BLOCK>>,
409    ) {
410        self.forks.insert(fork_id.clone(), fork.clone());
411        let resolved = fork
412            .opts
413            .resolved
414            .as_ref()
415            .expect("created forks always retain their resolved identity")
416            .clone();
417        let _ = sender.send(Ok(ForkResult {
418            id: fork_id.clone(),
419            backend: fork.backend.clone(),
420            env: fork.evm_env.clone(),
421            resolved: resolved.clone(),
422        }));
423
424        // Notify all additional senders and track unique forkIds.
425        for sender in additional_senders {
426            let next_fork_id = fork.inc_senders(fork_id.clone());
427            self.forks.insert(next_fork_id.clone(), fork.clone());
428            let _ = sender.send(Ok(ForkResult {
429                id: next_fork_id,
430                backend: fork.backend.clone(),
431                env: fork.evm_env.clone(),
432                resolved: resolved.clone(),
433            }));
434        }
435    }
436
437    /// Update the fork's block entire env
438    fn update_env(&mut self, fork_id: ForkId, env: BLOCK) {
439        if let Some(fork) = self.forks.get_mut(&fork_id) {
440            fork.evm_env.block_env = env;
441        }
442    }
443    /// Update fork block number and timestamp. Used to preserve values set by `roll` and `warp`
444    /// cheatcodes when new fork selected.
445    fn update_block(&mut self, fork_id: ForkId, block_number: U256, block_timestamp: U256) {
446        if let Some(fork) = self.forks.get_mut(&fork_id) {
447            fork.evm_env.block_env.set_number(block_number);
448            fork.evm_env.block_env.set_timestamp(block_timestamp);
449        }
450    }
451
452    fn on_request(&mut self, req: Request<N, SPEC, BLOCK>) {
453        match req {
454            Request::CreateFork(fork, sender) => self.create_fork(*fork, sender),
455            Request::GetFork(fork_id, sender) => {
456                let fork = self.forks.get(&fork_id).map(|f| f.backend.clone());
457                let _ = sender.send(fork);
458            }
459            Request::RollFork(fork_id, block, sender) => {
460                if let Some(fork) = self.forks.get(&fork_id) {
461                    trace!(target: "fork::multi", "rolling {} to {}", fork_id, block);
462                    let expected_identity = fork.opts.resolved.as_ref().map(ResolvedFork::context);
463                    let mut opts = fork.opts.clone();
464                    opts.evm_opts.fork_block_number = Some(block);
465                    opts.evm_opts.fork_block_number_is_inferred = false;
466                    opts.resolved = None;
467                    self.create_fork_with_identity(opts, expected_identity, sender)
468                } else {
469                    let _ =
470                        sender.send(Err(eyre::eyre!("No matching fork exists for {}", fork_id)));
471                }
472            }
473            Request::RollForkExact(fork_id, block, sender) => {
474                if let Some(fork) = self.forks.get(&fork_id) {
475                    trace!(target: "fork::multi", "rolling {} to exact block {:?}", fork_id, block);
476                    let mut opts = fork.opts.clone();
477                    opts.evm_opts.fork_block_number = Some(block.number);
478                    opts.evm_opts.fork_block_number_is_inferred = false;
479                    opts.resolved = Some(
480                        opts.resolved
481                            .as_ref()
482                            .expect("an exact roll requires an existing resolved fork")
483                            .at_block(block),
484                    );
485                    self.create_fork(opts, sender)
486                } else {
487                    let _ =
488                        sender.send(Err(eyre::eyre!("No matching fork exists for {}", fork_id)));
489                }
490            }
491            Request::GetEvmEnv(fork_id, sender) => {
492                let _ = sender.send(self.forks.get(&fork_id).map(|fork| fork.evm_env.clone()));
493            }
494            Request::UpdateBlock(fork_id, block_number, block_timestamp) => {
495                self.update_block(fork_id, block_number, block_timestamp);
496            }
497            Request::UpdateEnv(fork_id, block_env) => {
498                self.update_env(fork_id, block_env);
499            }
500            Request::ShutDown(sender) => {
501                trace!(target: "fork::multi", "received shutdown signal");
502                // We're emptying all fork backends, this way we ensure all caches get flushed.
503                self.forks.clear();
504                self.handlers.clear();
505                let _ = sender.send(());
506            }
507            Request::GetForkUrl(fork_id, sender) => {
508                let fork = self.forks.get(&fork_id).map(|f| f.opts.url.clone());
509                let _ = sender.send(fork);
510            }
511        }
512    }
513}
514
515// Drives all handler to completion.
516// This future will finish once all underlying BackendHandler are completed.
517impl<
518    N: Network,
519    SPEC: Into<SpecId> + Default + Copy + Unpin + 'static,
520    BLOCK: FoundryBlock + ForkBlockEnv + Default + Unpin,
521> Future for MultiForkHandler<N, SPEC, BLOCK>
522{
523    type Output = ();
524
525    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
526        let this = self.get_mut();
527
528        // Receive new requests.
529        loop {
530            match this.incoming.poll_next_unpin(cx) {
531                Poll::Ready(Some(req)) => this.on_request(req),
532                Poll::Ready(None) => {
533                    // Channel closed, but we still need to drive the fork handlers to completion.
534                    trace!(target: "fork::multi", "request channel closed");
535                    break;
536                }
537                Poll::Pending => break,
538            }
539        }
540
541        // Advance all tasks.
542        for n in (0..this.pending_tasks.len()).rev() {
543            let task = this.pending_tasks.swap_remove(n);
544            match task {
545                ForkTask::Create(mut fut, id, sender, additional_senders) => {
546                    if let Poll::Ready(resp) = fut.poll_unpin(cx) {
547                        match resp {
548                            Ok((fork_id, fork, handler)) => {
549                                if let Some(fork) = this.forks.get(&fork_id).cloned() {
550                                    this.insert_new_fork(
551                                        fork.inc_senders(fork_id),
552                                        fork,
553                                        sender,
554                                        additional_senders,
555                                    );
556                                } else {
557                                    this.handlers.push((fork_id.clone(), handler));
558                                    this.insert_new_fork(fork_id, fork, sender, additional_senders);
559                                }
560                            }
561                            Err(err) => {
562                                let _ = sender.send(Err(eyre::eyre!("{err}")));
563                                for sender in additional_senders {
564                                    let _ = sender.send(Err(eyre::eyre!("{err}")));
565                                }
566                            }
567                        }
568                    } else {
569                        this.pending_tasks.push(ForkTask::Create(
570                            fut,
571                            id,
572                            sender,
573                            additional_senders,
574                        ));
575                    }
576                }
577            }
578        }
579
580        // Advance all handlers.
581        for n in (0..this.handlers.len()).rev() {
582            let (id, mut handler) = this.handlers.swap_remove(n);
583            match handler.poll_unpin(cx) {
584                Poll::Ready(_) => {
585                    trace!(target: "fork::multi", "fork {:?} completed", id);
586                }
587                Poll::Pending => {
588                    this.handlers.push((id, handler));
589                }
590            }
591        }
592
593        if this.handlers.is_empty() && this.incoming.is_done() {
594            trace!(target: "fork::multi", "completed");
595            return Poll::Ready(());
596        }
597
598        // Periodically flush cached RPC state.
599        if this
600            .flush_cache_interval
601            .as_mut()
602            .map(|interval| interval.poll_tick(cx).is_ready())
603            .unwrap_or_default()
604            && !this.forks.is_empty()
605        {
606            trace!(target: "fork::multi", "tick flushing caches");
607            let forks = this.forks.values().map(|f| f.backend.clone()).collect::<Vec<_>>();
608            // Flush this on new thread to not block here.
609            std::thread::Builder::new()
610                .name("flusher".into())
611                .spawn(move || {
612                    for fork in forks {
613                        fork.flush_cache();
614                    }
615                })
616                .expect("failed to spawn thread");
617        }
618
619        Poll::Pending
620    }
621}
622
623/// Tracks the created Fork
624#[derive(Debug, Clone)]
625struct CreatedFork<N: Network, SPEC, BLOCK: ForkBlockEnv> {
626    /// How the fork was initially created.
627    opts: CreateFork,
628    /// The resolved EVM environment (fetched from the provider).
629    evm_env: EvmEnv<SPEC, BLOCK>,
630    /// Copy of the sender.
631    backend: SharedBackend<N, BLOCK>,
632    /// How many consumers there are, since a `SharedBacked` can be used by multiple
633    /// consumers.
634    num_senders: Arc<AtomicUsize>,
635}
636
637impl<N: Network, SPEC, BLOCK: ForkBlockEnv> CreatedFork<N, SPEC, BLOCK> {
638    pub fn new(
639        opts: CreateFork,
640        evm_env: EvmEnv<SPEC, BLOCK>,
641        backend: SharedBackend<N, BLOCK>,
642    ) -> Self {
643        Self { opts, evm_env, backend, num_senders: Arc::new(AtomicUsize::new(1)) }
644    }
645
646    /// Increment senders and return unique identifier of the fork.
647    fn inc_senders(&self, fork_id: ForkId) -> ForkId {
648        format!(
649            "{}-{}",
650            fork_id.as_str(),
651            self.num_senders.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
652        )
653        .into()
654    }
655}
656
657/// A type that's used to signaling the `MultiForkHandler` when it's time to shut down.
658///
659/// This is essentially a sync on drop, so that the `MultiForkHandler` can flush all rpc cashes.
660///
661/// This type intentionally does not implement `Clone` since it's intended that there's only once
662/// instance.
663#[derive(Debug)]
664struct ShutDownMultiFork<N: Network, SPEC, BLOCK: ForkBlockEnv> {
665    handler: Option<Sender<Request<N, SPEC, BLOCK>>>,
666}
667
668impl<N: Network, SPEC, BLOCK: ForkBlockEnv> Drop for ShutDownMultiFork<N, SPEC, BLOCK> {
669    fn drop(&mut self) {
670        trace!(target: "fork::multi", "initiating shutdown");
671        let (sender, rx) = oneshot_channel();
672        let req = Request::ShutDown(sender);
673        if let Some(mut handler) = self.handler.take()
674            && handler.try_send(req).is_ok()
675        {
676            let _ = rx.recv();
677            trace!(target: "fork::cache", "multifork backend shutdown");
678        }
679    }
680}
681
682/// Creates a new fork.
683///
684/// This will establish a new `Provider` to the endpoint and return the Fork Backend.
685async fn create_fork<
686    N: Network,
687    SPEC: Into<SpecId> + Default + Copy,
688    BLOCK: FoundryBlock + ForkBlockEnv + Default,
689>(
690    mut fork: CreateFork,
691    expected_identity: Option<ForkContext>,
692) -> eyre::Result<(ForkId, CreatedFork<N, SPEC, BLOCK>, BackendHandler<N, BLOCK>)> {
693    // Ensure evm_opts reflects the fork URL (may differ from the resolved CreateFork url when
694    // created via cheatcodes, where evm_opts is cloned from the base config).
695    let execution_networks = fork.evm_opts.networks;
696    let require_endpoint_family_match =
697        fork.evm_opts.fork_network_is_inferred || !execution_networks.has_network_selection();
698    let targets_new_endpoint =
699        fork.evm_opts.fork_url.as_ref().is_some_and(|endpoint| endpoint != &fork.url)
700            || fork
701                .evm_opts
702                .fork_endpoint
703                .as_ref()
704                .is_some_and(|identity| identity.endpoint != fork.url);
705    if targets_new_endpoint {
706        // The EVM implementation is already fixed, so use its family as the fallback for a custom
707        // endpoint without metadata. Clear identity and chain values inferred from the old URL;
708        // authoritative metadata from the new endpoint is still checked below.
709        fork.evm_opts.fork_endpoint = None;
710        fork.evm_opts.expected_fork_endpoint = None;
711        fork.evm_opts.fork_network_is_inferred = false;
712        if fork.evm_opts.fork_chain_id_is_inferred {
713            fork.evm_opts.env.chain_id = None;
714            fork.evm_opts.fork_chain_id_is_inferred = false;
715        }
716        if fork.evm_opts.fork_block_number_is_inferred {
717            fork.evm_opts.fork_block_number = None;
718            fork.evm_opts.fork_block_number_is_inferred = false;
719        }
720    }
721    fork.evm_opts.fork_url = Some(fork.url.clone());
722
723    // Initialise the fork environment.
724    // Here we use [`AnyNetwork`] to maximize compatibility with custom chains, aligned with
725    // `EvmOpts::env` impl.
726    let any_provider = fork.evm_opts.fork_provider_with_url::<AnyNetwork>(&fork.url)?;
727    let (evm_env, resolved) = if let Some(resolved) = fork.resolved.clone() {
728        let evm_env = fork
729            .evm_opts
730            .fork_evm_env_at_resolved::<_, BLOCK, _, _>(&any_provider, &resolved)
731            .await?;
732        (evm_env, resolved)
733    } else {
734        let (evm_env, resolved) =
735            fork.evm_opts.fork_evm_env_resolved::<_, BLOCK, _, _>(&any_provider).await?;
736        (evm_env, resolved)
737    };
738    let fork_context = resolved.context();
739    if require_endpoint_family_match
740        && !execution_networks.supports_fork_source(&fork_context.network_profile)
741    {
742        eyre::bail!(
743            "cannot create a `{}` fork with an EVM instantiated for `{}`",
744            fork_context.network,
745            execution_networks.execution_network()
746        );
747    }
748    if let Some(expected) = expected_identity {
749        eyre::ensure!(
750            fork_context.has_same_endpoint_identity(expected),
751            "fork endpoint identity changed while the fork was being rolled"
752        );
753    }
754    let number = resolved.number();
755    let meta = BlockchainDbMeta::new(evm_env.block_env.clone(), fork.url.clone())
756        .with_fork_identity(resolved.hash(), resolved.source_id());
757
758    // Determine the cache path if caching is enabled.
759    let cache_path = if fork.enable_caching {
760        Config::foundry_block_cache_dir(fork_context.source_chain_id, number)
761    } else {
762        None
763    };
764
765    let provider = fork.evm_opts.fork_provider_with_url::<N>(&fork.url)?;
766    let db = BlockchainDb::new(meta, cache_path);
767    let anchor = ForkBlock::with_rpc_number(
768        evm_env.block_env.number().saturating_to(),
769        resolved.number(),
770        resolved.hash(),
771    );
772    let (backend, handler) = SharedBackend::new_with_anchor(provider, db, anchor)?;
773    let fork_id = ForkId::resolved(&fork.url, &resolved);
774    fork.resolved = Some(resolved);
775    let fork = CreatedFork::new(fork, evm_env, backend);
776
777    Ok((fork_id, fork, handler))
778}
779
780#[cfg(test)]
781mod tests {
782    use super::*;
783    use alloy_primitives::B256;
784    use foundry_evm_networks::{NetworkConfigs, NetworkVariant};
785
786    fn context(block_number: u64) -> ForkContext {
787        ForkContext {
788            execution_chain_id: 1,
789            source_chain_id: 1,
790            network: NetworkVariant::Ethereum,
791            network_profile: NetworkConfigs::default(),
792            block_number,
793            hardfork: None,
794            instance_id: None,
795            source_fork_block_number: None,
796            source_fork_block_hash: None,
797        }
798    }
799
800    #[test]
801    fn resolved_fork_ids_include_hash_and_source_identity() {
802        let url = "http://localhost:8545";
803        let first = ResolvedFork::new(
804            url,
805            None,
806            None,
807            Some(1),
808            BlockNumHash::new(1, B256::with_last_byte(1)),
809            context(1),
810        );
811        let replacement = ResolvedFork::new(
812            url,
813            None,
814            None,
815            Some(1),
816            BlockNumHash::new(1, B256::with_last_byte(2)),
817            context(1),
818        );
819        let authenticated = ResolvedFork::new(
820            url,
821            Some(&["Authorization: secret".to_string()]),
822            None,
823            Some(1),
824            BlockNumHash::new(1, B256::with_last_byte(1)),
825            context(1),
826        );
827
828        assert_ne!(ForkId::resolved(url, &first), ForkId::resolved(url, &replacement));
829        assert_ne!(ForkId::resolved(url, &first), ForkId::resolved(url, &authenticated));
830    }
831}