1use clap::{
4 builder::{RangedU64ValueParser, Resettable},
5 Args,
6};
7use eyre::ensure;
8use reth_cli_util::{parse_duration_from_secs_or_ms, parsers::format_duration_as_secs_or_ms};
9use reth_engine_primitives::{
10 TreeConfig, DEFAULT_BACKFILL_RUN_THRESHOLD, DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
11 DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE, DEFAULT_NUM_STATE_MASKING_BLOCKS,
12 MIN_PERSISTENCE_BACKPRESSURE_THRESHOLD,
13};
14use std::{num::NonZeroUsize, sync::OnceLock, time::Duration};
15
16use crate::node_config::{
17 DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
18 DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
19};
20
21static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
23
24#[derive(Debug, Clone)]
28pub struct DefaultEngineValues {
29 persistence_threshold: u64,
30 persistence_backpressure_threshold: u64,
31 num_state_masking_blocks: u64,
32 memory_block_buffer_target: u64,
33 backfill_run_threshold: u64,
34 invalid_header_hit_eviction_threshold: u8,
35 state_cache_disabled: bool,
36 prewarming_disabled: bool,
37 txpool_prewarming_enabled: bool,
38 sender_recovery_cache_enabled: bool,
39 state_provider_metrics: bool,
40 cross_block_cache_size: usize,
41 state_root_task_compare_updates: bool,
42 accept_execution_requests_hash: bool,
43 multiproof_chunk_size: usize,
44 reserved_cpu_cores: usize,
45 precompile_cache_disabled: bool,
46 state_root_fallback: bool,
47 always_process_payload_attributes_on_canonical_head: bool,
48 allow_unwind_canonical_header: bool,
49 storage_worker_count: Option<usize>,
50 account_worker_count: Option<usize>,
51 prewarming_threads: Option<usize>,
52 cache_metrics_disabled: bool,
53 slow_block_threshold: Option<Duration>,
54 disable_sparse_trie_cache_pruning: bool,
55 state_root_task_timeout: Option<String>,
56 share_execution_cache_with_payload_builder: bool,
57 share_sparse_trie_with_payload_builder: bool,
58 suppress_persistence_during_build: bool,
59 bal_parallel_execution_disabled: bool,
60 bal_parallel_state_root_disabled: bool,
61}
62
63impl DefaultEngineValues {
64 pub fn try_init(self) -> Result<(), Self> {
66 ENGINE_DEFAULTS.set(self)
67 }
68
69 pub fn get_global() -> &'static Self {
71 ENGINE_DEFAULTS.get_or_init(Self::default)
72 }
73
74 pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
76 self.persistence_threshold = v;
77 self
78 }
79
80 pub const fn with_persistence_backpressure_threshold(mut self, v: u64) -> Self {
82 self.persistence_backpressure_threshold = v;
83 self
84 }
85
86 pub const fn with_num_state_masking_blocks(mut self, v: u64) -> Self {
88 self.num_state_masking_blocks = v;
89 self
90 }
91
92 pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
94 self.memory_block_buffer_target = v;
95 self
96 }
97
98 pub const fn with_backfill_run_threshold(mut self, v: u64) -> Self {
100 self.backfill_run_threshold = v;
101 self
102 }
103
104 pub const fn with_invalid_header_hit_eviction_threshold(mut self, v: u8) -> Self {
106 self.invalid_header_hit_eviction_threshold = v;
107 self
108 }
109
110 pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
112 self.state_cache_disabled = v;
113 self
114 }
115
116 pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
118 self.prewarming_disabled = v;
119 self
120 }
121
122 pub const fn with_txpool_prewarming_enabled(mut self, v: bool) -> Self {
124 self.txpool_prewarming_enabled = v;
125 self
126 }
127
128 pub const fn with_sender_recovery_cache_enabled(mut self, v: bool) -> Self {
130 self.sender_recovery_cache_enabled = v;
131 self
132 }
133
134 pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
136 self.state_provider_metrics = v;
137 self
138 }
139
140 pub const fn with_cross_block_cache_size(mut self, v: usize) -> Self {
142 self.cross_block_cache_size = v;
143 self
144 }
145
146 pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
148 self.state_root_task_compare_updates = v;
149 self
150 }
151
152 pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
154 self.accept_execution_requests_hash = v;
155 self
156 }
157
158 pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
160 self.multiproof_chunk_size = v;
161 self
162 }
163
164 pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
166 self.reserved_cpu_cores = v;
167 self
168 }
169
170 pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
172 self.precompile_cache_disabled = v;
173 self
174 }
175
176 pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
178 self.state_root_fallback = v;
179 self
180 }
181
182 pub const fn with_always_process_payload_attributes_on_canonical_head(
184 mut self,
185 v: bool,
186 ) -> Self {
187 self.always_process_payload_attributes_on_canonical_head = v;
188 self
189 }
190
191 pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
193 self.allow_unwind_canonical_header = v;
194 self
195 }
196
197 pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
199 self.storage_worker_count = v;
200 self
201 }
202
203 pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
205 self.account_worker_count = v;
206 self
207 }
208
209 pub const fn with_prewarming_threads(mut self, v: Option<usize>) -> Self {
211 self.prewarming_threads = v;
212 self
213 }
214
215 pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
217 self.cache_metrics_disabled = v;
218 self
219 }
220
221 pub const fn with_slow_block_threshold(mut self, v: Option<Duration>) -> Self {
223 self.slow_block_threshold = v;
224 self
225 }
226
227 pub const fn with_disable_sparse_trie_cache_pruning(mut self, v: bool) -> Self {
229 self.disable_sparse_trie_cache_pruning = v;
230 self
231 }
232
233 pub fn with_state_root_task_timeout(mut self, v: Option<String>) -> Self {
235 self.state_root_task_timeout = v;
236 self
237 }
238
239 pub const fn with_share_execution_cache_with_payload_builder(mut self, v: bool) -> Self {
241 self.share_execution_cache_with_payload_builder = v;
242 self
243 }
244
245 pub const fn with_share_sparse_trie_with_payload_builder(mut self, v: bool) -> Self {
247 self.share_sparse_trie_with_payload_builder = v;
248 self
249 }
250
251 pub const fn with_suppress_persistence_during_build(mut self, v: bool) -> Self {
253 self.suppress_persistence_during_build = v;
254 self
255 }
256
257 pub const fn with_bal_parallel_execution_disabled(mut self, v: bool) -> Self {
259 self.bal_parallel_execution_disabled = v;
260 self
261 }
262
263 pub const fn with_bal_parallel_state_root_disabled(mut self, v: bool) -> Self {
265 self.bal_parallel_state_root_disabled = v;
266 self
267 }
268}
269
270impl Default for DefaultEngineValues {
271 fn default() -> Self {
272 Self {
273 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
274 persistence_backpressure_threshold: MIN_PERSISTENCE_BACKPRESSURE_THRESHOLD,
275 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
276 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
277 backfill_run_threshold: DEFAULT_BACKFILL_RUN_THRESHOLD,
278 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
279 state_cache_disabled: false,
280 prewarming_disabled: false,
281 txpool_prewarming_enabled: false,
282 sender_recovery_cache_enabled: true,
283 state_provider_metrics: false,
284 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
285 state_root_task_compare_updates: false,
286 accept_execution_requests_hash: false,
287 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
288 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
289 precompile_cache_disabled: false,
290 state_root_fallback: false,
291 always_process_payload_attributes_on_canonical_head: false,
292 allow_unwind_canonical_header: false,
293 storage_worker_count: None,
294 account_worker_count: None,
295 prewarming_threads: None,
296 cache_metrics_disabled: false,
297 slow_block_threshold: None,
298 disable_sparse_trie_cache_pruning: false,
299 state_root_task_timeout: Some("4s".to_string()),
300 share_execution_cache_with_payload_builder: false,
301 share_sparse_trie_with_payload_builder: false,
302 suppress_persistence_during_build: false,
303 bal_parallel_execution_disabled: false,
304 bal_parallel_state_root_disabled: false,
305 }
306 }
307}
308
309fn default_persistence_backpressure_threshold(persistence_threshold: u64) -> u64 {
310 DefaultEngineValues::get_global()
311 .persistence_backpressure_threshold
312 .max(persistence_threshold.saturating_mul(2))
313}
314
315#[derive(Debug, Clone, Args, PartialEq, Eq)]
317#[command(next_help_heading = "Engine")]
318pub struct EngineArgs {
319 #[arg(
326 long = "engine.persistence-threshold",
327 env = "RETH_ENGINE_PERSISTENCE_THRESHOLD",
328 default_value_t = DefaultEngineValues::get_global().persistence_threshold
329 )]
330 pub persistence_threshold: u64,
331
332 #[arg(long = "engine.persistence-backpressure-threshold")]
340 pub persistence_backpressure_threshold: Option<u64>,
341
342 #[arg(
345 long = "engine.num-state-masking-blocks",
346 env = "RETH_ENGINE_NUM_STATE_MASKING_BLOCKS",
347 default_value_t = DefaultEngineValues::get_global().num_state_masking_blocks
348 )]
349 pub num_state_masking_blocks: u64,
350
351 #[arg(long = "engine.memory-block-buffer-target")]
356 pub memory_block_buffer_target: Option<u64>,
357
358 #[arg(
366 long = "engine.backfill-threshold",
367 env = "RETH_ENGINE_BACKFILL_THRESHOLD",
368 default_value_t = DefaultEngineValues::get_global().backfill_run_threshold
369 )]
370 pub backfill_run_threshold: u64,
371
372 #[arg(long = "engine.invalid-header-cache-hit-eviction-threshold", default_value_t = DefaultEngineValues::get_global().invalid_header_hit_eviction_threshold)]
378 pub invalid_header_hit_eviction_threshold: u8,
379
380 #[arg(long = "engine.legacy-state-root", default_value_t = false, hide = true)]
383 #[deprecated]
384 pub legacy_state_root_task_enabled: bool,
385
386 #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
389 #[deprecated]
390 pub caching_and_prewarming_enabled: bool,
391
392 #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
394 pub state_cache_disabled: bool,
395
396 #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
398 pub prewarming_disabled: bool,
399
400 #[arg(
402 long = "engine.txpool-prewarming",
403 env = "RETH_ENGINE_TXPOOL_PREWARMING",
404 default_value_t = DefaultEngineValues::get_global().txpool_prewarming_enabled
405 )]
406 pub txpool_prewarming_enabled: bool,
407
408 #[arg(
412 long = "engine.sender-recovery-cache",
413 env = "RETH_ENGINE_SENDER_RECOVERY_CACHE",
414 default_value_t = DefaultEngineValues::get_global().sender_recovery_cache_enabled,
415 num_args = 0..=1,
416 default_missing_value = "true",
417 )]
418 pub sender_recovery_cache_enabled: bool,
419
420 #[deprecated]
422 #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
423 pub parallel_sparse_trie_enabled: bool,
424
425 #[deprecated]
427 #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
428 pub parallel_sparse_trie_disabled: bool,
429
430 #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
434 pub state_provider_metrics: bool,
435
436 #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
438 pub cross_block_cache_size: usize,
439
440 #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
443 pub state_root_task_compare_updates: bool,
444
445 #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
447 pub accept_execution_requests_hash: bool,
448
449 #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size, value_parser = RangedU64ValueParser::<usize>::new().range(1..))]
451 pub multiproof_chunk_size: usize,
452
453 #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
455 pub reserved_cpu_cores: usize,
456
457 #[arg(long = "cpu-cores", value_name = "COUNT")]
460 pub cpu_cores: Option<NonZeroUsize>,
461
462 #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
465 #[deprecated]
466 pub precompile_cache_enabled: bool,
467
468 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
470 pub precompile_cache_disabled: bool,
471
472 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
474 pub state_root_fallback: bool,
475
476 #[arg(
482 long = "engine.always-process-payload-attributes-on-canonical-head",
483 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
484 )]
485 pub always_process_payload_attributes_on_canonical_head: bool,
486
487 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
490 pub allow_unwind_canonical_header: bool,
491
492 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
496 pub storage_worker_count: Option<usize>,
497
498 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
502 pub account_worker_count: Option<usize>,
503
504 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
507 pub prewarming_threads: Option<usize>,
508
509 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
511 pub cache_metrics_disabled: bool,
512
513 #[arg(long = "engine.slow-block-threshold", value_parser = parse_duration_from_secs_or_ms, value_name = "DURATION", default_value = Resettable::from(DefaultEngineValues::get_global().slow_block_threshold.map(|threshold| format_duration_as_secs_or_ms(threshold).into())))]
522 pub slow_block_threshold: Option<Duration>,
523
524 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
528 pub disable_sparse_trie_cache_pruning: bool,
529
530 #[arg(
539 long = "engine.state-root-task-timeout",
540 value_parser = humantime::parse_duration,
541 default_value = Resettable::from(DefaultEngineValues::get_global().state_root_task_timeout.as_ref().map(|v| v.as_str().into())),
542 )]
543 pub state_root_task_timeout: Option<Duration>,
544
545 #[arg(
553 long = "engine.share-execution-cache-with-payload-builder",
554 default_value_t = DefaultEngineValues::get_global().share_execution_cache_with_payload_builder,
555 )]
556 pub share_execution_cache_with_payload_builder: bool,
557
558 #[arg(
570 long = "engine.share-sparse-trie-with-payload-builder",
571 default_value_t = DefaultEngineValues::get_global().share_sparse_trie_with_payload_builder,
572 )]
573 pub share_sparse_trie_with_payload_builder: bool,
574
575 #[arg(
581 long = "engine.suppress-persistence-during-build",
582 default_value_t = DefaultEngineValues::get_global().suppress_persistence_during_build,
583 )]
584 pub suppress_persistence_during_build: bool,
585
586 #[arg(long = "engine.disable-bal-parallel-execution", default_value_t = DefaultEngineValues::get_global().bal_parallel_execution_disabled)]
588 pub bal_parallel_execution_disabled: bool,
589
590 #[arg(long = "engine.disable-bal-parallel-state-root", default_value_t = DefaultEngineValues::get_global().bal_parallel_state_root_disabled)]
593 pub bal_parallel_state_root_disabled: bool,
594
595 #[arg(long = "engine.disable-bal-batch-io", default_value_t = false)]
598 pub disable_bal_batch_io: bool,
599
600 #[arg(
603 long = "engine.prewarm-reuse",
604 env = "RETH_ENGINE_PREWARM_REUSE",
605 default_value_t = false
606 )]
607 pub prewarm_reuse: bool,
608
609 #[cfg(feature = "trie-debug")]
616 #[arg(
617 long = "engine.proof-jitter",
618 value_parser = humantime::parse_duration,
619 )]
620 pub proof_jitter: Option<Duration>,
621}
622
623#[allow(deprecated)]
624impl Default for EngineArgs {
625 fn default() -> Self {
626 let DefaultEngineValues {
627 persistence_threshold,
628 persistence_backpressure_threshold: _,
629 num_state_masking_blocks,
630 memory_block_buffer_target: _,
631 backfill_run_threshold,
632 invalid_header_hit_eviction_threshold,
633 state_cache_disabled,
634 prewarming_disabled,
635 txpool_prewarming_enabled,
636 sender_recovery_cache_enabled,
637 state_provider_metrics,
638 cross_block_cache_size,
639 state_root_task_compare_updates,
640 accept_execution_requests_hash,
641 multiproof_chunk_size,
642 reserved_cpu_cores,
643 precompile_cache_disabled,
644 state_root_fallback,
645 always_process_payload_attributes_on_canonical_head,
646 allow_unwind_canonical_header,
647 storage_worker_count,
648 account_worker_count,
649 prewarming_threads,
650 cache_metrics_disabled,
651 slow_block_threshold,
652 disable_sparse_trie_cache_pruning,
653 state_root_task_timeout,
654 share_execution_cache_with_payload_builder,
655 share_sparse_trie_with_payload_builder,
656 suppress_persistence_during_build,
657 bal_parallel_execution_disabled,
658 bal_parallel_state_root_disabled,
659 } = DefaultEngineValues::get_global().clone();
660 Self {
661 persistence_threshold,
662 persistence_backpressure_threshold: None,
663 num_state_masking_blocks,
664 memory_block_buffer_target: None,
665 backfill_run_threshold,
666 invalid_header_hit_eviction_threshold,
667 state_root_task_compare_updates,
668 legacy_state_root_task_enabled: false,
669 caching_and_prewarming_enabled: true,
670 state_cache_disabled,
671 prewarming_disabled,
672 txpool_prewarming_enabled,
673 sender_recovery_cache_enabled,
674 parallel_sparse_trie_enabled: true,
675 parallel_sparse_trie_disabled: false,
676 state_provider_metrics,
677 cross_block_cache_size,
678 accept_execution_requests_hash,
679 multiproof_chunk_size,
680 reserved_cpu_cores,
681 cpu_cores: None,
682 precompile_cache_enabled: true,
683 precompile_cache_disabled,
684 state_root_fallback,
685 always_process_payload_attributes_on_canonical_head,
686 allow_unwind_canonical_header,
687 storage_worker_count,
688 account_worker_count,
689 prewarming_threads,
690 cache_metrics_disabled,
691 slow_block_threshold,
692 disable_sparse_trie_cache_pruning,
693 state_root_task_timeout: state_root_task_timeout
694 .as_deref()
695 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
696 share_execution_cache_with_payload_builder,
697 share_sparse_trie_with_payload_builder,
698 suppress_persistence_during_build,
699 bal_parallel_execution_disabled,
700 bal_parallel_state_root_disabled,
701 disable_bal_batch_io: false,
702 prewarm_reuse: false,
703 #[cfg(feature = "trie-debug")]
704 proof_jitter: None,
705 }
706 }
707}
708
709impl EngineArgs {
710 pub const fn num_state_masking_blocks(&self) -> u64 {
712 if self.persistence_threshold == 0 {
713 0
714 } else {
715 self.num_state_masking_blocks
716 }
717 }
718
719 pub fn memory_block_buffer_target(&self) -> u64 {
721 self.memory_block_buffer_target.unwrap_or_else(|| {
722 self.persistence_threshold
723 .min(DefaultEngineValues::get_global().memory_block_buffer_target)
724 })
725 }
726
727 pub fn persistence_backpressure_threshold(&self) -> u64 {
729 self.persistence_backpressure_threshold.unwrap_or_else(|| {
730 default_persistence_backpressure_threshold(self.persistence_threshold)
731 })
732 }
733
734 pub fn validate(&self) -> eyre::Result<()> {
736 let persistence_backpressure_threshold = self.persistence_backpressure_threshold();
737 let memory_block_buffer_target = self.memory_block_buffer_target();
738 let num_state_masking_blocks = self.num_state_masking_blocks();
739 ensure!(
740 persistence_backpressure_threshold > self.persistence_threshold,
741 "--engine.persistence-backpressure-threshold ({}) must be greater than --engine.persistence-threshold ({})",
742 persistence_backpressure_threshold,
743 self.persistence_threshold
744 );
745 ensure!(
746 memory_block_buffer_target <= self.persistence_threshold,
747 "--engine.memory-block-buffer-target ({}) must be less than or equal to --engine.persistence-threshold ({})",
748 memory_block_buffer_target,
749 self.persistence_threshold,
750 );
751 ensure!(
752 num_state_masking_blocks == 0 ||
753 matches!(
754 num_state_masking_blocks.checked_add(memory_block_buffer_target),
755 Some(window) if window < self.persistence_threshold
756 ),
757 "--engine.num-state-masking-blocks ({}) + --engine.memory-block-buffer-target ({}) must be less than --engine.persistence-threshold ({})",
758 num_state_masking_blocks,
759 memory_block_buffer_target,
760 self.persistence_threshold,
761 );
762 ensure!(
763 !self.state_cache_disabled || !self.txpool_prewarming_enabled,
764 "--engine.txpool-prewarming conflicts with --engine.disable-state-cache"
765 );
766 ensure!(
767 self.bal_parallel_execution_disabled || !self.bal_parallel_state_root_disabled,
768 "--engine.disable-bal-parallel-state-root requires --engine.disable-bal-parallel-execution because BAL parallel execution depends on BAL prewarm state-root updates"
769 );
770 Ok(())
771 }
772
773 pub fn tree_config(&self) -> TreeConfig {
775 #[allow(deprecated)]
776 if self.legacy_state_root_task_enabled {
777 tracing::warn!(target: "reth::cli", "--engine.legacy-state-root has no effect anymore, use --engine.state-root-fallback to force synchronous state root computation");
778 }
779 let config = TreeConfig::default()
780 .with_persistence_threshold(0)
782 .with_persistence_backpressure_threshold(self.persistence_backpressure_threshold())
783 .with_persistence_threshold(self.persistence_threshold)
784 .with_memory_block_buffer_target(self.memory_block_buffer_target())
785 .with_num_state_masking_blocks(self.num_state_masking_blocks())
786 .with_backfill_run_threshold(self.backfill_run_threshold)
787 .with_invalid_header_hit_eviction_threshold(self.invalid_header_hit_eviction_threshold)
788 .without_state_cache(self.state_cache_disabled)
789 .without_prewarming(self.prewarming_disabled)
790 .with_txpool_prewarming(self.txpool_prewarming_enabled)
791 .with_state_provider_metrics(self.state_provider_metrics)
792 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
793 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
794 .with_multiproof_chunk_size(self.multiproof_chunk_size)
795 .with_reserved_cpu_cores(self.reserved_cpu_cores)
796 .without_precompile_cache(self.precompile_cache_disabled)
797 .with_state_root_fallback(self.state_root_fallback)
798 .with_always_process_payload_attributes_on_canonical_head(
799 self.always_process_payload_attributes_on_canonical_head,
800 )
801 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
802 .without_cache_metrics(self.cache_metrics_disabled)
803 .with_slow_block_threshold(self.slow_block_threshold)
804 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
805 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
806 .with_share_execution_cache_with_payload_builder(
807 self.share_execution_cache_with_payload_builder,
808 )
809 .with_share_sparse_trie_with_payload_builder(
810 self.share_sparse_trie_with_payload_builder,
811 )
812 .with_suppress_persistence_during_build(self.suppress_persistence_during_build)
813 .without_bal_parallel_execution(self.bal_parallel_execution_disabled)
814 .without_bal_parallel_state_root(self.bal_parallel_state_root_disabled)
815 .without_bal_batch_io(self.disable_bal_batch_io)
816 .with_prewarm_reuse(self.prewarm_reuse);
817 #[cfg(feature = "trie-debug")]
818 let config = config.with_proof_jitter(self.proof_jitter);
819 if let Some(cpu_cores) = self.cpu_cores {
820 config.with_has_enough_parallelism(cpu_cores.get() >= 5)
821 } else {
822 config
823 }
824 }
825}
826
827#[cfg(test)]
828mod tests {
829 use super::*;
830 use clap::Parser;
831
832 #[derive(Parser)]
834 struct CommandParser<T: Args> {
835 #[command(flatten)]
836 args: T,
837 }
838
839 #[test]
840 fn cpu_cores_override_controls_parallel_state_root() {
841 for (cores, enabled) in [(1, false), (4, false), (5, true), (8, true)] {
842 let args = CommandParser::<EngineArgs>::parse_from([
843 "reth",
844 "--cpu-cores",
845 &cores.to_string(),
846 ])
847 .args;
848 assert_eq!(args.cpu_cores.unwrap().get(), cores);
849 assert_eq!(args.tree_config().use_state_root_task(), enabled);
850 }
851 }
852
853 #[test]
854 fn cpu_cores_override_preserves_state_root_fallback() {
855 let args = CommandParser::<EngineArgs>::parse_from([
856 "reth",
857 "--cpu-cores",
858 "8",
859 "--engine.state-root-fallback",
860 ])
861 .args;
862 assert!(!args.tree_config().use_state_root_task());
863 }
864
865 #[test]
866 fn test_parse_engine_args() {
867 let default_args = EngineArgs::default();
868 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
869 assert_eq!(args, default_args);
870 assert_eq!(args.persistence_threshold, 50);
871 assert_eq!(args.num_state_masking_blocks, 30);
872 assert_eq!(args.memory_block_buffer_target, None);
873 assert_eq!(args.memory_block_buffer_target(), 5);
874 assert_eq!(args.persistence_backpressure_threshold(), 100);
875 args.validate().unwrap();
876 let config = args.tree_config();
877 assert_eq!(config.persistence_threshold(), 50);
878 assert_eq!(config.num_state_masking_blocks(), 30);
879 assert_eq!(config.persistence_backpressure_threshold(), 100);
880 }
881
882 #[test]
883 fn txpool_prewarming_is_disabled_by_default_and_can_be_enabled() {
884 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
885 assert!(!args.txpool_prewarming_enabled);
886 assert!(!args.tree_config().txpool_prewarming());
887
888 let args =
889 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.txpool-prewarming"]).args;
890 assert!(args.txpool_prewarming_enabled);
891 assert!(args.tree_config().txpool_prewarming());
892 }
893
894 #[test]
895 fn validate_rejects_txpool_prewarming_with_disabled_state_cache() {
896 let args = EngineArgs {
897 state_cache_disabled: true,
898 txpool_prewarming_enabled: true,
899 ..EngineArgs::default()
900 };
901
902 let err = args.validate().unwrap_err().to_string();
903 assert!(err.contains("engine.txpool-prewarming"));
904 assert!(err.contains("engine.disable-state-cache"));
905 }
906
907 #[test]
908 fn default_backpressure_threshold_uses_parsed_persistence_args() {
909 let args = CommandParser::<EngineArgs>::parse_from([
910 "reth",
911 "--engine.persistence-threshold",
912 "100",
913 "--engine.memory-block-buffer-target",
914 "50",
915 ])
916 .args;
917
918 assert_eq!(args.persistence_backpressure_threshold(), 200);
919
920 let tree_config = args.tree_config();
921 assert_eq!(tree_config.persistence_threshold(), 100);
922 assert_eq!(tree_config.memory_block_buffer_target(), 50);
923 assert_eq!(tree_config.persistence_backpressure_threshold(), 200);
924 }
925
926 #[test]
927 fn default_backpressure_threshold_uses_minimum_when_larger() {
928 let args = CommandParser::<EngineArgs>::parse_from([
929 "reth",
930 "--engine.persistence-threshold",
931 "4",
932 ])
933 .args;
934
935 assert_eq!(
936 args.persistence_backpressure_threshold(),
937 DefaultEngineValues::get_global().persistence_backpressure_threshold
938 );
939 assert_eq!(args.persistence_backpressure_threshold(), 16);
940 }
941
942 #[test]
943 fn explicit_backpressure_threshold_overrides_calculated_default() {
944 let args = CommandParser::<EngineArgs>::parse_from([
945 "reth",
946 "--engine.persistence-threshold",
947 "100",
948 "--engine.memory-block-buffer-target",
949 "50",
950 "--engine.persistence-backpressure-threshold",
951 "101",
952 ])
953 .args;
954
955 assert_eq!(args.persistence_backpressure_threshold(), 101);
956 }
957
958 #[test]
959 fn sender_recovery_cache_is_enabled_by_default_and_can_be_disabled() {
960 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
961 assert!(args.sender_recovery_cache_enabled);
962
963 let args =
964 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.sender-recovery-cache"])
965 .args;
966 assert!(args.sender_recovery_cache_enabled);
967
968 let args = CommandParser::<EngineArgs>::parse_from([
969 "reth",
970 "--engine.sender-recovery-cache=true",
971 ])
972 .args;
973 assert!(args.sender_recovery_cache_enabled);
974
975 let args = CommandParser::<EngineArgs>::parse_from([
976 "reth",
977 "--engine.sender-recovery-cache=false",
978 ])
979 .args;
980 assert!(!args.sender_recovery_cache_enabled);
981
982 let args = CommandParser::<EngineArgs>::parse_from([
983 "reth",
984 "--engine.sender-recovery-cache",
985 "false",
986 ])
987 .args;
988 assert!(!args.sender_recovery_cache_enabled);
989 }
990
991 #[test]
992 #[allow(deprecated)]
993 fn engine_args() {
994 let args = EngineArgs {
995 persistence_threshold: 100,
996 persistence_backpressure_threshold: Some(101),
997 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
998 memory_block_buffer_target: Some(50),
999 backfill_run_threshold: 100,
1000 invalid_header_hit_eviction_threshold: 7,
1001 legacy_state_root_task_enabled: true,
1002 caching_and_prewarming_enabled: true,
1003 state_cache_disabled: true,
1004 prewarming_disabled: true,
1005 txpool_prewarming_enabled: false,
1007 sender_recovery_cache_enabled: true,
1008 parallel_sparse_trie_enabled: true,
1009 parallel_sparse_trie_disabled: false,
1010 state_provider_metrics: true,
1011 cross_block_cache_size: 256,
1012 state_root_task_compare_updates: true,
1013 accept_execution_requests_hash: true,
1014 multiproof_chunk_size: 512,
1015 reserved_cpu_cores: 4,
1016 cpu_cores: Some(NonZeroUsize::new(8).unwrap()),
1017 precompile_cache_enabled: true,
1018 precompile_cache_disabled: true,
1019 state_root_fallback: true,
1020 always_process_payload_attributes_on_canonical_head: true,
1021 allow_unwind_canonical_header: true,
1022 storage_worker_count: Some(16),
1023 account_worker_count: Some(8),
1024 prewarming_threads: Some(4),
1025 cache_metrics_disabled: true,
1026 slow_block_threshold: None,
1027 disable_sparse_trie_cache_pruning: true,
1028 state_root_task_timeout: Some(Duration::from_secs(2)),
1029 share_execution_cache_with_payload_builder: false,
1030 share_sparse_trie_with_payload_builder: false,
1031 suppress_persistence_during_build: false,
1032 bal_parallel_execution_disabled: true,
1033 bal_parallel_state_root_disabled: true,
1034 disable_bal_batch_io: true,
1035 prewarm_reuse: true,
1036 #[cfg(feature = "trie-debug")]
1037 proof_jitter: None,
1038 };
1039
1040 let parsed_args = CommandParser::<EngineArgs>::parse_from([
1041 "reth",
1042 "--engine.persistence-threshold",
1043 "100",
1044 "--engine.persistence-backpressure-threshold",
1045 "101",
1046 "--engine.memory-block-buffer-target",
1047 "50",
1048 "--engine.backfill-threshold",
1049 "100",
1050 "--engine.invalid-header-cache-hit-eviction-threshold",
1051 "7",
1052 "--engine.legacy-state-root",
1053 "--engine.disable-state-cache",
1054 "--engine.disable-prewarming",
1055 "--engine.sender-recovery-cache",
1056 "--engine.state-provider-metrics",
1057 "--engine.cross-block-cache-size",
1058 "256",
1059 "--engine.state-root-task-compare-updates",
1060 "--engine.accept-execution-requests-hash",
1061 "--engine.multiproof-chunk-size",
1062 "512",
1063 "--engine.reserved-cpu-cores",
1064 "4",
1065 "--cpu-cores",
1066 "8",
1067 "--engine.disable-precompile-cache",
1068 "--engine.state-root-fallback",
1069 "--engine.always-process-payload-attributes-on-canonical-head",
1070 "--engine.allow-unwind-canonical-header",
1071 "--engine.storage-worker-count",
1072 "16",
1073 "--engine.account-worker-count",
1074 "8",
1075 "--engine.prewarming-threads",
1076 "4",
1077 "--engine.disable-cache-metrics",
1078 "--engine.disable-sparse-trie-cache-pruning",
1079 "--engine.state-root-task-timeout",
1080 "2s",
1081 "--engine.disable-bal-parallel-execution",
1082 "--engine.disable-bal-parallel-state-root",
1083 "--engine.disable-bal-batch-io",
1084 "--engine.prewarm-reuse",
1085 ])
1086 .args;
1087
1088 assert_eq!(parsed_args, args);
1089 }
1090
1091 #[test]
1092 fn validate_rejects_invalid_backpressure_threshold() {
1093 let args = EngineArgs {
1094 persistence_threshold: 4,
1095 persistence_backpressure_threshold: Some(4),
1096 ..EngineArgs::default()
1097 };
1098
1099 let err = args.validate().unwrap_err().to_string();
1100 assert!(err.contains("engine.persistence-backpressure-threshold"));
1101 assert!(err.contains("engine.persistence-threshold"));
1102 }
1103
1104 #[test]
1105 fn validate_memory_block_buffer_target() {
1106 let args = EngineArgs {
1107 persistence_threshold: 4,
1108 num_state_masking_blocks: 0,
1109 memory_block_buffer_target: Some(4),
1110 ..EngineArgs::default()
1111 };
1112 args.validate().unwrap();
1113
1114 let args = EngineArgs { memory_block_buffer_target: Some(5), ..args };
1115 let err = args.validate().unwrap_err().to_string();
1116 assert!(err.contains("engine.memory-block-buffer-target"));
1117 assert!(err.contains("engine.persistence-threshold"));
1118 }
1119
1120 #[test]
1121 fn test_parse_backfill_threshold() {
1122 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1123 let config = args.tree_config();
1124 assert_eq!(config.backfill_run_threshold(), DEFAULT_BACKFILL_RUN_THRESHOLD);
1125
1126 for (threshold, expected) in [("500", 500), ("2048", 1024)] {
1127 let args = CommandParser::<EngineArgs>::parse_from([
1128 "reth",
1129 "--engine.backfill-threshold",
1130 threshold,
1131 ])
1132 .args;
1133 assert_eq!(args.tree_config().backfill_run_threshold(), expected);
1134 }
1135 }
1136
1137 #[test]
1138 fn validate_rejects_state_masking_window_at_or_above_threshold() {
1139 let args = EngineArgs {
1140 persistence_threshold: 4,
1141 num_state_masking_blocks: 2,
1142 memory_block_buffer_target: Some(2),
1143 ..EngineArgs::default()
1144 };
1145
1146 let err = args.validate().unwrap_err().to_string();
1147 assert!(err.contains("engine.num-state-masking-blocks"));
1148 }
1149
1150 #[test]
1151 fn validate_rejects_overflowing_state_masking_window() {
1152 let args = EngineArgs {
1153 persistence_threshold: 7,
1154 num_state_masking_blocks: u64::MAX,
1155 ..EngineArgs::default()
1156 };
1157
1158 let err = args.validate().unwrap_err().to_string();
1159 assert!(err.contains("engine.num-state-masking-blocks"));
1160 }
1161
1162 #[test]
1163 fn default_memory_block_buffer_target_is_bounded_by_persistence_threshold() {
1164 let args = CommandParser::<EngineArgs>::parse_from([
1165 "reth",
1166 "--engine.persistence-threshold",
1167 "4",
1168 "--engine.num-state-masking-blocks",
1169 "0",
1170 ])
1171 .args;
1172
1173 assert_eq!(args.memory_block_buffer_target, None);
1174 assert_eq!(args.memory_block_buffer_target(), 4);
1175 assert_eq!(args.tree_config().memory_block_buffer_target(), 4);
1176 args.validate().unwrap();
1177 }
1178
1179 #[test]
1180 fn explicit_persistence_settings_can_be_lower_than_defaults() {
1181 let args = CommandParser::<EngineArgs>::parse_from([
1182 "reth",
1183 "--engine.persistence-threshold",
1184 "0",
1185 "--engine.persistence-backpressure-threshold",
1186 "1",
1187 ])
1188 .args;
1189
1190 args.validate().unwrap();
1191 let config = args.tree_config();
1192 assert_eq!(config.persistence_threshold(), 0);
1193 assert_eq!(config.num_state_masking_blocks(), 0);
1194 assert_eq!(config.memory_block_buffer_target(), 0);
1195 assert_eq!(config.persistence_backpressure_threshold(), 1);
1196 }
1197
1198 #[test]
1199 fn parse_rejects_zero_multiproof_chunk_size() {
1200 let result = CommandParser::<EngineArgs>::try_parse_from([
1201 "reth",
1202 "--engine.multiproof-chunk-size",
1203 "0",
1204 ]);
1205
1206 assert!(result.is_err());
1207 }
1208
1209 #[test]
1210 fn validate_rejects_bal_parallel_execution_without_bal_parallel_state_root() {
1211 let args = EngineArgs {
1212 bal_parallel_execution_disabled: false,
1213 bal_parallel_state_root_disabled: true,
1214 ..EngineArgs::default()
1215 };
1216
1217 let err = args.validate().unwrap_err().to_string();
1218 assert!(err.contains("engine.disable-bal-parallel-state-root"));
1219 assert!(err.contains("engine.disable-bal-parallel-execution"));
1220 }
1221
1222 #[test]
1223 fn test_parse_slow_block_threshold() {
1224 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1226 assert_eq!(args.slow_block_threshold, None);
1227
1228 let args =
1230 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
1231 .args;
1232 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
1233
1234 let args = CommandParser::<EngineArgs>::parse_from([
1236 "reth",
1237 "--engine.slow-block-threshold",
1238 "500",
1239 ])
1240 .args;
1241 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
1242
1243 let args = CommandParser::<EngineArgs>::parse_from([
1244 "reth",
1245 "--engine.slow-block-threshold",
1246 "500ms",
1247 ])
1248 .args;
1249 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
1250 }
1251
1252 #[test]
1253 fn test_parse_invalid_header_hit_eviction_threshold() {
1254 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1255 assert_eq!(
1256 args.invalid_header_hit_eviction_threshold,
1257 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1258 );
1259 assert_eq!(
1260 args.tree_config().invalid_header_hit_eviction_threshold(),
1261 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1262 );
1263
1264 let args = CommandParser::<EngineArgs>::parse_from([
1265 "reth",
1266 "--engine.invalid-header-cache-hit-eviction-threshold",
1267 "0",
1268 ])
1269 .args;
1270 assert_eq!(args.invalid_header_hit_eviction_threshold, 0);
1271 assert_eq!(args.tree_config().invalid_header_hit_eviction_threshold(), 0);
1272 }
1273
1274 #[test]
1275 fn test_parse_share_sparse_trie_flag() {
1276 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1277 assert!(!args.share_sparse_trie_with_payload_builder);
1278 assert!(!args.tree_config().share_sparse_trie_with_payload_builder());
1279
1280 let args = CommandParser::<EngineArgs>::parse_from([
1281 "reth",
1282 "--engine.share-sparse-trie-with-payload-builder",
1283 ])
1284 .args;
1285 assert!(args.share_sparse_trie_with_payload_builder);
1286 assert!(args.tree_config().share_sparse_trie_with_payload_builder());
1287 }
1288}