1use crate::{
4 args::{
5 DatabaseArgs, DatadirArgs, DebugArgs, DevArgs, EngineArgs, NetworkArgs, PayloadBuilderArgs,
6 PruningArgs, RpcServerArgs, TxPoolArgs,
7 },
8 dirs::{ChainPath, DataDirPath},
9 utils::get_single_header,
10};
11use alloy_consensus::BlockHeader;
12use alloy_eips::BlockHashOrNumber;
13use alloy_primitives::{BlockNumber, B256};
14use eyre::eyre;
15use reth_chainspec::{ChainSpec, EthChainSpec, MAINNET};
16use reth_config::config::PruneConfig;
17use reth_engine_local::MiningMode;
18use reth_ethereum_forks::{EthereumHardforks, Head};
19use reth_network_p2p::headers::client::HeadersClient;
20use reth_primitives_traits::SealedHeader;
21use reth_stages_types::StageId;
22use reth_storage_api::{
23 BlockHashReader, DatabaseProviderFactory, HeaderProvider, StageCheckpointReader,
24};
25use reth_storage_errors::provider::ProviderResult;
26use reth_transaction_pool::TransactionPool;
27use serde::{de::DeserializeOwned, Serialize};
28use std::{
29 fs,
30 net::SocketAddr,
31 path::{Path, PathBuf},
32 sync::Arc,
33};
34use tracing::*;
35
36use crate::args::EraArgs;
37pub use reth_engine_primitives::{
38 DEFAULT_MAX_PROOF_TASK_CONCURRENCY, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
39 DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
40};
41
42pub const DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB: u64 = 4 * 1024;
44
45#[derive(Debug)]
91pub struct NodeConfig<ChainSpec> {
92 pub datadir: DatadirArgs,
94
95 pub config: Option<PathBuf>,
97
98 pub chain: Arc<ChainSpec>,
102
103 pub metrics: Option<SocketAddr>,
107
108 pub instance: Option<u16>,
124
125 pub network: NetworkArgs,
127
128 pub rpc: RpcServerArgs,
130
131 pub txpool: TxPoolArgs,
133
134 pub builder: PayloadBuilderArgs,
136
137 pub debug: DebugArgs,
139
140 pub db: DatabaseArgs,
142
143 pub dev: DevArgs,
145
146 pub pruning: PruningArgs,
148
149 pub engine: EngineArgs,
151
152 pub era: EraArgs,
154}
155
156impl NodeConfig<ChainSpec> {
157 pub fn test() -> Self {
159 Self::default()
160 .with_unused_ports()
162 }
163}
164
165impl<ChainSpec> NodeConfig<ChainSpec> {
166 pub fn new(chain: Arc<ChainSpec>) -> Self {
168 Self {
169 config: None,
170 chain,
171 metrics: None,
172 instance: None,
173 network: NetworkArgs::default(),
174 rpc: RpcServerArgs::default(),
175 txpool: TxPoolArgs::default(),
176 builder: PayloadBuilderArgs::default(),
177 debug: DebugArgs::default(),
178 db: DatabaseArgs::default(),
179 dev: DevArgs::default(),
180 pruning: PruningArgs::default(),
181 datadir: DatadirArgs::default(),
182 engine: EngineArgs::default(),
183 era: EraArgs::default(),
184 }
185 }
186
187 pub const fn dev(mut self) -> Self {
192 self.dev.dev = true;
193 self.network.discovery.disable_discovery = true;
194 self
195 }
196
197 pub const fn set_dev(self, dev: bool) -> Self {
199 if dev {
200 self.dev()
201 } else {
202 self
203 }
204 }
205
206 pub fn with_datadir_args(mut self, datadir_args: DatadirArgs) -> Self {
208 self.datadir = datadir_args;
209 self
210 }
211
212 pub fn with_config(mut self, config: impl Into<PathBuf>) -> Self {
214 self.config = Some(config.into());
215 self
216 }
217
218 pub fn with_chain(mut self, chain: impl Into<Arc<ChainSpec>>) -> Self {
220 self.chain = chain.into();
221 self
222 }
223
224 pub const fn with_metrics(mut self, metrics: SocketAddr) -> Self {
226 self.metrics = Some(metrics);
227 self
228 }
229
230 pub const fn with_instance(mut self, instance: u16) -> Self {
232 self.instance = Some(instance);
233 self
234 }
235
236 pub fn get_instance(&self) -> u16 {
238 self.instance.unwrap_or(1)
239 }
240
241 pub fn with_network(mut self, network: NetworkArgs) -> Self {
243 self.network = network;
244 self
245 }
246
247 pub fn with_rpc(mut self, rpc: RpcServerArgs) -> Self {
249 self.rpc = rpc;
250 self
251 }
252
253 pub fn with_txpool(mut self, txpool: TxPoolArgs) -> Self {
255 self.txpool = txpool;
256 self
257 }
258
259 pub fn with_payload_builder(mut self, builder: PayloadBuilderArgs) -> Self {
261 self.builder = builder;
262 self
263 }
264
265 pub fn with_debug(mut self, debug: DebugArgs) -> Self {
267 self.debug = debug;
268 self
269 }
270
271 pub const fn with_db(mut self, db: DatabaseArgs) -> Self {
273 self.db = db;
274 self
275 }
276
277 pub const fn with_dev(mut self, dev: DevArgs) -> Self {
279 self.dev = dev;
280 self
281 }
282
283 pub fn with_pruning(mut self, pruning: PruningArgs) -> Self {
285 self.pruning = pruning;
286 self
287 }
288
289 pub fn prune_config(&self) -> Option<PruneConfig>
291 where
292 ChainSpec: EthereumHardforks,
293 {
294 self.pruning.prune_config(&self.chain)
295 }
296
297 pub async fn max_block<Provider, Client>(
300 &self,
301 network_client: Client,
302 provider: Provider,
303 ) -> eyre::Result<Option<BlockNumber>>
304 where
305 Provider: HeaderProvider,
306 Client: HeadersClient<Header: reth_primitives_traits::BlockHeader>,
307 {
308 let max_block = if let Some(block) = self.debug.max_block {
309 Some(block)
310 } else if let Some(tip) = self.debug.tip {
311 Some(self.lookup_or_fetch_tip(provider, network_client, tip).await?)
312 } else {
313 None
314 };
315
316 Ok(max_block)
317 }
318
319 pub fn lookup_head<Factory>(&self, factory: &Factory) -> ProviderResult<Head>
323 where
324 Factory: DatabaseProviderFactory<
325 Provider: HeaderProvider + StageCheckpointReader + BlockHashReader,
326 >,
327 {
328 let provider = factory.database_provider_ro()?;
329
330 let head = provider.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default().block_number;
331
332 let header = provider
333 .header_by_number(head)?
334 .expect("the header for the latest block is missing, database is corrupt");
335
336 let total_difficulty = provider
337 .header_td_by_number(head)?
338 .unwrap_or_default();
341
342 let hash = provider
343 .block_hash(head)?
344 .expect("the hash for the latest block is missing, database is corrupt");
345
346 Ok(Head {
347 number: head,
348 hash,
349 difficulty: header.difficulty(),
350 total_difficulty,
351 timestamp: header.timestamp(),
352 })
353 }
354
355 pub async fn lookup_or_fetch_tip<Provider, Client>(
360 &self,
361 provider: Provider,
362 client: Client,
363 tip: B256,
364 ) -> ProviderResult<u64>
365 where
366 Provider: HeaderProvider,
367 Client: HeadersClient<Header: reth_primitives_traits::BlockHeader>,
368 {
369 let header = provider.header_by_hash_or_number(tip.into())?;
370
371 if let Some(header) = header {
373 info!(target: "reth::cli", ?tip, "Successfully looked up tip block in the database");
374 return Ok(header.number())
375 }
376
377 Ok(self.fetch_tip_from_network(client, tip.into()).await.number())
378 }
379
380 pub async fn fetch_tip_from_network<Client>(
384 &self,
385 client: Client,
386 tip: BlockHashOrNumber,
387 ) -> SealedHeader<Client::Header>
388 where
389 Client: HeadersClient<Header: reth_primitives_traits::BlockHeader>,
390 {
391 info!(target: "reth::cli", ?tip, "Fetching tip block from the network.");
392 let mut fetch_failures = 0;
393 loop {
394 match get_single_header(&client, tip).await {
395 Ok(tip_header) => {
396 info!(target: "reth::cli", ?tip, "Successfully fetched tip");
397 return tip_header
398 }
399 Err(error) => {
400 fetch_failures += 1;
401 if fetch_failures % 20 == 0 {
402 error!(target: "reth::cli", ?fetch_failures, %error, "Failed to fetch the tip. Retrying...");
403 }
404 }
405 }
406 }
407 }
408
409 pub fn adjust_instance_ports(&mut self) {
412 self.network.adjust_instance_ports(self.instance);
413 self.rpc.adjust_instance_ports(self.instance);
414 }
415
416 pub fn with_unused_ports(mut self) -> Self {
419 self.rpc = self.rpc.with_unused_ports();
420 self.network = self.network.with_unused_ports();
421 self
422 }
423
424 pub const fn with_disabled_rpc_cache(mut self) -> Self {
429 self.rpc.rpc_state_cache.set_zero_lengths();
430 self
431 }
432
433 pub fn datadir(&self) -> ChainPath<DataDirPath>
435 where
436 ChainSpec: EthChainSpec,
437 {
438 self.datadir.clone().resolve_datadir(self.chain.chain())
439 }
440
441 pub fn load_path<T: Serialize + DeserializeOwned + Default>(
446 path: impl AsRef<Path>,
447 ) -> eyre::Result<T> {
448 let path = path.as_ref();
449 match fs::read_to_string(path) {
450 Ok(cfg_string) => {
451 toml::from_str(&cfg_string).map_err(|e| eyre!("Failed to parse TOML: {e}"))
452 }
453 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
454 if let Some(parent) = path.parent() {
455 fs::create_dir_all(parent)
456 .map_err(|e| eyre!("Failed to create directory: {e}"))?;
457 }
458 let cfg = T::default();
459 let s = toml::to_string_pretty(&cfg)
460 .map_err(|e| eyre!("Failed to serialize to TOML: {e}"))?;
461 fs::write(path, s).map_err(|e| eyre!("Failed to write configuration file: {e}"))?;
462 Ok(cfg)
463 }
464 Err(e) => Err(eyre!("Failed to load configuration: {e}")),
465 }
466 }
467
468 pub fn map_chainspec<F, C>(self, f: F) -> NodeConfig<C>
470 where
471 F: FnOnce(Arc<ChainSpec>) -> C,
472 {
473 let chain = Arc::new(f(self.chain));
474 NodeConfig {
475 chain,
476 datadir: self.datadir,
477 config: self.config,
478 metrics: self.metrics,
479 instance: self.instance,
480 network: self.network,
481 rpc: self.rpc,
482 txpool: self.txpool,
483 builder: self.builder,
484 debug: self.debug,
485 db: self.db,
486 dev: self.dev,
487 pruning: self.pruning,
488 engine: self.engine,
489 era: self.era,
490 }
491 }
492
493 pub fn dev_mining_mode<Pool>(&self, pool: Pool) -> MiningMode<Pool>
495 where
496 Pool: TransactionPool + Unpin,
497 {
498 if let Some(interval) = self.dev.block_time {
499 MiningMode::interval(interval)
500 } else {
501 MiningMode::instant(pool, self.dev.block_max_transactions)
502 }
503 }
504}
505
506impl Default for NodeConfig<ChainSpec> {
507 fn default() -> Self {
508 Self::new(MAINNET.clone())
509 }
510}
511
512impl<ChainSpec> Clone for NodeConfig<ChainSpec> {
513 fn clone(&self) -> Self {
514 Self {
515 chain: self.chain.clone(),
516 config: self.config.clone(),
517 metrics: self.metrics,
518 instance: self.instance,
519 network: self.network.clone(),
520 rpc: self.rpc.clone(),
521 txpool: self.txpool.clone(),
522 builder: self.builder.clone(),
523 debug: self.debug.clone(),
524 db: self.db,
525 dev: self.dev,
526 pruning: self.pruning.clone(),
527 datadir: self.datadir.clone(),
528 engine: self.engine.clone(),
529 era: self.era.clone(),
530 }
531 }
532}