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