anvil/
lib.rs

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