1#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
4
5use crate::{
6 eth::{
7 backend::{info::StorageInfo, mem},
8 fees::{FeeHistoryService, FeeManager},
9 miner::{Miner, MiningMode},
10 pool::Pool,
11 sign::{DevSigner, Signer as EthSigner},
12 EthApi,
13 },
14 filter::Filters,
15 logging::{LoggingManager, NodeLogLayer},
16 server::error::{NodeError, NodeResult},
17 service::NodeService,
18 shutdown::Signal,
19 tasks::TaskManager,
20};
21use alloy_primitives::{Address, U256};
22use alloy_signer_local::PrivateKeySigner;
23use eth::backend::fork::ClientFork;
24use eyre::Result;
25use foundry_common::provider::{ProviderBuilder, RetryProvider};
26use foundry_evm::revm;
27use futures::{FutureExt, TryFutureExt};
28use parking_lot::Mutex;
29use server::try_spawn_ipc;
30use std::{
31 future::Future,
32 net::SocketAddr,
33 pin::Pin,
34 sync::Arc,
35 task::{Context, Poll},
36};
37use tokio::{
38 runtime::Handle,
39 task::{JoinError, JoinHandle},
40};
41
42mod service;
44
45mod config;
46pub use config::{
47 AccountGenerator, ForkChoice, NodeConfig, CHAIN_ID, DEFAULT_GAS_LIMIT, VERSION_MESSAGE,
48};
49
50mod hardfork;
51pub use hardfork::EthereumHardfork;
52
53pub mod eth;
55mod evm;
57pub use evm::{inject_precompiles, PrecompileFactory};
58pub mod filter;
60pub mod logging;
62pub mod pubsub;
64pub mod server;
66mod shutdown;
68mod tasks;
70
71#[cfg(feature = "cmd")]
73pub mod cmd;
74
75#[cfg(feature = "cmd")]
76pub mod args;
77
78#[cfg(feature = "cmd")]
79pub mod opts;
80
81#[macro_use]
82extern crate foundry_common;
83
84#[macro_use]
85extern crate tracing;
86
87pub async fn spawn(config: NodeConfig) -> (EthApi, NodeHandle) {
113 try_spawn(config).await.expect("failed to spawn node")
114}
115
116pub async fn try_spawn(mut config: NodeConfig) -> Result<(EthApi, NodeHandle)> {
137 let logger = if config.enable_tracing { init_tracing() } else { Default::default() };
138 logger.set_enabled(!config.silent);
139
140 let backend = Arc::new(config.setup().await?);
141
142 if config.enable_auto_impersonate {
143 backend.auto_impersonate_account(true);
144 }
145
146 let fork = backend.get_fork();
147
148 let NodeConfig {
149 signer_accounts,
150 block_time,
151 port,
152 max_transactions,
153 server_config,
154 no_mining,
155 transaction_order,
156 genesis,
157 mixed_mining,
158 ..
159 } = config.clone();
160
161 let pool = Arc::new(Pool::default());
162
163 let mode = if let Some(block_time) = block_time {
164 if mixed_mining {
165 let listener = pool.add_ready_listener();
166 MiningMode::mixed(max_transactions, listener, block_time)
167 } else {
168 MiningMode::interval(block_time)
169 }
170 } else if no_mining {
171 MiningMode::None
172 } else {
173 let listener = pool.add_ready_listener();
175 MiningMode::instant(max_transactions, listener)
176 };
177
178 let miner = match &fork {
179 Some(fork) => {
180 Miner::new(mode).with_forced_transactions(fork.config.read().force_transactions.clone())
181 }
182 _ => Miner::new(mode),
183 };
184
185 let dev_signer: Box<dyn EthSigner> = Box::new(DevSigner::new(signer_accounts));
186 let mut signers = vec![dev_signer];
187 if let Some(genesis) = genesis {
188 let genesis_signers = genesis
189 .alloc
190 .values()
191 .filter_map(|acc| acc.private_key)
192 .flat_map(|k| PrivateKeySigner::from_bytes(&k))
193 .collect::<Vec<_>>();
194 if !genesis_signers.is_empty() {
195 signers.push(Box::new(DevSigner::new(genesis_signers)));
196 }
197 }
198
199 let fee_history_cache = Arc::new(Mutex::new(Default::default()));
200 let fee_history_service = FeeHistoryService::new(
201 backend.new_block_notifications(),
202 Arc::clone(&fee_history_cache),
203 StorageInfo::new(Arc::clone(&backend)),
204 );
205 if let Some(header) = backend.get_block(backend.best_number()).map(|block| block.header) {
207 fee_history_service.insert_cache_entry_for_block(header.hash_slow(), &header);
208 }
209
210 let filters = Filters::default();
211
212 let api = EthApi::new(
214 Arc::clone(&pool),
215 Arc::clone(&backend),
216 Arc::new(signers),
217 fee_history_cache,
218 fee_history_service.fee_history_limit(),
219 miner.clone(),
220 logger,
221 filters.clone(),
222 transaction_order,
223 );
224
225 let node_service =
227 tokio::task::spawn(NodeService::new(pool, backend, miner, fee_history_service, filters));
228
229 let mut servers = Vec::with_capacity(config.host.len());
230 let mut addresses = Vec::with_capacity(config.host.len());
231
232 for addr in &config.host {
233 let sock_addr = SocketAddr::new(*addr, port);
234
235 let tcp_listener = tokio::net::TcpListener::bind(sock_addr).await?;
237 addresses.push(tcp_listener.local_addr()?);
238
239 let srv = server::serve_on(tcp_listener, api.clone(), server_config.clone());
241 servers.push(tokio::task::spawn(srv.map_err(Into::into)));
242 }
243
244 let tokio_handle = Handle::current();
245 let (signal, on_shutdown) = shutdown::signal();
246 let task_manager = TaskManager::new(tokio_handle, on_shutdown);
247
248 let ipc_task =
249 config.get_ipc_path().map(|path| try_spawn_ipc(api.clone(), path)).transpose()?;
250
251 let handle = NodeHandle {
252 config,
253 node_service,
254 servers,
255 ipc_task,
256 addresses,
257 _signal: Some(signal),
258 task_manager,
259 };
260
261 handle.print(fork.as_ref())?;
262
263 Ok((api, handle))
264}
265
266type IpcTask = JoinHandle<()>;
267
268pub struct NodeHandle {
272 config: NodeConfig,
273 addresses: Vec<SocketAddr>,
275 pub node_service: JoinHandle<Result<(), NodeError>>,
277 pub servers: Vec<JoinHandle<Result<(), NodeError>>>,
279 ipc_task: Option<IpcTask>,
281 _signal: Option<Signal>,
283 task_manager: TaskManager,
285}
286
287impl Drop for NodeHandle {
288 fn drop(&mut self) {
289 if let Some(signal) = self._signal.take() {
291 let _ = signal.fire();
292 }
293 }
294}
295
296impl NodeHandle {
297 pub fn config(&self) -> &NodeConfig {
299 &self.config
300 }
301
302 pub(crate) fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
304 self.config.print(fork)?;
305 if !self.config.silent {
306 if let Some(ipc_path) = self.ipc_path() {
307 sh_println!("IPC path: {ipc_path}")?;
308 }
309 sh_println!(
310 "Listening on {}",
311 self.addresses
312 .iter()
313 .map(|addr| { addr.to_string() })
314 .collect::<Vec<String>>()
315 .join(", ")
316 )?;
317 }
318 Ok(())
319 }
320
321 pub fn socket_address(&self) -> &SocketAddr {
326 &self.addresses[0]
327 }
328
329 pub fn http_endpoint(&self) -> String {
331 format!("http://{}", self.socket_address())
332 }
333
334 pub fn ws_endpoint(&self) -> String {
336 format!("ws://{}", self.socket_address())
337 }
338
339 pub fn ipc_path(&self) -> Option<String> {
341 self.config.get_ipc_path()
342 }
343
344 pub fn http_provider(&self) -> RetryProvider {
346 ProviderBuilder::new(&self.http_endpoint()).build().expect("failed to build HTTP provider")
347 }
348
349 pub fn ws_provider(&self) -> RetryProvider {
351 ProviderBuilder::new(&self.ws_endpoint()).build().expect("failed to build WS provider")
352 }
353
354 pub fn ipc_provider(&self) -> Option<RetryProvider> {
356 ProviderBuilder::new(&self.config.get_ipc_path()?).build().ok()
357 }
358
359 pub fn dev_accounts(&self) -> impl Iterator<Item = Address> + '_ {
361 self.config.signer_accounts.iter().map(|wallet| wallet.address())
362 }
363
364 pub fn dev_wallets(&self) -> impl Iterator<Item = PrivateKeySigner> + '_ {
366 self.config.signer_accounts.iter().cloned()
367 }
368
369 pub fn genesis_accounts(&self) -> impl Iterator<Item = Address> + '_ {
371 self.config.genesis_accounts.iter().map(|w| w.address())
372 }
373
374 pub fn genesis_balance(&self) -> U256 {
376 self.config.genesis_balance
377 }
378
379 pub fn gas_price(&self) -> u128 {
381 self.config.get_gas_price()
382 }
383
384 pub fn shutdown_signal(&self) -> &Option<Signal> {
386 &self._signal
387 }
388
389 pub fn shutdown_signal_mut(&mut self) -> &mut Option<Signal> {
393 &mut self._signal
394 }
395
396 pub fn task_manager(&self) -> &TaskManager {
412 &self.task_manager
413 }
414}
415
416impl Future for NodeHandle {
417 type Output = Result<NodeResult<()>, JoinError>;
418
419 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
420 let pin = self.get_mut();
421
422 if let Some(mut ipc) = pin.ipc_task.take() {
424 if let Poll::Ready(res) = ipc.poll_unpin(cx) {
425 return Poll::Ready(res.map(|()| Ok(())));
426 } else {
427 pin.ipc_task = Some(ipc);
428 }
429 }
430
431 if let Poll::Ready(res) = pin.node_service.poll_unpin(cx) {
433 return Poll::Ready(res);
434 }
435
436 for server in &mut pin.servers {
438 if let Poll::Ready(res) = server.poll_unpin(cx) {
439 return Poll::Ready(res);
440 }
441 }
442
443 Poll::Pending
444 }
445}
446
447#[doc(hidden)]
448pub fn init_tracing() -> LoggingManager {
449 use tracing_subscriber::prelude::*;
450
451 let manager = LoggingManager::default();
452 let _ = if std::env::var("RUST_LOG").is_ok() {
454 tracing_subscriber::Registry::default()
455 .with(tracing_subscriber::EnvFilter::from_default_env())
456 .with(tracing_subscriber::fmt::layer())
457 .try_init()
458 } else {
459 tracing_subscriber::Registry::default()
460 .with(NodeLogLayer::new(manager.clone()))
461 .with(
462 tracing_subscriber::fmt::layer()
463 .without_time()
464 .with_target(false)
465 .with_level(false),
466 )
467 .try_init()
468 };
469
470 manager
471}