1use reth_network_peers::TrustedPeer;
3use reth_network_types::{PeersConfig, SessionsConfig};
4use reth_prune_types::{PruneModes, MINIMUM_UNWIND_SAFE_DISTANCE};
5use reth_stages_types::ExecutionStageThresholds;
6use reth_static_file_types::{StaticFileMap, StaticFileSegment};
7use std::{
8 path::{Path, PathBuf},
9 time::Duration,
10};
11use url::Url;
12
13#[cfg(feature = "serde")]
14const EXTENSION: &str = "toml";
15
16pub const DEFAULT_BLOCK_INTERVAL: usize = 5;
18
19#[derive(Debug, Clone, Default, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "serde", serde(default))]
23pub struct Config {
24 pub bootnodes: Vec<TrustedPeer>,
28 pub stages: StageConfig,
30 #[cfg_attr(feature = "serde", serde(default))]
32 pub prune: PruneConfig,
33 pub peers: PeersConfig,
35 pub sessions: SessionsConfig,
37 #[cfg_attr(feature = "serde", serde(default))]
39 pub static_files: StaticFilesConfig,
40}
41
42impl Config {
43 pub fn set_prune_config(&mut self, prune_config: PruneConfig) {
45 self.prune = prune_config;
46 }
47}
48
49#[cfg(feature = "serde")]
50impl Config {
51 pub fn from_path(path: impl AsRef<Path>) -> eyre::Result<Self> {
56 Self::from_path_inner(path.as_ref())
57 }
58
59 fn from_path_inner(path: &Path) -> eyre::Result<Self> {
61 match std::fs::read_to_string(path) {
62 Ok(cfg_string) => {
63 toml::from_str(&cfg_string).map_err(|e| eyre::eyre!("Failed to parse TOML: {e}"))
64 }
65 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
66 if let Some(parent) = path.parent() {
67 std::fs::create_dir_all(parent)
68 .map_err(|e| eyre::eyre!("Failed to create directory: {e}"))?;
69 }
70 let cfg = Self::default();
71 let s = toml::to_string_pretty(&cfg)
72 .map_err(|e| eyre::eyre!("Failed to serialize to TOML: {e}"))?;
73 std::fs::write(path, s)
74 .map_err(|e| eyre::eyre!("Failed to write configuration file: {e}"))?;
75 Ok(cfg)
76 }
77 Err(e) => Err(eyre::eyre!("Failed to load configuration: {e}")),
78 }
79 }
80
81 pub fn peers_config_with_basic_nodes_from_file(
85 &self,
86 peers_file: Option<&Path>,
87 ) -> PeersConfig {
88 self.peers
89 .clone()
90 .with_basic_nodes_from_file(peers_file)
91 .unwrap_or_else(|_| self.peers.clone())
92 }
93
94 pub fn save(&self, path: &Path) -> Result<(), std::io::Error> {
96 if path.extension() != Some(std::ffi::OsStr::new(EXTENSION)) {
97 return Err(std::io::Error::new(
98 std::io::ErrorKind::InvalidInput,
99 format!("reth config file extension must be '{EXTENSION}'"),
100 ));
101 }
102
103 std::fs::write(
104 path,
105 toml::to_string(self)
106 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?,
107 )
108 }
109}
110
111#[derive(Debug, Clone, Default, PartialEq, Eq)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114#[cfg_attr(feature = "serde", serde(default))]
115pub struct StageConfig {
116 pub era: EraConfig,
118 pub headers: HeadersConfig,
120 pub bodies: BodiesConfig,
122 pub sender_recovery: SenderRecoveryConfig,
124 pub execution: ExecutionConfig,
126 pub prune: PruneStageConfig,
128 pub account_hashing: HashingConfig,
130 pub storage_hashing: HashingConfig,
132 pub merkle: MerkleConfig,
134 pub transaction_lookup: TransactionLookupConfig,
136 pub index_account_history: IndexHistoryConfig,
138 pub index_storage_history: IndexHistoryConfig,
140 pub etl: EtlConfig,
142}
143
144impl StageConfig {
145 pub fn execution_external_clean_threshold(&self) -> u64 {
150 self.merkle
151 .incremental_threshold
152 .max(self.account_hashing.clean_threshold)
153 .max(self.storage_hashing.clean_threshold)
154 }
155}
156
157#[derive(Debug, Clone, Default, PartialEq, Eq)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160#[cfg_attr(feature = "serde", serde(default))]
161pub struct EraConfig {
162 pub path: Option<PathBuf>,
166 pub url: Option<Url>,
170 pub folder: Option<PathBuf>,
174}
175
176impl EraConfig {
177 pub fn with_datadir(mut self, dir: impl AsRef<Path>) -> Self {
179 self.folder = Some(dir.as_ref().join("era"));
180 self
181 }
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187#[cfg_attr(feature = "serde", serde(default))]
188pub struct HeadersConfig {
189 pub downloader_max_concurrent_requests: usize,
193 pub downloader_min_concurrent_requests: usize,
197 pub downloader_max_buffered_responses: usize,
200 pub downloader_request_limit: u64,
202 pub commit_threshold: u64,
204}
205
206impl Default for HeadersConfig {
207 fn default() -> Self {
208 Self {
209 commit_threshold: 10_000,
210 downloader_request_limit: 1_000,
211 downloader_max_concurrent_requests: 100,
212 downloader_min_concurrent_requests: 5,
213 downloader_max_buffered_responses: 100,
214 }
215 }
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221#[cfg_attr(feature = "serde", serde(default))]
222pub struct BodiesConfig {
223 pub downloader_request_limit: u64,
227 pub downloader_stream_batch_size: usize,
231 pub downloader_max_buffered_blocks_size_bytes: usize,
235 pub downloader_min_concurrent_requests: usize,
239 pub downloader_max_concurrent_requests: usize,
244}
245
246impl Default for BodiesConfig {
247 fn default() -> Self {
248 Self {
249 downloader_request_limit: 200,
250 downloader_stream_batch_size: 1_000,
251 downloader_max_buffered_blocks_size_bytes: 2 * 1024 * 1024 * 1024, downloader_min_concurrent_requests: 5,
253 downloader_max_concurrent_requests: 100,
254 }
255 }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
261#[cfg_attr(feature = "serde", serde(default))]
262pub struct SenderRecoveryConfig {
263 pub commit_threshold: u64,
265}
266
267impl Default for SenderRecoveryConfig {
268 fn default() -> Self {
269 Self { commit_threshold: 5_000_000 }
270 }
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
276#[cfg_attr(feature = "serde", serde(default))]
277pub struct ExecutionConfig {
278 pub max_blocks: Option<u64>,
280 pub max_changes: Option<u64>,
282 pub max_cumulative_gas: Option<u64>,
284 #[cfg_attr(
286 feature = "serde",
287 serde(
288 serialize_with = "humantime_serde::serialize",
289 deserialize_with = "deserialize_duration"
290 )
291 )]
292 pub max_duration: Option<Duration>,
293}
294
295impl Default for ExecutionConfig {
296 fn default() -> Self {
297 Self {
298 max_blocks: Some(500_000),
299 max_changes: Some(5_000_000),
300 max_cumulative_gas: Some(30_000_000 * 50_000),
302 max_duration: Some(Duration::from_secs(10 * 60)),
304 }
305 }
306}
307
308impl From<ExecutionConfig> for ExecutionStageThresholds {
309 fn from(config: ExecutionConfig) -> Self {
310 Self {
311 max_blocks: config.max_blocks,
312 max_changes: config.max_changes,
313 max_cumulative_gas: config.max_cumulative_gas,
314 max_duration: config.max_duration,
315 }
316 }
317}
318
319#[derive(Debug, Clone, Copy, PartialEq, Eq)]
321#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
322#[cfg_attr(feature = "serde", serde(default))]
323pub struct PruneStageConfig {
324 pub commit_threshold: usize,
326}
327
328impl Default for PruneStageConfig {
329 fn default() -> Self {
330 Self { commit_threshold: 1_000_000 }
331 }
332}
333
334#[derive(Debug, Clone, Copy, PartialEq, Eq)]
336#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
337#[cfg_attr(feature = "serde", serde(default))]
338pub struct HashingConfig {
339 pub clean_threshold: u64,
342 pub commit_threshold: u64,
344 pub commit_entries: u64,
348}
349
350impl Default for HashingConfig {
351 fn default() -> Self {
352 Self { clean_threshold: 500_000, commit_threshold: 100_000, commit_entries: 30_000_000 }
353 }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359#[cfg_attr(feature = "serde", serde(default))]
360pub struct MerkleConfig {
361 pub incremental_threshold: u64,
369 pub rebuild_threshold: u64,
372}
373
374impl Default for MerkleConfig {
375 fn default() -> Self {
376 Self { incremental_threshold: 7_000, rebuild_threshold: 100_000 }
377 }
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq)]
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
383#[cfg_attr(feature = "serde", serde(default))]
384pub struct TransactionLookupConfig {
385 pub chunk_size: u64,
387}
388
389impl Default for TransactionLookupConfig {
390 fn default() -> Self {
391 Self { chunk_size: 5_000_000 }
392 }
393}
394
395#[derive(Debug, Clone, PartialEq, Eq)]
397#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
398#[cfg_attr(feature = "serde", serde(default))]
399pub struct EtlConfig {
400 pub dir: Option<PathBuf>,
402 pub file_size: usize,
404}
405
406impl Default for EtlConfig {
407 fn default() -> Self {
408 Self { dir: None, file_size: Self::default_file_size() }
409 }
410}
411
412impl EtlConfig {
413 pub const fn new(dir: Option<PathBuf>, file_size: usize) -> Self {
415 Self { dir, file_size }
416 }
417
418 pub fn from_datadir(path: &Path) -> PathBuf {
420 path.join("etl-tmp")
421 }
422
423 pub const fn default_file_size() -> usize {
425 500 * (1024 * 1024)
427 }
428}
429
430#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
432#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
433#[cfg_attr(feature = "serde", serde(default))]
434pub struct StaticFilesConfig {
435 pub blocks_per_file: BlocksPerFileConfig,
437}
438
439#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
441#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
442#[cfg_attr(feature = "serde", serde(default))]
443pub struct BlocksPerFileConfig {
444 pub headers: Option<u64>,
446 pub transactions: Option<u64>,
448 pub receipts: Option<u64>,
450 pub transaction_senders: Option<u64>,
452 pub account_change_sets: Option<u64>,
454 pub storage_change_sets: Option<u64>,
456}
457
458impl StaticFilesConfig {
459 pub fn validate(&self) -> eyre::Result<()> {
463 let BlocksPerFileConfig {
464 headers,
465 transactions,
466 receipts,
467 transaction_senders,
468 account_change_sets,
469 storage_change_sets,
470 } = self.blocks_per_file;
471 eyre::ensure!(headers != Some(0), "Headers segment blocks per file must be greater than 0");
472 eyre::ensure!(
473 transactions != Some(0),
474 "Transactions segment blocks per file must be greater than 0"
475 );
476 eyre::ensure!(
477 receipts != Some(0),
478 "Receipts segment blocks per file must be greater than 0"
479 );
480 eyre::ensure!(
481 transaction_senders != Some(0),
482 "Transaction senders segment blocks per file must be greater than 0"
483 );
484 eyre::ensure!(
485 account_change_sets != Some(0),
486 "Account changesets segment blocks per file must be greater than 0"
487 );
488 eyre::ensure!(
489 storage_change_sets != Some(0),
490 "Storage changesets segment blocks per file must be greater than 0"
491 );
492 Ok(())
493 }
494
495 pub fn as_blocks_per_file_map(&self) -> StaticFileMap<u64> {
497 let BlocksPerFileConfig {
498 headers,
499 transactions,
500 receipts,
501 transaction_senders,
502 account_change_sets,
503 storage_change_sets,
504 } = self.blocks_per_file;
505
506 let mut map = StaticFileMap::default();
507 for segment in StaticFileSegment::iter() {
510 let blocks_per_file = match segment {
511 StaticFileSegment::Headers => headers,
512 StaticFileSegment::Transactions => transactions,
513 StaticFileSegment::Receipts => receipts,
514 StaticFileSegment::TransactionSenders => transaction_senders,
515 StaticFileSegment::AccountChangeSets => account_change_sets,
516 StaticFileSegment::StorageChangeSets => storage_change_sets,
517 };
518
519 if let Some(blocks_per_file) = blocks_per_file {
520 map.insert(segment, blocks_per_file);
521 }
522 }
523 map
524 }
525}
526
527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
529#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
530#[cfg_attr(feature = "serde", serde(default))]
531pub struct IndexHistoryConfig {
532 pub commit_threshold: u64,
534}
535
536impl Default for IndexHistoryConfig {
537 fn default() -> Self {
538 Self { commit_threshold: 100_000 }
539 }
540}
541
542#[derive(Debug, Clone, PartialEq, Eq)]
544#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
545#[cfg_attr(feature = "serde", serde(default))]
546pub struct PruneConfig {
547 pub block_interval: usize,
549 #[cfg_attr(feature = "serde", serde(alias = "parts"))]
551 pub segments: PruneModes,
552 #[cfg_attr(feature = "serde", serde(default = "default_minimum_pruning_distance"))]
555 pub minimum_pruning_distance: u64,
556}
557
558const fn default_minimum_pruning_distance() -> u64 {
560 MINIMUM_UNWIND_SAFE_DISTANCE
561}
562
563impl Default for PruneConfig {
564 fn default() -> Self {
565 Self {
566 block_interval: DEFAULT_BLOCK_INTERVAL,
567 segments: PruneModes::default(),
568 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
569 }
570 }
571}
572
573impl PruneConfig {
574 pub fn is_default(&self) -> bool {
576 self == &Self::default()
577 }
578
579 pub fn has_receipts_pruning(&self) -> bool {
581 self.segments.has_receipts_pruning()
582 }
583
584 pub fn merge(&mut self, other: Self) {
590 let Self {
591 block_interval,
592 segments:
593 PruneModes {
594 sender_recovery,
595 transaction_lookup,
596 receipts,
597 account_history,
598 storage_history,
599 bodies_history,
600 receipts_log_filter,
601 },
602 minimum_pruning_distance,
603 } = other;
604
605 if self.block_interval == DEFAULT_BLOCK_INTERVAL {
607 self.block_interval = block_interval;
608 }
609
610 if self.minimum_pruning_distance == MINIMUM_UNWIND_SAFE_DISTANCE {
612 self.minimum_pruning_distance = minimum_pruning_distance;
613 }
614
615 self.segments.sender_recovery = self.segments.sender_recovery.or(sender_recovery);
617 self.segments.transaction_lookup = self.segments.transaction_lookup.or(transaction_lookup);
618 self.segments.receipts = self.segments.receipts.or(receipts);
619 self.segments.account_history = self.segments.account_history.or(account_history);
620 self.segments.storage_history = self.segments.storage_history.or(storage_history);
621 self.segments.bodies_history = self.segments.bodies_history.or(bodies_history);
622
623 if self.segments.receipts_log_filter.0.is_empty() && !receipts_log_filter.0.is_empty() {
624 self.segments.receipts_log_filter = receipts_log_filter;
625 }
626 }
627}
628
629#[cfg(feature = "serde")]
631fn deserialize_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
632where
633 D: serde::de::Deserializer<'de>,
634{
635 #[derive(serde::Deserialize)]
636 #[serde(untagged)]
637 enum AnyDuration {
638 #[serde(deserialize_with = "humantime_serde::deserialize")]
639 Human(Option<Duration>),
640 Duration(Option<Duration>),
641 }
642
643 <AnyDuration as serde::Deserialize>::deserialize(deserializer).map(|d| match d {
644 AnyDuration::Human(duration) | AnyDuration::Duration(duration) => duration,
645 })
646}
647
648#[cfg(all(test, feature = "serde"))]
649mod tests {
650 use super::{Config, EXTENSION};
651 use crate::PruneConfig;
652 use alloy_primitives::Address;
653 use reth_network_peers::TrustedPeer;
654 use reth_prune_types::{
655 PruneMode, PruneModes, ReceiptsLogPruneConfig, MINIMUM_UNWIND_SAFE_DISTANCE,
656 };
657 use std::{collections::BTreeMap, path::Path, str::FromStr, time::Duration};
658
659 fn with_tempdir(filename: &str, proc: fn(&std::path::Path)) {
660 let temp_dir = tempfile::tempdir().unwrap();
661 let config_path = temp_dir.path().join(filename).with_extension(EXTENSION);
662
663 proc(&config_path);
664
665 temp_dir.close().unwrap()
666 }
667
668 fn with_config_path(test_fn: fn(&Path)) {
670 let config_dir = tempfile::tempdir().expect("creating test fixture failed");
672 let config_path =
674 config_dir.path().join("example-app").join("example-config").with_extension("toml");
675 test_fn(&config_path);
677 config_dir.close().expect("removing test fixture failed");
678 }
679
680 #[test]
681 fn test_load_path_works() {
682 with_config_path(|path| {
683 let config = Config::from_path(path).expect("load_path failed");
684 assert_eq!(config, Config::default());
685 })
686 }
687
688 #[test]
689 fn test_load_path_reads_existing_config() {
690 with_config_path(|path| {
691 let config = Config::default();
692
693 if let Some(parent) = path.parent() {
695 std::fs::create_dir_all(parent).expect("Failed to create directories");
696 }
697
698 std::fs::write(path, toml::to_string(&config).unwrap())
700 .expect("Failed to write config");
701
702 let loaded = Config::from_path(path).expect("load_path failed");
704 assert_eq!(config, loaded);
705 })
706 }
707
708 #[test]
709 fn test_load_path_fails_on_invalid_toml() {
710 with_config_path(|path| {
711 let invalid_toml = "invalid toml data";
712
713 if let Some(parent) = path.parent() {
715 std::fs::create_dir_all(parent).expect("Failed to create directories");
716 }
717
718 std::fs::write(path, invalid_toml).expect("Failed to write invalid TOML");
720
721 let result = Config::from_path(path);
723 assert!(result.is_err());
724 })
725 }
726
727 #[test]
728 fn test_load_path_creates_directory_if_not_exists() {
729 with_config_path(|path| {
730 let parent = path.parent().unwrap();
732 assert!(!parent.exists());
733
734 let config = Config::from_path(path).expect("load_path failed");
736 assert_eq!(config, Config::default());
737
738 assert!(parent.exists());
740 assert!(path.exists());
741 });
742 }
743
744 #[test]
745 fn test_store_config() {
746 with_tempdir("config-store-test", |config_path| {
747 let config = Config::default();
748 std::fs::write(
749 config_path,
750 toml::to_string(&config).expect("Failed to serialize config"),
751 )
752 .expect("Failed to write config file");
753 })
754 }
755
756 #[test]
757 fn test_store_config_method() {
758 with_tempdir("config-store-test-method", |config_path| {
759 let config = Config::default();
760 config.save(config_path).expect("Failed to store config");
761 })
762 }
763
764 #[test]
765 fn test_load_config() {
766 with_tempdir("config-load-test", |config_path| {
767 let config = Config::default();
768
769 std::fs::write(
771 config_path,
772 toml::to_string(&config).expect("Failed to serialize config"),
773 )
774 .expect("Failed to write config file");
775
776 let loaded_config = Config::from_path(config_path).unwrap();
778
779 assert_eq!(config, loaded_config);
781 })
782 }
783
784 #[test]
785 fn test_load_execution_stage() {
786 with_tempdir("config-load-test", |config_path| {
787 let mut config = Config::default();
788 config.stages.execution.max_duration = Some(Duration::from_secs(10 * 60));
789
790 std::fs::write(
792 config_path,
793 toml::to_string(&config).expect("Failed to serialize config"),
794 )
795 .expect("Failed to write config file");
796
797 let loaded_config = Config::from_path(config_path).unwrap();
799
800 assert_eq!(config, loaded_config);
802 })
803 }
804
805 #[test]
807 fn test_backwards_compatibility() {
808 let alpha_0_0_8 = r"#
809[stages.headers]
810downloader_max_concurrent_requests = 100
811downloader_min_concurrent_requests = 5
812downloader_max_buffered_responses = 100
813downloader_request_limit = 1000
814commit_threshold = 10000
815
816[stages.bodies]
817downloader_request_limit = 200
818downloader_stream_batch_size = 1000
819downloader_max_buffered_blocks_size_bytes = 2147483648
820downloader_min_concurrent_requests = 5
821downloader_max_concurrent_requests = 100
822
823[stages.sender_recovery]
824commit_threshold = 5000000
825
826[stages.execution]
827max_blocks = 500000
828max_changes = 5000000
829
830[stages.account_hashing]
831clean_threshold = 500000
832commit_threshold = 100000
833
834[stages.storage_hashing]
835clean_threshold = 500000
836commit_threshold = 100000
837
838[stages.merkle]
839clean_threshold = 50000
840
841[stages.transaction_lookup]
842chunk_size = 5000000
843
844[stages.index_account_history]
845commit_threshold = 100000
846
847[stages.index_storage_history]
848commit_threshold = 100000
849
850[peers]
851refill_slots_interval = '1s'
852trusted_nodes = []
853connect_trusted_nodes_only = false
854max_backoff_count = 5
855ban_duration = '12h'
856
857[peers.connection_info]
858max_outbound = 100
859max_inbound = 30
860
861[peers.reputation_weights]
862bad_message = -16384
863bad_block = -16384
864bad_transactions = -16384
865already_seen_transactions = 0
866timeout = -4096
867bad_protocol = -2147483648
868failed_to_connect = -25600
869dropped = -4096
870
871[peers.backoff_durations]
872low = '30s'
873medium = '3m'
874high = '15m'
875max = '1h'
876
877[sessions]
878session_command_buffer = 32
879session_event_buffer = 260
880
881[sessions.limits]
882
883[sessions.initial_internal_request_timeout]
884secs = 20
885nanos = 0
886
887[sessions.protocol_breach_request_timeout]
888secs = 120
889nanos = 0
890
891[prune]
892block_interval = 5
893
894[prune.parts]
895sender_recovery = { distance = 16384 }
896transaction_lookup = 'full'
897receipts = { before = 1920000 }
898account_history = { distance = 16384 }
899storage_history = { distance = 16384 }
900[prune.parts.receipts_log_filter]
901'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' = { before = 17000000 }
902'0xdac17f958d2ee523a2206206994597c13d831ec7' = { distance = 1000 }
903#";
904 let _conf: Config = toml::from_str(alpha_0_0_8).unwrap();
905
906 let alpha_0_0_11 = r"#
907[prune.segments]
908sender_recovery = { distance = 16384 }
909transaction_lookup = 'full'
910receipts = { before = 1920000 }
911account_history = { distance = 16384 }
912storage_history = { distance = 16384 }
913[prune.segments.receipts_log_filter]
914'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' = { before = 17000000 }
915'0xdac17f958d2ee523a2206206994597c13d831ec7' = { distance = 1000 }
916#";
917 let _conf: Config = toml::from_str(alpha_0_0_11).unwrap();
918
919 let alpha_0_0_18 = r"#
920[stages.headers]
921downloader_max_concurrent_requests = 100
922downloader_min_concurrent_requests = 5
923downloader_max_buffered_responses = 100
924downloader_request_limit = 1000
925commit_threshold = 10000
926
927[stages.total_difficulty]
928commit_threshold = 100000
929
930[stages.bodies]
931downloader_request_limit = 200
932downloader_stream_batch_size = 1000
933downloader_max_buffered_blocks_size_bytes = 2147483648
934downloader_min_concurrent_requests = 5
935downloader_max_concurrent_requests = 100
936
937[stages.sender_recovery]
938commit_threshold = 5000000
939
940[stages.execution]
941max_blocks = 500000
942max_changes = 5000000
943max_cumulative_gas = 1500000000000
944[stages.execution.max_duration]
945secs = 600
946nanos = 0
947
948[stages.account_hashing]
949clean_threshold = 500000
950commit_threshold = 100000
951
952[stages.storage_hashing]
953clean_threshold = 500000
954commit_threshold = 100000
955
956[stages.merkle]
957clean_threshold = 50000
958
959[stages.transaction_lookup]
960commit_threshold = 5000000
961
962[stages.index_account_history]
963commit_threshold = 100000
964
965[stages.index_storage_history]
966commit_threshold = 100000
967
968[peers]
969refill_slots_interval = '5s'
970trusted_nodes = []
971connect_trusted_nodes_only = false
972max_backoff_count = 5
973ban_duration = '12h'
974
975[peers.connection_info]
976max_outbound = 100
977max_inbound = 30
978max_concurrent_outbound_dials = 10
979
980[peers.reputation_weights]
981bad_message = -16384
982bad_block = -16384
983bad_transactions = -16384
984already_seen_transactions = 0
985timeout = -4096
986bad_protocol = -2147483648
987failed_to_connect = -25600
988dropped = -4096
989bad_announcement = -1024
990
991[peers.backoff_durations]
992low = '30s'
993medium = '3m'
994high = '15m'
995max = '1h'
996
997[sessions]
998session_command_buffer = 32
999session_event_buffer = 260
1000
1001[sessions.limits]
1002
1003[sessions.initial_internal_request_timeout]
1004secs = 20
1005nanos = 0
1006
1007[sessions.protocol_breach_request_timeout]
1008secs = 120
1009nanos = 0
1010#";
1011 let conf: Config = toml::from_str(alpha_0_0_18).unwrap();
1012 assert_eq!(conf.stages.execution.max_duration, Some(Duration::from_secs(10 * 60)));
1013
1014 let alpha_0_0_19 = r"#
1015[stages.headers]
1016downloader_max_concurrent_requests = 100
1017downloader_min_concurrent_requests = 5
1018downloader_max_buffered_responses = 100
1019downloader_request_limit = 1000
1020commit_threshold = 10000
1021
1022[stages.total_difficulty]
1023commit_threshold = 100000
1024
1025[stages.bodies]
1026downloader_request_limit = 200
1027downloader_stream_batch_size = 1000
1028downloader_max_buffered_blocks_size_bytes = 2147483648
1029downloader_min_concurrent_requests = 5
1030downloader_max_concurrent_requests = 100
1031
1032[stages.sender_recovery]
1033commit_threshold = 5000000
1034
1035[stages.execution]
1036max_blocks = 500000
1037max_changes = 5000000
1038max_cumulative_gas = 1500000000000
1039max_duration = '10m'
1040
1041[stages.account_hashing]
1042clean_threshold = 500000
1043commit_threshold = 100000
1044
1045[stages.storage_hashing]
1046clean_threshold = 500000
1047commit_threshold = 100000
1048
1049[stages.merkle]
1050clean_threshold = 50000
1051
1052[stages.transaction_lookup]
1053commit_threshold = 5000000
1054
1055[stages.index_account_history]
1056commit_threshold = 100000
1057
1058[stages.index_storage_history]
1059commit_threshold = 100000
1060
1061[peers]
1062refill_slots_interval = '5s'
1063trusted_nodes = []
1064connect_trusted_nodes_only = false
1065max_backoff_count = 5
1066ban_duration = '12h'
1067
1068[peers.connection_info]
1069max_outbound = 100
1070max_inbound = 30
1071max_concurrent_outbound_dials = 10
1072
1073[peers.reputation_weights]
1074bad_message = -16384
1075bad_block = -16384
1076bad_transactions = -16384
1077already_seen_transactions = 0
1078timeout = -4096
1079bad_protocol = -2147483648
1080failed_to_connect = -25600
1081dropped = -4096
1082bad_announcement = -1024
1083
1084[peers.backoff_durations]
1085low = '30s'
1086medium = '3m'
1087high = '15m'
1088max = '1h'
1089
1090[sessions]
1091session_command_buffer = 32
1092session_event_buffer = 260
1093
1094[sessions.limits]
1095
1096[sessions.initial_internal_request_timeout]
1097secs = 20
1098nanos = 0
1099
1100[sessions.protocol_breach_request_timeout]
1101secs = 120
1102nanos = 0
1103#";
1104 let _conf: Config = toml::from_str(alpha_0_0_19).unwrap();
1105 }
1106
1107 #[test]
1109 fn test_backwards_compatibility_prune_full() {
1110 let s = r"#
1111[prune]
1112block_interval = 5
1113
1114[prune.segments]
1115sender_recovery = { distance = 16384 }
1116transaction_lookup = 'full'
1117receipts = { distance = 16384 }
1118#";
1119 let _conf: Config = toml::from_str(s).unwrap();
1120 }
1121
1122 #[test]
1123 fn test_prune_config_merge() {
1124 let mut config1 = PruneConfig {
1125 block_interval: 5,
1126 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
1127 segments: PruneModes {
1128 sender_recovery: Some(PruneMode::Full),
1129 transaction_lookup: None,
1130 receipts: Some(PruneMode::Distance(1000)),
1131 account_history: None,
1132 storage_history: Some(PruneMode::Before(5000)),
1133 bodies_history: None,
1134 receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([(
1135 Address::random(),
1136 PruneMode::Full,
1137 )])),
1138 },
1139 };
1140
1141 let config2 = PruneConfig {
1142 block_interval: 10,
1143 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
1144 segments: PruneModes {
1145 sender_recovery: Some(PruneMode::Distance(500)),
1146 transaction_lookup: Some(PruneMode::Full),
1147 receipts: Some(PruneMode::Full),
1148 account_history: Some(PruneMode::Distance(2000)),
1149 storage_history: Some(PruneMode::Distance(3000)),
1150 bodies_history: None,
1151 receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([
1152 (Address::random(), PruneMode::Distance(1000)),
1153 (Address::random(), PruneMode::Before(2000)),
1154 ])),
1155 },
1156 };
1157
1158 let original_filter = config1.segments.receipts_log_filter.clone();
1159 config1.merge(config2);
1160
1161 assert_eq!(config1.block_interval, 10);
1164 assert_eq!(config1.segments.sender_recovery, Some(PruneMode::Full));
1165 assert_eq!(config1.segments.transaction_lookup, Some(PruneMode::Full));
1166 assert_eq!(config1.segments.receipts, Some(PruneMode::Distance(1000)));
1167 assert_eq!(config1.segments.account_history, Some(PruneMode::Distance(2000)));
1168 assert_eq!(config1.segments.storage_history, Some(PruneMode::Before(5000)));
1169 assert_eq!(config1.segments.receipts_log_filter, original_filter);
1170 }
1171
1172 #[test]
1173 fn test_conf_trust_nodes_only() {
1174 let trusted_nodes_only = r"#
1175[peers]
1176trusted_nodes_only = true
1177#";
1178 let conf: Config = toml::from_str(trusted_nodes_only).unwrap();
1179 assert!(conf.peers.trusted_nodes_only);
1180
1181 let trusted_nodes_only = r"#
1182[peers]
1183connect_trusted_nodes_only = true
1184#";
1185 let conf: Config = toml::from_str(trusted_nodes_only).unwrap();
1186 assert!(conf.peers.trusted_nodes_only);
1187 }
1188
1189 #[test]
1190 fn test_can_support_dns_in_trusted_nodes() {
1191 let reth_toml = r#"
1192 [peers]
1193 trusted_nodes = [
1194 "enode://0401e494dbd0c84c5c0f72adac5985d2f2525e08b68d448958aae218f5ac8198a80d1498e0ebec2ce38b1b18d6750f6e61a56b4614c5a6c6cf0981c39aed47dc@34.159.32.127:30303",
1195 "enode://e9675164b5e17b9d9edf0cc2bd79e6b6f487200c74d1331c220abb5b8ee80c2eefbf18213989585e9d0960683e819542e11d4eefb5f2b4019e1e49f9fd8fff18@berav2-bootnode.staketab.org:30303"
1196 ]
1197 "#;
1198
1199 let conf: Config = toml::from_str(reth_toml).unwrap();
1200 assert_eq!(conf.peers.trusted_nodes.len(), 2);
1201
1202 let expected_enodes = vec![
1203 "enode://0401e494dbd0c84c5c0f72adac5985d2f2525e08b68d448958aae218f5ac8198a80d1498e0ebec2ce38b1b18d6750f6e61a56b4614c5a6c6cf0981c39aed47dc@34.159.32.127:30303",
1204 "enode://e9675164b5e17b9d9edf0cc2bd79e6b6f487200c74d1331c220abb5b8ee80c2eefbf18213989585e9d0960683e819542e11d4eefb5f2b4019e1e49f9fd8fff18@berav2-bootnode.staketab.org:30303",
1205 ];
1206
1207 for enode in expected_enodes {
1208 let node = TrustedPeer::from_str(enode).unwrap();
1209 assert!(conf.peers.trusted_nodes.contains(&node));
1210 }
1211 }
1212
1213 #[test]
1214 fn test_bootnodes() {
1215 let reth_toml = r#"
1216 bootnodes = [
1217 "enode://0401e494dbd0c84c5c0f72adac5985d2f2525e08b68d448958aae218f5ac8198a80d1498e0ebec2ce38b1b18d6750f6e61a56b4614c5a6c6cf0981c39aed47dc@34.159.32.127:30303",
1218 "enode://e9675164b5e17b9d9edf0cc2bd79e6b6f487200c74d1331c220abb5b8ee80c2eefbf18213989585e9d0960683e819542e11d4eefb5f2b4019e1e49f9fd8fff18@berav2-bootnode.staketab.org:30303",
1219 "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8"
1220 ]
1221 "#;
1222
1223 let conf: Config = toml::from_str(reth_toml).unwrap();
1224 assert_eq!(conf.bootnodes.len(), 3);
1225 assert_eq!(
1226 conf.bootnodes[1].host,
1227 url::Host::<String>::Domain("berav2-bootnode.staketab.org".to_string())
1228 );
1229 assert_eq!(
1231 conf.bootnodes[2],
1232 TrustedPeer::from_str("enode://ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f@127.0.0.1:30303").unwrap()
1233 );
1234
1235 let serialized = toml::to_string(&conf).unwrap();
1237 assert_eq!(toml::from_str::<Config>(&serialized).unwrap(), conf);
1238 }
1239}