anvil/
lib.rs

1//! Anvil is a fast local Ethereum development node.
2
3#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
4#![cfg_attr(not(test), warn(unused_crate_dependencies))]
5
6use crate::{
7    eth::{
8        EthApi,
9        backend::{info::StorageInfo, mem},
10        fees::{FeeHistoryService, FeeManager},
11        miner::{Miner, MiningMode},
12        pool::Pool,
13        sign::{DevSigner, Signer as EthSigner},
14    },
15    filter::Filters,
16    logging::{LoggingManager, NodeLogLayer},
17    server::error::{NodeError, NodeResult},
18    service::NodeService,
19    shutdown::Signal,
20    tasks::TaskManager,
21};
22use alloy_eips::eip7840::BlobParams;
23use alloy_primitives::{Address, U256};
24use alloy_signer_local::PrivateKeySigner;
25use eth::backend::fork::ClientFork;
26use eyre::Result;
27use foundry_common::provider::{ProviderBuilder, RetryProvider};
28use futures::{FutureExt, TryFutureExt};
29use parking_lot::Mutex;
30use revm::primitives::hardfork::SpecId;
31use server::try_spawn_ipc;
32use std::{
33    net::SocketAddr,
34    pin::Pin,
35    sync::Arc,
36    task::{Context, Poll},
37};
38use tokio::{
39    runtime::Handle,
40    task::{JoinError, JoinHandle},
41};
42
43/// contains the background service that drives the node
44mod service;
45
46mod config;
47pub use config::{
48    AccountGenerator, CHAIN_ID, DEFAULT_GAS_LIMIT, ForkChoice, NodeConfig, VERSION_MESSAGE,
49};
50
51mod hardfork;
52pub use alloy_hardforks::EthereumHardfork;
53/// ethereum related implementations
54pub mod eth;
55/// Evm related abstractions
56mod evm;
57pub use evm::{PrecompileFactory, inject_precompiles};
58
59/// support for polling filters
60pub mod filter;
61/// commandline output
62pub mod logging;
63/// types for subscriptions
64pub mod pubsub;
65/// axum RPC server implementations
66pub mod server;
67/// Futures for shutdown signal
68mod shutdown;
69/// additional task management
70mod tasks;
71
72/// contains cli command
73#[cfg(feature = "cmd")]
74pub mod cmd;
75
76#[cfg(feature = "cmd")]
77pub mod args;
78
79#[cfg(feature = "cmd")]
80pub mod opts;
81
82#[macro_use]
83extern crate foundry_common;
84
85#[macro_use]
86extern crate tracing;
87
88/// Creates the node and runs the server.
89///
90/// Returns the [EthApi] that can be used to interact with the node and the [JoinHandle] of the
91/// task.
92///
93/// # Panics
94///
95/// Panics if any error occurs. For a non-panicking version, use [`try_spawn`].
96///
97///
98/// # Examples
99///
100/// ```no_run
101/// # use anvil::NodeConfig;
102/// # async fn spawn() -> eyre::Result<()> {
103/// let config = NodeConfig::default();
104/// let (api, handle) = anvil::spawn(config).await;
105///
106/// // use api
107///
108/// // wait forever
109/// handle.await.unwrap().unwrap();
110/// # Ok(())
111/// # }
112/// ```
113pub async fn spawn(config: NodeConfig) -> (EthApi, NodeHandle) {
114    try_spawn(config).await.expect("failed to spawn node")
115}
116
117/// Creates the node and runs the server
118///
119/// Returns the [EthApi] that can be used to interact with the node and the [JoinHandle] of the
120/// task.
121///
122/// # Examples
123///
124/// ```no_run
125/// # use anvil::NodeConfig;
126/// # async fn spawn() -> eyre::Result<()> {
127/// let config = NodeConfig::default();
128/// let (api, handle) = anvil::try_spawn(config).await?;
129///
130/// // use api
131///
132/// // wait forever
133/// handle.await??;
134/// # Ok(())
135/// # }
136/// ```
137pub async fn try_spawn(mut config: NodeConfig) -> Result<(EthApi, NodeHandle)> {
138    let logger = if config.enable_tracing { init_tracing() } else { Default::default() };
139    logger.set_enabled(!config.silent);
140
141    let backend = Arc::new(config.setup().await?);
142
143    if config.enable_auto_impersonate {
144        backend.auto_impersonate_account(true);
145    }
146
147    let fork = backend.get_fork();
148
149    let NodeConfig {
150        signer_accounts,
151        block_time,
152        port,
153        max_transactions,
154        server_config,
155        no_mining,
156        transaction_order,
157        genesis,
158        mixed_mining,
159        ..
160    } = config.clone();
161
162    let pool = Arc::new(Pool::default());
163
164    let mode = if let Some(block_time) = block_time {
165        if mixed_mining {
166            let listener = pool.add_ready_listener();
167            MiningMode::mixed(max_transactions, listener, block_time)
168        } else {
169            MiningMode::interval(block_time)
170        }
171    } else if no_mining {
172        MiningMode::None
173    } else {
174        // get a listener for ready transactions
175        let listener = pool.add_ready_listener();
176        MiningMode::instant(max_transactions, listener)
177    };
178
179    let miner = match &fork {
180        Some(fork) => {
181            Miner::new(mode).with_forced_transactions(fork.config.read().force_transactions.clone())
182        }
183        _ => Miner::new(mode),
184    };
185
186    let dev_signer: Box<dyn EthSigner> = Box::new(DevSigner::new(signer_accounts));
187    let mut signers = vec![dev_signer];
188    if let Some(genesis) = genesis {
189        let genesis_signers = genesis
190            .alloc
191            .values()
192            .filter_map(|acc| acc.private_key)
193            .flat_map(|k| PrivateKeySigner::from_bytes(&k))
194            .collect::<Vec<_>>();
195        if !genesis_signers.is_empty() {
196            signers.push(Box::new(DevSigner::new(genesis_signers)));
197        }
198    }
199
200    let fee_history_cache = Arc::new(Mutex::new(Default::default()));
201    let fee_history_service = FeeHistoryService::new(
202        match backend.spec_id() {
203            SpecId::OSAKA => BlobParams::osaka(),
204            SpecId::PRAGUE => BlobParams::prague(),
205            _ => BlobParams::cancun(),
206        },
207        backend.new_block_notifications(),
208        Arc::clone(&fee_history_cache),
209        StorageInfo::new(Arc::clone(&backend)),
210    );
211    // create an entry for the best block
212    if let Some(header) = backend.get_block(backend.best_number()).map(|block| block.header) {
213        fee_history_service.insert_cache_entry_for_block(header.hash_slow(), &header);
214    }
215
216    let filters = Filters::default();
217
218    // create the cloneable api wrapper
219    let api = EthApi::new(
220        Arc::clone(&pool),
221        Arc::clone(&backend),
222        Arc::new(signers),
223        fee_history_cache,
224        fee_history_service.fee_history_limit(),
225        miner.clone(),
226        logger,
227        filters.clone(),
228        transaction_order,
229    );
230
231    // spawn the node service
232    let node_service =
233        tokio::task::spawn(NodeService::new(pool, backend, miner, fee_history_service, filters));
234
235    let mut servers = Vec::with_capacity(config.host.len());
236    let mut addresses = Vec::with_capacity(config.host.len());
237
238    for addr in &config.host {
239        let sock_addr = SocketAddr::new(*addr, port);
240
241        // Create a TCP listener.
242        let tcp_listener = tokio::net::TcpListener::bind(sock_addr).await?;
243        addresses.push(tcp_listener.local_addr()?);
244
245        // Spawn the server future on a new task.
246        let srv = server::serve_on(tcp_listener, api.clone(), server_config.clone());
247        servers.push(tokio::task::spawn(srv.map_err(Into::into)));
248    }
249
250    let tokio_handle = Handle::current();
251    let (signal, on_shutdown) = shutdown::signal();
252    let task_manager = TaskManager::new(tokio_handle, on_shutdown);
253
254    let ipc_task =
255        config.get_ipc_path().map(|path| try_spawn_ipc(api.clone(), path)).transpose()?;
256
257    let handle = NodeHandle {
258        config,
259        node_service,
260        servers,
261        ipc_task,
262        addresses,
263        _signal: Some(signal),
264        task_manager,
265    };
266
267    handle.print(fork.as_ref())?;
268
269    Ok((api, handle))
270}
271
272type IpcTask = JoinHandle<()>;
273
274/// A handle to the spawned node and server tasks.
275///
276/// This future will resolve if either the node or server task resolve/fail.
277pub struct NodeHandle {
278    config: NodeConfig,
279    /// The address of the running rpc server.
280    addresses: Vec<SocketAddr>,
281    /// Join handle for the Node Service.
282    pub node_service: JoinHandle<Result<(), NodeError>>,
283    /// Join handles (one per socket) for the Anvil server.
284    pub servers: Vec<JoinHandle<Result<(), NodeError>>>,
285    /// The future that joins the ipc server, if any.
286    ipc_task: Option<IpcTask>,
287    /// A signal that fires the shutdown, fired on drop.
288    _signal: Option<Signal>,
289    /// A task manager that can be used to spawn additional tasks.
290    task_manager: TaskManager,
291}
292
293impl Drop for NodeHandle {
294    fn drop(&mut self) {
295        // Fire shutdown signal to make sure anvil instance is terminated.
296        if let Some(signal) = self._signal.take() {
297            let _ = signal.fire();
298        }
299    }
300}
301
302impl NodeHandle {
303    /// The [NodeConfig] the node was launched with.
304    pub fn config(&self) -> &NodeConfig {
305        &self.config
306    }
307
308    /// Prints the launch info.
309    pub(crate) fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
310        self.config.print(fork)?;
311        if !self.config.silent {
312            if let Some(ipc_path) = self.ipc_path() {
313                sh_println!("IPC path: {ipc_path}")?;
314            }
315            sh_println!(
316                "Listening on {}",
317                self.addresses
318                    .iter()
319                    .map(|addr| { addr.to_string() })
320                    .collect::<Vec<String>>()
321                    .join(", ")
322            )?;
323        }
324        Ok(())
325    }
326
327    /// The address of the launched server.
328    ///
329    /// **N.B.** this may not necessarily be the same `host + port` as configured in the
330    /// `NodeConfig`, if port was set to 0, then the OS auto picks an available port.
331    pub fn socket_address(&self) -> &SocketAddr {
332        &self.addresses[0]
333    }
334
335    /// Returns the http endpoint.
336    pub fn http_endpoint(&self) -> String {
337        format!("http://{}", self.socket_address())
338    }
339
340    /// Returns the websocket endpoint.
341    pub fn ws_endpoint(&self) -> String {
342        format!("ws://{}", self.socket_address())
343    }
344
345    /// Returns the path of the launched ipc server, if any.
346    pub fn ipc_path(&self) -> Option<String> {
347        self.config.get_ipc_path()
348    }
349
350    /// Constructs a [`RetryProvider`] for this handle's HTTP endpoint.
351    pub fn http_provider(&self) -> RetryProvider {
352        ProviderBuilder::new(&self.http_endpoint()).build().expect("failed to build HTTP provider")
353    }
354
355    /// Constructs a [`RetryProvider`] for this handle's WS endpoint.
356    pub fn ws_provider(&self) -> RetryProvider {
357        ProviderBuilder::new(&self.ws_endpoint()).build().expect("failed to build WS provider")
358    }
359
360    /// Constructs a [`RetryProvider`] for this handle's IPC endpoint, if any.
361    pub fn ipc_provider(&self) -> Option<RetryProvider> {
362        ProviderBuilder::new(&self.config.get_ipc_path()?).build().ok()
363    }
364
365    /// Signer accounts that can sign messages/transactions from the EVM node.
366    pub fn dev_accounts(&self) -> impl Iterator<Item = Address> + '_ {
367        self.config.signer_accounts.iter().map(|wallet| wallet.address())
368    }
369
370    /// Signer accounts that can sign messages/transactions from the EVM node.
371    pub fn dev_wallets(&self) -> impl Iterator<Item = PrivateKeySigner> + '_ {
372        self.config.signer_accounts.iter().cloned()
373    }
374
375    /// Accounts that will be initialised with `genesis_balance` in the genesis block.
376    pub fn genesis_accounts(&self) -> impl Iterator<Item = Address> + '_ {
377        self.config.genesis_accounts.iter().map(|w| w.address())
378    }
379
380    /// Native token balance of every genesis account in the genesis block.
381    pub fn genesis_balance(&self) -> U256 {
382        self.config.genesis_balance
383    }
384
385    /// Default gas price for all txs.
386    pub fn gas_price(&self) -> u128 {
387        self.config.get_gas_price()
388    }
389
390    /// Returns the shutdown signal.
391    pub fn shutdown_signal(&self) -> &Option<Signal> {
392        &self._signal
393    }
394
395    /// Returns mutable access to the shutdown signal.
396    ///
397    /// This can be used to extract the Signal.
398    pub fn shutdown_signal_mut(&mut self) -> &mut Option<Signal> {
399        &mut self._signal
400    }
401
402    /// Returns the task manager that can be used to spawn new tasks.
403    ///
404    /// ```
405    /// use anvil::NodeHandle;
406    /// # fn t(handle: NodeHandle) {
407    /// let task_manager = handle.task_manager();
408    /// let on_shutdown = task_manager.on_shutdown();
409    ///
410    /// task_manager.spawn(async move {
411    ///     on_shutdown.await;
412    ///     // do something
413    /// });
414    ///
415    /// # }
416    /// ```
417    pub fn task_manager(&self) -> &TaskManager {
418        &self.task_manager
419    }
420}
421
422impl Future for NodeHandle {
423    type Output = Result<NodeResult<()>, JoinError>;
424
425    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
426        let pin = self.get_mut();
427
428        // poll the ipc task
429        if let Some(mut ipc) = pin.ipc_task.take() {
430            if let Poll::Ready(res) = ipc.poll_unpin(cx) {
431                return Poll::Ready(res.map(|()| Ok(())));
432            } else {
433                pin.ipc_task = Some(ipc);
434            }
435        }
436
437        // poll the node service task
438        if let Poll::Ready(res) = pin.node_service.poll_unpin(cx) {
439            return Poll::Ready(res);
440        }
441
442        // poll the axum server handles
443        for server in &mut pin.servers {
444            if let Poll::Ready(res) = server.poll_unpin(cx) {
445                return Poll::Ready(res);
446            }
447        }
448
449        Poll::Pending
450    }
451}
452
453#[doc(hidden)]
454pub fn init_tracing() -> LoggingManager {
455    use tracing_subscriber::prelude::*;
456
457    let manager = LoggingManager::default();
458    // check whether `RUST_LOG` is explicitly set
459    let _ = if std::env::var("RUST_LOG").is_ok() {
460        tracing_subscriber::Registry::default()
461            .with(tracing_subscriber::EnvFilter::from_default_env())
462            .with(tracing_subscriber::fmt::layer())
463            .try_init()
464    } else {
465        tracing_subscriber::Registry::default()
466            .with(NodeLogLayer::new(manager.clone()))
467            .with(
468                tracing_subscriber::fmt::layer()
469                    .without_time()
470                    .with_target(false)
471                    .with_level(false),
472            )
473            .try_init()
474    };
475
476    manager
477}