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::{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 = "engine.precompile-cache", default_value = "true", hide = true)]
460 #[deprecated]
461 pub precompile_cache_enabled: bool,
462
463 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
465 pub precompile_cache_disabled: bool,
466
467 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
469 pub state_root_fallback: bool,
470
471 #[arg(
477 long = "engine.always-process-payload-attributes-on-canonical-head",
478 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
479 )]
480 pub always_process_payload_attributes_on_canonical_head: bool,
481
482 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
485 pub allow_unwind_canonical_header: bool,
486
487 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
490 pub storage_worker_count: Option<usize>,
491
492 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
495 pub account_worker_count: Option<usize>,
496
497 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
500 pub prewarming_threads: Option<usize>,
501
502 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
504 pub cache_metrics_disabled: bool,
505
506 #[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())))]
515 pub slow_block_threshold: Option<Duration>,
516
517 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
521 pub disable_sparse_trie_cache_pruning: bool,
522
523 #[arg(
532 long = "engine.state-root-task-timeout",
533 value_parser = humantime::parse_duration,
534 default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("4s"),
535 )]
536 pub state_root_task_timeout: Option<Duration>,
537
538 #[arg(
546 long = "engine.share-execution-cache-with-payload-builder",
547 default_value_t = DefaultEngineValues::get_global().share_execution_cache_with_payload_builder,
548 )]
549 pub share_execution_cache_with_payload_builder: bool,
550
551 #[arg(
563 long = "engine.share-sparse-trie-with-payload-builder",
564 default_value_t = DefaultEngineValues::get_global().share_sparse_trie_with_payload_builder,
565 )]
566 pub share_sparse_trie_with_payload_builder: bool,
567
568 #[arg(
574 long = "engine.suppress-persistence-during-build",
575 default_value_t = DefaultEngineValues::get_global().suppress_persistence_during_build,
576 )]
577 pub suppress_persistence_during_build: bool,
578
579 #[arg(long = "engine.disable-bal-parallel-execution", default_value_t = DefaultEngineValues::get_global().bal_parallel_execution_disabled)]
581 pub bal_parallel_execution_disabled: bool,
582
583 #[arg(long = "engine.disable-bal-parallel-state-root", default_value_t = DefaultEngineValues::get_global().bal_parallel_state_root_disabled)]
586 pub bal_parallel_state_root_disabled: bool,
587
588 #[arg(long = "engine.disable-bal-batch-io", default_value_t = false)]
591 pub disable_bal_batch_io: bool,
592
593 #[cfg(feature = "trie-debug")]
600 #[arg(
601 long = "engine.proof-jitter",
602 value_parser = humantime::parse_duration,
603 )]
604 pub proof_jitter: Option<Duration>,
605}
606
607#[allow(deprecated)]
608impl Default for EngineArgs {
609 fn default() -> Self {
610 let DefaultEngineValues {
611 persistence_threshold,
612 persistence_backpressure_threshold: _,
613 num_state_masking_blocks,
614 memory_block_buffer_target: _,
615 backfill_run_threshold,
616 invalid_header_hit_eviction_threshold,
617 state_cache_disabled,
618 prewarming_disabled,
619 txpool_prewarming_enabled,
620 sender_recovery_cache_enabled,
621 state_provider_metrics,
622 cross_block_cache_size,
623 state_root_task_compare_updates,
624 accept_execution_requests_hash,
625 multiproof_chunk_size,
626 reserved_cpu_cores,
627 precompile_cache_disabled,
628 state_root_fallback,
629 always_process_payload_attributes_on_canonical_head,
630 allow_unwind_canonical_header,
631 storage_worker_count,
632 account_worker_count,
633 prewarming_threads,
634 cache_metrics_disabled,
635 slow_block_threshold,
636 disable_sparse_trie_cache_pruning,
637 state_root_task_timeout,
638 share_execution_cache_with_payload_builder,
639 share_sparse_trie_with_payload_builder,
640 suppress_persistence_during_build,
641 bal_parallel_execution_disabled,
642 bal_parallel_state_root_disabled,
643 } = DefaultEngineValues::get_global().clone();
644 Self {
645 persistence_threshold,
646 persistence_backpressure_threshold: None,
647 num_state_masking_blocks,
648 memory_block_buffer_target: None,
649 backfill_run_threshold,
650 invalid_header_hit_eviction_threshold,
651 state_root_task_compare_updates,
652 legacy_state_root_task_enabled: false,
653 caching_and_prewarming_enabled: true,
654 state_cache_disabled,
655 prewarming_disabled,
656 txpool_prewarming_enabled,
657 sender_recovery_cache_enabled,
658 parallel_sparse_trie_enabled: true,
659 parallel_sparse_trie_disabled: false,
660 state_provider_metrics,
661 cross_block_cache_size,
662 accept_execution_requests_hash,
663 multiproof_chunk_size,
664 reserved_cpu_cores,
665 precompile_cache_enabled: true,
666 precompile_cache_disabled,
667 state_root_fallback,
668 always_process_payload_attributes_on_canonical_head,
669 allow_unwind_canonical_header,
670 storage_worker_count,
671 account_worker_count,
672 prewarming_threads,
673 cache_metrics_disabled,
674 slow_block_threshold,
675 disable_sparse_trie_cache_pruning,
676 state_root_task_timeout: state_root_task_timeout
677 .as_deref()
678 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
679 share_execution_cache_with_payload_builder,
680 share_sparse_trie_with_payload_builder,
681 suppress_persistence_during_build,
682 bal_parallel_execution_disabled,
683 bal_parallel_state_root_disabled,
684 disable_bal_batch_io: false,
685 #[cfg(feature = "trie-debug")]
686 proof_jitter: None,
687 }
688 }
689}
690
691impl EngineArgs {
692 pub const fn num_state_masking_blocks(&self) -> u64 {
694 if self.persistence_threshold == 0 {
695 0
696 } else {
697 self.num_state_masking_blocks
698 }
699 }
700
701 pub fn memory_block_buffer_target(&self) -> u64 {
703 self.memory_block_buffer_target.unwrap_or_else(|| {
704 self.persistence_threshold
705 .min(DefaultEngineValues::get_global().memory_block_buffer_target)
706 })
707 }
708
709 pub fn persistence_backpressure_threshold(&self) -> u64 {
711 self.persistence_backpressure_threshold.unwrap_or_else(|| {
712 default_persistence_backpressure_threshold(self.persistence_threshold)
713 })
714 }
715
716 pub fn validate(&self) -> eyre::Result<()> {
718 let persistence_backpressure_threshold = self.persistence_backpressure_threshold();
719 let memory_block_buffer_target = self.memory_block_buffer_target();
720 let num_state_masking_blocks = self.num_state_masking_blocks();
721 ensure!(
722 persistence_backpressure_threshold > self.persistence_threshold,
723 "--engine.persistence-backpressure-threshold ({}) must be greater than --engine.persistence-threshold ({})",
724 persistence_backpressure_threshold,
725 self.persistence_threshold
726 );
727 ensure!(
728 memory_block_buffer_target <= self.persistence_threshold,
729 "--engine.memory-block-buffer-target ({}) must be less than or equal to --engine.persistence-threshold ({})",
730 memory_block_buffer_target,
731 self.persistence_threshold,
732 );
733 ensure!(
734 num_state_masking_blocks == 0 ||
735 matches!(
736 num_state_masking_blocks.checked_add(memory_block_buffer_target),
737 Some(window) if window < self.persistence_threshold
738 ),
739 "--engine.num-state-masking-blocks ({}) + --engine.memory-block-buffer-target ({}) must be less than --engine.persistence-threshold ({})",
740 num_state_masking_blocks,
741 memory_block_buffer_target,
742 self.persistence_threshold,
743 );
744 ensure!(
745 !self.state_cache_disabled || !self.txpool_prewarming_enabled,
746 "--engine.txpool-prewarming conflicts with --engine.disable-state-cache"
747 );
748 ensure!(
749 self.bal_parallel_execution_disabled || !self.bal_parallel_state_root_disabled,
750 "--engine.disable-bal-parallel-state-root requires --engine.disable-bal-parallel-execution because BAL parallel execution depends on BAL prewarm state-root updates"
751 );
752 Ok(())
753 }
754
755 pub fn tree_config(&self) -> TreeConfig {
757 #[allow(deprecated)]
758 if self.legacy_state_root_task_enabled {
759 tracing::warn!(target: "reth::cli", "--engine.legacy-state-root has no effect anymore, use --engine.state-root-fallback to force synchronous state root computation");
760 }
761 let config = TreeConfig::default()
762 .with_persistence_threshold(0)
764 .with_persistence_backpressure_threshold(self.persistence_backpressure_threshold())
765 .with_persistence_threshold(self.persistence_threshold)
766 .with_memory_block_buffer_target(self.memory_block_buffer_target())
767 .with_num_state_masking_blocks(self.num_state_masking_blocks())
768 .with_backfill_run_threshold(self.backfill_run_threshold)
769 .with_invalid_header_hit_eviction_threshold(self.invalid_header_hit_eviction_threshold)
770 .without_state_cache(self.state_cache_disabled)
771 .without_prewarming(self.prewarming_disabled)
772 .with_txpool_prewarming(self.txpool_prewarming_enabled)
773 .with_state_provider_metrics(self.state_provider_metrics)
774 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
775 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
776 .with_multiproof_chunk_size(self.multiproof_chunk_size)
777 .with_reserved_cpu_cores(self.reserved_cpu_cores)
778 .without_precompile_cache(self.precompile_cache_disabled)
779 .with_state_root_fallback(self.state_root_fallback)
780 .with_always_process_payload_attributes_on_canonical_head(
781 self.always_process_payload_attributes_on_canonical_head,
782 )
783 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
784 .without_cache_metrics(self.cache_metrics_disabled)
785 .with_slow_block_threshold(self.slow_block_threshold)
786 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
787 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
788 .with_share_execution_cache_with_payload_builder(
789 self.share_execution_cache_with_payload_builder,
790 )
791 .with_share_sparse_trie_with_payload_builder(
792 self.share_sparse_trie_with_payload_builder,
793 )
794 .with_suppress_persistence_during_build(self.suppress_persistence_during_build)
795 .without_bal_parallel_execution(self.bal_parallel_execution_disabled)
796 .without_bal_parallel_state_root(self.bal_parallel_state_root_disabled)
797 .without_bal_batch_io(self.disable_bal_batch_io);
798 #[cfg(feature = "trie-debug")]
799 let config = config.with_proof_jitter(self.proof_jitter);
800 config
801 }
802}
803
804#[cfg(test)]
805mod tests {
806 use super::*;
807 use clap::Parser;
808
809 #[derive(Parser)]
811 struct CommandParser<T: Args> {
812 #[command(flatten)]
813 args: T,
814 }
815
816 #[test]
817 fn test_parse_engine_args() {
818 let default_args = EngineArgs::default();
819 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
820 assert_eq!(args, default_args);
821 assert_eq!(args.persistence_threshold, 50);
822 assert_eq!(args.num_state_masking_blocks, 30);
823 assert_eq!(args.memory_block_buffer_target, None);
824 assert_eq!(args.memory_block_buffer_target(), 5);
825 assert_eq!(args.persistence_backpressure_threshold(), 100);
826 args.validate().unwrap();
827 let config = args.tree_config();
828 assert_eq!(config.persistence_threshold(), 50);
829 assert_eq!(config.num_state_masking_blocks(), 30);
830 assert_eq!(config.persistence_backpressure_threshold(), 100);
831 }
832
833 #[test]
834 fn txpool_prewarming_is_disabled_by_default_and_can_be_enabled() {
835 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
836 assert!(!args.txpool_prewarming_enabled);
837 assert!(!args.tree_config().txpool_prewarming());
838
839 let args =
840 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.txpool-prewarming"]).args;
841 assert!(args.txpool_prewarming_enabled);
842 assert!(args.tree_config().txpool_prewarming());
843 }
844
845 #[test]
846 fn validate_rejects_txpool_prewarming_with_disabled_state_cache() {
847 let args = EngineArgs {
848 state_cache_disabled: true,
849 txpool_prewarming_enabled: true,
850 ..EngineArgs::default()
851 };
852
853 let err = args.validate().unwrap_err().to_string();
854 assert!(err.contains("engine.txpool-prewarming"));
855 assert!(err.contains("engine.disable-state-cache"));
856 }
857
858 #[test]
859 fn default_backpressure_threshold_uses_parsed_persistence_args() {
860 let args = CommandParser::<EngineArgs>::parse_from([
861 "reth",
862 "--engine.persistence-threshold",
863 "100",
864 "--engine.memory-block-buffer-target",
865 "50",
866 ])
867 .args;
868
869 assert_eq!(args.persistence_backpressure_threshold(), 200);
870
871 let tree_config = args.tree_config();
872 assert_eq!(tree_config.persistence_threshold(), 100);
873 assert_eq!(tree_config.memory_block_buffer_target(), 50);
874 assert_eq!(tree_config.persistence_backpressure_threshold(), 200);
875 }
876
877 #[test]
878 fn default_backpressure_threshold_uses_minimum_when_larger() {
879 let args = CommandParser::<EngineArgs>::parse_from([
880 "reth",
881 "--engine.persistence-threshold",
882 "4",
883 ])
884 .args;
885
886 assert_eq!(
887 args.persistence_backpressure_threshold(),
888 DefaultEngineValues::get_global().persistence_backpressure_threshold
889 );
890 assert_eq!(args.persistence_backpressure_threshold(), 16);
891 }
892
893 #[test]
894 fn explicit_backpressure_threshold_overrides_calculated_default() {
895 let args = CommandParser::<EngineArgs>::parse_from([
896 "reth",
897 "--engine.persistence-threshold",
898 "100",
899 "--engine.memory-block-buffer-target",
900 "50",
901 "--engine.persistence-backpressure-threshold",
902 "101",
903 ])
904 .args;
905
906 assert_eq!(args.persistence_backpressure_threshold(), 101);
907 }
908
909 #[test]
910 fn sender_recovery_cache_is_enabled_by_default_and_can_be_disabled() {
911 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
912 assert!(args.sender_recovery_cache_enabled);
913
914 let args =
915 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.sender-recovery-cache"])
916 .args;
917 assert!(args.sender_recovery_cache_enabled);
918
919 let args = CommandParser::<EngineArgs>::parse_from([
920 "reth",
921 "--engine.sender-recovery-cache=true",
922 ])
923 .args;
924 assert!(args.sender_recovery_cache_enabled);
925
926 let args = CommandParser::<EngineArgs>::parse_from([
927 "reth",
928 "--engine.sender-recovery-cache=false",
929 ])
930 .args;
931 assert!(!args.sender_recovery_cache_enabled);
932
933 let args = CommandParser::<EngineArgs>::parse_from([
934 "reth",
935 "--engine.sender-recovery-cache",
936 "false",
937 ])
938 .args;
939 assert!(!args.sender_recovery_cache_enabled);
940 }
941
942 #[test]
943 #[allow(deprecated)]
944 fn engine_args() {
945 let args = EngineArgs {
946 persistence_threshold: 100,
947 persistence_backpressure_threshold: Some(101),
948 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
949 memory_block_buffer_target: Some(50),
950 backfill_run_threshold: 100,
951 invalid_header_hit_eviction_threshold: 7,
952 legacy_state_root_task_enabled: true,
953 caching_and_prewarming_enabled: true,
954 state_cache_disabled: true,
955 prewarming_disabled: true,
956 txpool_prewarming_enabled: false,
958 sender_recovery_cache_enabled: true,
959 parallel_sparse_trie_enabled: true,
960 parallel_sparse_trie_disabled: false,
961 state_provider_metrics: true,
962 cross_block_cache_size: 256,
963 state_root_task_compare_updates: true,
964 accept_execution_requests_hash: true,
965 multiproof_chunk_size: 512,
966 reserved_cpu_cores: 4,
967 precompile_cache_enabled: true,
968 precompile_cache_disabled: true,
969 state_root_fallback: true,
970 always_process_payload_attributes_on_canonical_head: true,
971 allow_unwind_canonical_header: true,
972 storage_worker_count: Some(16),
973 account_worker_count: Some(8),
974 prewarming_threads: Some(4),
975 cache_metrics_disabled: true,
976 slow_block_threshold: None,
977 disable_sparse_trie_cache_pruning: true,
978 state_root_task_timeout: Some(Duration::from_secs(2)),
979 share_execution_cache_with_payload_builder: false,
980 share_sparse_trie_with_payload_builder: false,
981 suppress_persistence_during_build: false,
982 bal_parallel_execution_disabled: true,
983 bal_parallel_state_root_disabled: true,
984 disable_bal_batch_io: true,
985 #[cfg(feature = "trie-debug")]
986 proof_jitter: None,
987 };
988
989 let parsed_args = CommandParser::<EngineArgs>::parse_from([
990 "reth",
991 "--engine.persistence-threshold",
992 "100",
993 "--engine.persistence-backpressure-threshold",
994 "101",
995 "--engine.memory-block-buffer-target",
996 "50",
997 "--engine.backfill-threshold",
998 "100",
999 "--engine.invalid-header-cache-hit-eviction-threshold",
1000 "7",
1001 "--engine.legacy-state-root",
1002 "--engine.disable-state-cache",
1003 "--engine.disable-prewarming",
1004 "--engine.sender-recovery-cache",
1005 "--engine.state-provider-metrics",
1006 "--engine.cross-block-cache-size",
1007 "256",
1008 "--engine.state-root-task-compare-updates",
1009 "--engine.accept-execution-requests-hash",
1010 "--engine.multiproof-chunk-size",
1011 "512",
1012 "--engine.reserved-cpu-cores",
1013 "4",
1014 "--engine.disable-precompile-cache",
1015 "--engine.state-root-fallback",
1016 "--engine.always-process-payload-attributes-on-canonical-head",
1017 "--engine.allow-unwind-canonical-header",
1018 "--engine.storage-worker-count",
1019 "16",
1020 "--engine.account-worker-count",
1021 "8",
1022 "--engine.prewarming-threads",
1023 "4",
1024 "--engine.disable-cache-metrics",
1025 "--engine.disable-sparse-trie-cache-pruning",
1026 "--engine.state-root-task-timeout",
1027 "2s",
1028 "--engine.disable-bal-parallel-execution",
1029 "--engine.disable-bal-parallel-state-root",
1030 "--engine.disable-bal-batch-io",
1031 ])
1032 .args;
1033
1034 assert_eq!(parsed_args, args);
1035 }
1036
1037 #[test]
1038 fn validate_rejects_invalid_backpressure_threshold() {
1039 let args = EngineArgs {
1040 persistence_threshold: 4,
1041 persistence_backpressure_threshold: Some(4),
1042 ..EngineArgs::default()
1043 };
1044
1045 let err = args.validate().unwrap_err().to_string();
1046 assert!(err.contains("engine.persistence-backpressure-threshold"));
1047 assert!(err.contains("engine.persistence-threshold"));
1048 }
1049
1050 #[test]
1051 fn validate_memory_block_buffer_target() {
1052 let args = EngineArgs {
1053 persistence_threshold: 4,
1054 num_state_masking_blocks: 0,
1055 memory_block_buffer_target: Some(4),
1056 ..EngineArgs::default()
1057 };
1058 args.validate().unwrap();
1059
1060 let args = EngineArgs { memory_block_buffer_target: Some(5), ..args };
1061 let err = args.validate().unwrap_err().to_string();
1062 assert!(err.contains("engine.memory-block-buffer-target"));
1063 assert!(err.contains("engine.persistence-threshold"));
1064 }
1065
1066 #[test]
1067 fn test_parse_num_state_masking_blocks() {
1068 let args = CommandParser::<EngineArgs>::parse_from([
1069 "reth",
1070 "--engine.persistence-threshold",
1071 "13",
1072 "--engine.num-state-masking-blocks",
1073 "7",
1074 ])
1075 .args;
1076
1077 assert_eq!(args.tree_config().num_state_masking_blocks(), 7);
1078 }
1079
1080 #[test]
1081 fn test_parse_backfill_threshold() {
1082 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1083 let config = args.tree_config();
1084 assert_eq!(config.backfill_run_threshold(), DEFAULT_BACKFILL_RUN_THRESHOLD);
1085
1086 for (threshold, expected) in [("500", 500), ("2048", 1024)] {
1087 let args = CommandParser::<EngineArgs>::parse_from([
1088 "reth",
1089 "--engine.backfill-threshold",
1090 threshold,
1091 ])
1092 .args;
1093 assert_eq!(args.tree_config().backfill_run_threshold(), expected);
1094 }
1095 }
1096
1097 #[test]
1098 fn validate_rejects_state_masking_window_at_or_above_threshold() {
1099 let args = EngineArgs {
1100 persistence_threshold: 4,
1101 num_state_masking_blocks: 2,
1102 memory_block_buffer_target: Some(2),
1103 ..EngineArgs::default()
1104 };
1105
1106 let err = args.validate().unwrap_err().to_string();
1107 assert!(err.contains("engine.num-state-masking-blocks"));
1108 }
1109
1110 #[test]
1111 fn validate_rejects_overflowing_state_masking_window() {
1112 let args = EngineArgs {
1113 persistence_threshold: 7,
1114 num_state_masking_blocks: u64::MAX,
1115 ..EngineArgs::default()
1116 };
1117
1118 let err = args.validate().unwrap_err().to_string();
1119 assert!(err.contains("engine.num-state-masking-blocks"));
1120 }
1121
1122 #[test]
1123 fn default_memory_block_buffer_target_is_bounded_by_persistence_threshold() {
1124 let args = CommandParser::<EngineArgs>::parse_from([
1125 "reth",
1126 "--engine.persistence-threshold",
1127 "4",
1128 "--engine.num-state-masking-blocks",
1129 "0",
1130 ])
1131 .args;
1132
1133 assert_eq!(args.memory_block_buffer_target, None);
1134 assert_eq!(args.memory_block_buffer_target(), 4);
1135 assert_eq!(args.tree_config().memory_block_buffer_target(), 4);
1136 args.validate().unwrap();
1137 }
1138
1139 #[test]
1140 fn explicit_persistence_settings_can_be_lower_than_defaults() {
1141 let args = CommandParser::<EngineArgs>::parse_from([
1142 "reth",
1143 "--engine.persistence-threshold",
1144 "0",
1145 "--engine.persistence-backpressure-threshold",
1146 "1",
1147 ])
1148 .args;
1149
1150 args.validate().unwrap();
1151 let config = args.tree_config();
1152 assert_eq!(config.persistence_threshold(), 0);
1153 assert_eq!(config.num_state_masking_blocks(), 0);
1154 assert_eq!(config.memory_block_buffer_target(), 0);
1155 assert_eq!(config.persistence_backpressure_threshold(), 1);
1156 }
1157
1158 #[test]
1159 fn zero_persistence_threshold_disables_explicit_state_masking() {
1160 let args = EngineArgs {
1161 persistence_threshold: 0,
1162 num_state_masking_blocks: u64::MAX,
1163 ..EngineArgs::default()
1164 };
1165 args.validate().unwrap();
1166 assert_eq!(args.tree_config().num_state_masking_blocks(), 0);
1167 }
1168
1169 #[test]
1170 fn parse_rejects_zero_multiproof_chunk_size() {
1171 let result = CommandParser::<EngineArgs>::try_parse_from([
1172 "reth",
1173 "--engine.multiproof-chunk-size",
1174 "0",
1175 ]);
1176
1177 assert!(result.is_err());
1178 }
1179
1180 #[test]
1181 fn validate_rejects_bal_parallel_execution_without_bal_parallel_state_root() {
1182 let args = EngineArgs {
1183 bal_parallel_execution_disabled: false,
1184 bal_parallel_state_root_disabled: true,
1185 ..EngineArgs::default()
1186 };
1187
1188 let err = args.validate().unwrap_err().to_string();
1189 assert!(err.contains("engine.disable-bal-parallel-state-root"));
1190 assert!(err.contains("engine.disable-bal-parallel-execution"));
1191 }
1192
1193 #[test]
1194 fn test_parse_slow_block_threshold() {
1195 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1197 assert_eq!(args.slow_block_threshold, None);
1198
1199 let args =
1201 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
1202 .args;
1203 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
1204
1205 let args = CommandParser::<EngineArgs>::parse_from([
1207 "reth",
1208 "--engine.slow-block-threshold",
1209 "500",
1210 ])
1211 .args;
1212 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
1213
1214 let args = CommandParser::<EngineArgs>::parse_from([
1215 "reth",
1216 "--engine.slow-block-threshold",
1217 "500ms",
1218 ])
1219 .args;
1220 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
1221 }
1222
1223 #[test]
1224 fn test_parse_invalid_header_hit_eviction_threshold() {
1225 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1226 assert_eq!(
1227 args.invalid_header_hit_eviction_threshold,
1228 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1229 );
1230 assert_eq!(
1231 args.tree_config().invalid_header_hit_eviction_threshold(),
1232 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1233 );
1234
1235 let args = CommandParser::<EngineArgs>::parse_from([
1236 "reth",
1237 "--engine.invalid-header-cache-hit-eviction-threshold",
1238 "0",
1239 ])
1240 .args;
1241 assert_eq!(args.invalid_header_hit_eviction_threshold, 0);
1242 assert_eq!(args.tree_config().invalid_header_hit_eviction_threshold(), 0);
1243 }
1244
1245 #[test]
1246 fn test_parse_share_sparse_trie_flag() {
1247 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1248 assert!(!args.share_sparse_trie_with_payload_builder);
1249 assert!(!args.tree_config().share_sparse_trie_with_payload_builder());
1250
1251 let args = CommandParser::<EngineArgs>::parse_from([
1252 "reth",
1253 "--engine.share-sparse-trie-with-payload-builder",
1254 ])
1255 .args;
1256 assert!(args.share_sparse_trie_with_payload_builder);
1257 assert!(args.tree_config().share_sparse_trie_with_payload_builder());
1258 }
1259}