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_INVALID_HEADER_HIT_EVICTION_THRESHOLD, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
11 DEFAULT_NUM_STATE_MASKING_BLOCKS, DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
12};
13use std::{sync::OnceLock, time::Duration};
14
15use crate::node_config::{
16 DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
17 DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
18};
19
20static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
22
23#[derive(Debug, Clone)]
27pub struct DefaultEngineValues {
28 persistence_threshold: u64,
29 persistence_backpressure_threshold: u64,
30 num_state_masking_blocks: u64,
31 memory_block_buffer_target: u64,
32 invalid_header_hit_eviction_threshold: u8,
33 state_cache_disabled: bool,
34 prewarming_disabled: bool,
35 txpool_prewarming_enabled: bool,
36 sender_recovery_cache_enabled: bool,
37 state_provider_metrics: bool,
38 cross_block_cache_size: usize,
39 state_root_task_compare_updates: bool,
40 accept_execution_requests_hash: bool,
41 multiproof_chunk_size: usize,
42 reserved_cpu_cores: usize,
43 precompile_cache_disabled: bool,
44 state_root_fallback: bool,
45 always_process_payload_attributes_on_canonical_head: bool,
46 allow_unwind_canonical_header: bool,
47 storage_worker_count: Option<usize>,
48 account_worker_count: Option<usize>,
49 prewarming_threads: Option<usize>,
50 cache_metrics_disabled: bool,
51 slow_block_threshold: Option<Duration>,
52 disable_sparse_trie_cache_pruning: bool,
53 state_root_task_timeout: Option<String>,
54 share_execution_cache_with_payload_builder: bool,
55 share_sparse_trie_with_payload_builder: bool,
56 suppress_persistence_during_build: bool,
57 bal_parallel_execution_disabled: bool,
58 bal_parallel_state_root_disabled: bool,
59}
60
61impl DefaultEngineValues {
62 pub fn try_init(self) -> Result<(), Self> {
64 ENGINE_DEFAULTS.set(self)
65 }
66
67 pub fn get_global() -> &'static Self {
69 ENGINE_DEFAULTS.get_or_init(Self::default)
70 }
71
72 pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
74 self.persistence_threshold = v;
75 self
76 }
77
78 pub const fn with_persistence_backpressure_threshold(mut self, v: u64) -> Self {
80 self.persistence_backpressure_threshold = v;
81 self
82 }
83
84 pub const fn with_num_state_masking_blocks(mut self, v: u64) -> Self {
86 self.num_state_masking_blocks = v;
87 self
88 }
89
90 pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
92 self.memory_block_buffer_target = v;
93 self
94 }
95
96 pub const fn with_invalid_header_hit_eviction_threshold(mut self, v: u8) -> Self {
98 self.invalid_header_hit_eviction_threshold = v;
99 self
100 }
101
102 pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
104 self.state_cache_disabled = v;
105 self
106 }
107
108 pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
110 self.prewarming_disabled = v;
111 self
112 }
113
114 pub const fn with_txpool_prewarming_enabled(mut self, v: bool) -> Self {
116 self.txpool_prewarming_enabled = v;
117 self
118 }
119
120 pub const fn with_sender_recovery_cache_enabled(mut self, v: bool) -> Self {
122 self.sender_recovery_cache_enabled = v;
123 self
124 }
125
126 pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
128 self.state_provider_metrics = v;
129 self
130 }
131
132 pub const fn with_cross_block_cache_size(mut self, v: usize) -> Self {
134 self.cross_block_cache_size = v;
135 self
136 }
137
138 pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
140 self.state_root_task_compare_updates = v;
141 self
142 }
143
144 pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
146 self.accept_execution_requests_hash = v;
147 self
148 }
149
150 pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
152 self.multiproof_chunk_size = v;
153 self
154 }
155
156 pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
158 self.reserved_cpu_cores = v;
159 self
160 }
161
162 pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
164 self.precompile_cache_disabled = v;
165 self
166 }
167
168 pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
170 self.state_root_fallback = v;
171 self
172 }
173
174 pub const fn with_always_process_payload_attributes_on_canonical_head(
176 mut self,
177 v: bool,
178 ) -> Self {
179 self.always_process_payload_attributes_on_canonical_head = v;
180 self
181 }
182
183 pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
185 self.allow_unwind_canonical_header = v;
186 self
187 }
188
189 pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
191 self.storage_worker_count = v;
192 self
193 }
194
195 pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
197 self.account_worker_count = v;
198 self
199 }
200
201 pub const fn with_prewarming_threads(mut self, v: Option<usize>) -> Self {
203 self.prewarming_threads = v;
204 self
205 }
206
207 pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
209 self.cache_metrics_disabled = v;
210 self
211 }
212
213 pub const fn with_slow_block_threshold(mut self, v: Option<Duration>) -> Self {
215 self.slow_block_threshold = v;
216 self
217 }
218
219 pub const fn with_disable_sparse_trie_cache_pruning(mut self, v: bool) -> Self {
221 self.disable_sparse_trie_cache_pruning = v;
222 self
223 }
224
225 pub fn with_state_root_task_timeout(mut self, v: Option<String>) -> Self {
227 self.state_root_task_timeout = v;
228 self
229 }
230
231 pub const fn with_share_execution_cache_with_payload_builder(mut self, v: bool) -> Self {
233 self.share_execution_cache_with_payload_builder = v;
234 self
235 }
236
237 pub const fn with_share_sparse_trie_with_payload_builder(mut self, v: bool) -> Self {
239 self.share_sparse_trie_with_payload_builder = v;
240 self
241 }
242
243 pub const fn with_suppress_persistence_during_build(mut self, v: bool) -> Self {
245 self.suppress_persistence_during_build = v;
246 self
247 }
248
249 pub const fn with_bal_parallel_execution_disabled(mut self, v: bool) -> Self {
251 self.bal_parallel_execution_disabled = v;
252 self
253 }
254
255 pub const fn with_bal_parallel_state_root_disabled(mut self, v: bool) -> Self {
257 self.bal_parallel_state_root_disabled = v;
258 self
259 }
260}
261
262impl Default for DefaultEngineValues {
263 fn default() -> Self {
264 Self {
265 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
266 persistence_backpressure_threshold: DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
267 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
268 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
269 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
270 state_cache_disabled: false,
271 prewarming_disabled: false,
272 txpool_prewarming_enabled: false,
273 sender_recovery_cache_enabled: false,
274 state_provider_metrics: false,
275 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
276 state_root_task_compare_updates: false,
277 accept_execution_requests_hash: false,
278 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
279 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
280 precompile_cache_disabled: false,
281 state_root_fallback: false,
282 always_process_payload_attributes_on_canonical_head: false,
283 allow_unwind_canonical_header: false,
284 storage_worker_count: None,
285 account_worker_count: None,
286 prewarming_threads: None,
287 cache_metrics_disabled: false,
288 slow_block_threshold: None,
289 disable_sparse_trie_cache_pruning: false,
290 state_root_task_timeout: Some("4s".to_string()),
291 share_execution_cache_with_payload_builder: false,
292 share_sparse_trie_with_payload_builder: false,
293 suppress_persistence_during_build: false,
294 bal_parallel_execution_disabled: false,
295 bal_parallel_state_root_disabled: false,
296 }
297 }
298}
299
300fn default_persistence_backpressure_threshold(persistence_threshold: u64) -> u64 {
301 DefaultEngineValues::get_global()
302 .persistence_backpressure_threshold
303 .max(persistence_threshold.saturating_mul(2))
304}
305
306#[derive(Debug, Clone, Args, PartialEq, Eq)]
308#[command(next_help_heading = "Engine")]
309pub struct EngineArgs {
310 #[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
317 pub persistence_threshold: u64,
318
319 #[arg(long = "engine.persistence-backpressure-threshold")]
327 pub persistence_backpressure_threshold: Option<u64>,
328
329 #[cfg_attr(
332 feature = "partial-persistence",
333 arg(
334 long = "engine.num-state-masking-blocks",
335 default_value_t = DefaultEngineValues::get_global().num_state_masking_blocks
336 )
337 )]
338 #[cfg_attr(
339 not(feature = "partial-persistence"),
340 arg(skip = DefaultEngineValues::get_global().num_state_masking_blocks)
341 )]
342 pub num_state_masking_blocks: u64,
343
344 #[arg(long = "engine.memory-block-buffer-target")]
349 pub memory_block_buffer_target: Option<u64>,
350
351 #[arg(long = "engine.invalid-header-cache-hit-eviction-threshold", default_value_t = DefaultEngineValues::get_global().invalid_header_hit_eviction_threshold)]
357 pub invalid_header_hit_eviction_threshold: u8,
358
359 #[arg(long = "engine.legacy-state-root", default_value_t = false, hide = true)]
362 #[deprecated]
363 pub legacy_state_root_task_enabled: bool,
364
365 #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
368 #[deprecated]
369 pub caching_and_prewarming_enabled: bool,
370
371 #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
373 pub state_cache_disabled: bool,
374
375 #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
377 pub prewarming_disabled: bool,
378
379 #[arg(
381 long = "engine.txpool-prewarming",
382 env = "RETH_ENGINE_TXPOOL_PREWARMING",
383 default_value_t = DefaultEngineValues::get_global().txpool_prewarming_enabled
384 )]
385 pub txpool_prewarming_enabled: bool,
386
387 #[arg(
390 long = "engine.sender-recovery-cache",
391 env = "RETH_ENGINE_SENDER_RECOVERY_CACHE",
392 default_value_t = DefaultEngineValues::get_global().sender_recovery_cache_enabled
393 )]
394 pub sender_recovery_cache_enabled: bool,
395
396 #[deprecated]
398 #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
399 pub parallel_sparse_trie_enabled: bool,
400
401 #[deprecated]
403 #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
404 pub parallel_sparse_trie_disabled: bool,
405
406 #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
410 pub state_provider_metrics: bool,
411
412 #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
414 pub cross_block_cache_size: usize,
415
416 #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
419 pub state_root_task_compare_updates: bool,
420
421 #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
423 pub accept_execution_requests_hash: bool,
424
425 #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size, value_parser = RangedU64ValueParser::<usize>::new().range(1..))]
427 pub multiproof_chunk_size: usize,
428
429 #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
431 pub reserved_cpu_cores: usize,
432
433 #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
436 #[deprecated]
437 pub precompile_cache_enabled: bool,
438
439 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
441 pub precompile_cache_disabled: bool,
442
443 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
445 pub state_root_fallback: bool,
446
447 #[arg(
453 long = "engine.always-process-payload-attributes-on-canonical-head",
454 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
455 )]
456 pub always_process_payload_attributes_on_canonical_head: bool,
457
458 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
461 pub allow_unwind_canonical_header: bool,
462
463 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
466 pub storage_worker_count: Option<usize>,
467
468 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
471 pub account_worker_count: Option<usize>,
472
473 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
476 pub prewarming_threads: Option<usize>,
477
478 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
480 pub cache_metrics_disabled: bool,
481
482 #[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())))]
491 pub slow_block_threshold: Option<Duration>,
492
493 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
497 pub disable_sparse_trie_cache_pruning: bool,
498
499 #[arg(
508 long = "engine.state-root-task-timeout",
509 value_parser = humantime::parse_duration,
510 default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("4s"),
511 )]
512 pub state_root_task_timeout: Option<Duration>,
513
514 #[arg(
522 long = "engine.share-execution-cache-with-payload-builder",
523 default_value_t = DefaultEngineValues::get_global().share_execution_cache_with_payload_builder,
524 )]
525 pub share_execution_cache_with_payload_builder: bool,
526
527 #[arg(
539 long = "engine.share-sparse-trie-with-payload-builder",
540 default_value_t = DefaultEngineValues::get_global().share_sparse_trie_with_payload_builder,
541 )]
542 pub share_sparse_trie_with_payload_builder: bool,
543
544 #[arg(
550 long = "engine.suppress-persistence-during-build",
551 default_value_t = DefaultEngineValues::get_global().suppress_persistence_during_build,
552 )]
553 pub suppress_persistence_during_build: bool,
554
555 #[arg(long = "engine.disable-bal-parallel-execution", default_value_t = DefaultEngineValues::get_global().bal_parallel_execution_disabled)]
557 pub bal_parallel_execution_disabled: bool,
558
559 #[arg(long = "engine.disable-bal-parallel-state-root", default_value_t = DefaultEngineValues::get_global().bal_parallel_state_root_disabled)]
562 pub bal_parallel_state_root_disabled: bool,
563
564 #[arg(long = "engine.disable-bal-batch-io", default_value_t = false)]
567 pub disable_bal_batch_io: bool,
568
569 #[cfg(feature = "trie-debug")]
576 #[arg(
577 long = "engine.proof-jitter",
578 value_parser = humantime::parse_duration,
579 )]
580 pub proof_jitter: Option<Duration>,
581}
582
583#[allow(deprecated)]
584impl Default for EngineArgs {
585 fn default() -> Self {
586 let DefaultEngineValues {
587 persistence_threshold,
588 persistence_backpressure_threshold: _,
589 num_state_masking_blocks,
590 memory_block_buffer_target: _,
591 invalid_header_hit_eviction_threshold,
592 state_cache_disabled,
593 prewarming_disabled,
594 txpool_prewarming_enabled,
595 sender_recovery_cache_enabled,
596 state_provider_metrics,
597 cross_block_cache_size,
598 state_root_task_compare_updates,
599 accept_execution_requests_hash,
600 multiproof_chunk_size,
601 reserved_cpu_cores,
602 precompile_cache_disabled,
603 state_root_fallback,
604 always_process_payload_attributes_on_canonical_head,
605 allow_unwind_canonical_header,
606 storage_worker_count,
607 account_worker_count,
608 prewarming_threads,
609 cache_metrics_disabled,
610 slow_block_threshold,
611 disable_sparse_trie_cache_pruning,
612 state_root_task_timeout,
613 share_execution_cache_with_payload_builder,
614 share_sparse_trie_with_payload_builder,
615 suppress_persistence_during_build,
616 bal_parallel_execution_disabled,
617 bal_parallel_state_root_disabled,
618 } = DefaultEngineValues::get_global().clone();
619 Self {
620 persistence_threshold,
621 persistence_backpressure_threshold: None,
622 num_state_masking_blocks,
623 memory_block_buffer_target: None,
624 invalid_header_hit_eviction_threshold,
625 state_root_task_compare_updates,
626 legacy_state_root_task_enabled: false,
627 caching_and_prewarming_enabled: true,
628 state_cache_disabled,
629 prewarming_disabled,
630 txpool_prewarming_enabled,
631 sender_recovery_cache_enabled,
632 parallel_sparse_trie_enabled: true,
633 parallel_sparse_trie_disabled: false,
634 state_provider_metrics,
635 cross_block_cache_size,
636 accept_execution_requests_hash,
637 multiproof_chunk_size,
638 reserved_cpu_cores,
639 precompile_cache_enabled: true,
640 precompile_cache_disabled,
641 state_root_fallback,
642 always_process_payload_attributes_on_canonical_head,
643 allow_unwind_canonical_header,
644 storage_worker_count,
645 account_worker_count,
646 prewarming_threads,
647 cache_metrics_disabled,
648 slow_block_threshold,
649 disable_sparse_trie_cache_pruning,
650 state_root_task_timeout: state_root_task_timeout
651 .as_deref()
652 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
653 share_execution_cache_with_payload_builder,
654 share_sparse_trie_with_payload_builder,
655 suppress_persistence_during_build,
656 bal_parallel_execution_disabled,
657 bal_parallel_state_root_disabled,
658 disable_bal_batch_io: false,
659 #[cfg(feature = "trie-debug")]
660 proof_jitter: None,
661 }
662 }
663}
664
665impl EngineArgs {
666 pub fn memory_block_buffer_target(&self) -> u64 {
668 self.memory_block_buffer_target.unwrap_or_else(|| {
669 self.persistence_threshold
670 .min(DefaultEngineValues::get_global().memory_block_buffer_target)
671 })
672 }
673
674 pub fn persistence_backpressure_threshold(&self) -> u64 {
676 self.persistence_backpressure_threshold.unwrap_or_else(|| {
677 default_persistence_backpressure_threshold(self.persistence_threshold)
678 })
679 }
680
681 pub fn validate(&self) -> eyre::Result<()> {
683 let persistence_backpressure_threshold = self.persistence_backpressure_threshold();
684 let memory_block_buffer_target = self.memory_block_buffer_target();
685 ensure!(
686 persistence_backpressure_threshold > self.persistence_threshold,
687 "--engine.persistence-backpressure-threshold ({}) must be greater than --engine.persistence-threshold ({})",
688 persistence_backpressure_threshold,
689 self.persistence_threshold
690 );
691 ensure!(
692 memory_block_buffer_target <= self.persistence_threshold,
693 "--engine.memory-block-buffer-target ({}) must be less than or equal to --engine.persistence-threshold ({})",
694 memory_block_buffer_target,
695 self.persistence_threshold,
696 );
697 ensure!(
698 self.num_state_masking_blocks == 0 ||
699 matches!(
700 self.num_state_masking_blocks.checked_add(memory_block_buffer_target),
701 Some(window) if window < self.persistence_threshold
702 ),
703 "--engine.num-state-masking-blocks ({}) + --engine.memory-block-buffer-target ({}) must be less than --engine.persistence-threshold ({})",
704 self.num_state_masking_blocks,
705 memory_block_buffer_target,
706 self.persistence_threshold,
707 );
708 ensure!(
709 !self.state_cache_disabled || !self.txpool_prewarming_enabled,
710 "--engine.txpool-prewarming conflicts with --engine.disable-state-cache"
711 );
712 ensure!(
713 self.bal_parallel_execution_disabled || !self.bal_parallel_state_root_disabled,
714 "--engine.disable-bal-parallel-state-root requires --engine.disable-bal-parallel-execution because BAL parallel execution depends on BAL prewarm state-root updates"
715 );
716 Ok(())
717 }
718
719 pub fn tree_config(&self) -> TreeConfig {
721 #[allow(deprecated)]
722 if self.legacy_state_root_task_enabled {
723 tracing::warn!(target: "reth::cli", "--engine.legacy-state-root has no effect anymore, use --engine.state-root-fallback to force synchronous state root computation");
724 }
725 let config = TreeConfig::default()
726 .with_persistence_backpressure_threshold(self.persistence_backpressure_threshold())
727 .with_persistence_threshold(self.persistence_threshold)
728 .with_memory_block_buffer_target(self.memory_block_buffer_target())
729 .with_num_state_masking_blocks(self.num_state_masking_blocks)
730 .with_invalid_header_hit_eviction_threshold(self.invalid_header_hit_eviction_threshold)
731 .without_state_cache(self.state_cache_disabled)
732 .without_prewarming(self.prewarming_disabled)
733 .with_txpool_prewarming(self.txpool_prewarming_enabled)
734 .with_state_provider_metrics(self.state_provider_metrics)
735 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
736 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
737 .with_multiproof_chunk_size(self.multiproof_chunk_size)
738 .with_reserved_cpu_cores(self.reserved_cpu_cores)
739 .without_precompile_cache(self.precompile_cache_disabled)
740 .with_state_root_fallback(self.state_root_fallback)
741 .with_always_process_payload_attributes_on_canonical_head(
742 self.always_process_payload_attributes_on_canonical_head,
743 )
744 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
745 .without_cache_metrics(self.cache_metrics_disabled)
746 .with_slow_block_threshold(self.slow_block_threshold)
747 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
748 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
749 .with_share_execution_cache_with_payload_builder(
750 self.share_execution_cache_with_payload_builder,
751 )
752 .with_share_sparse_trie_with_payload_builder(
753 self.share_sparse_trie_with_payload_builder,
754 )
755 .with_suppress_persistence_during_build(self.suppress_persistence_during_build)
756 .without_bal_parallel_execution(self.bal_parallel_execution_disabled)
757 .without_bal_parallel_state_root(self.bal_parallel_state_root_disabled)
758 .without_bal_batch_io(self.disable_bal_batch_io);
759 #[cfg(feature = "trie-debug")]
760 let config = config.with_proof_jitter(self.proof_jitter);
761 config
762 }
763}
764
765#[cfg(test)]
766mod tests {
767 use super::*;
768 use clap::Parser;
769
770 #[derive(Parser)]
772 struct CommandParser<T: Args> {
773 #[command(flatten)]
774 args: T,
775 }
776
777 #[test]
778 fn test_parse_engine_args() {
779 let default_args = EngineArgs::default();
780 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
781 assert_eq!(args, default_args);
782 assert_eq!(args.persistence_threshold, 7);
783 assert_eq!(args.memory_block_buffer_target, None);
784 assert_eq!(args.memory_block_buffer_target(), 5);
785 assert_eq!(
786 args.persistence_backpressure_threshold(),
787 DefaultEngineValues::get_global().persistence_backpressure_threshold
788 );
789 }
790
791 #[test]
792 fn txpool_prewarming_is_disabled_by_default_and_can_be_enabled() {
793 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
794 assert!(!args.txpool_prewarming_enabled);
795 assert!(!args.tree_config().txpool_prewarming());
796
797 let args =
798 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.txpool-prewarming"]).args;
799 assert!(args.txpool_prewarming_enabled);
800 assert!(args.tree_config().txpool_prewarming());
801 }
802
803 #[test]
804 fn validate_rejects_txpool_prewarming_with_disabled_state_cache() {
805 let args = EngineArgs {
806 state_cache_disabled: true,
807 txpool_prewarming_enabled: true,
808 ..EngineArgs::default()
809 };
810
811 let err = args.validate().unwrap_err().to_string();
812 assert!(err.contains("engine.txpool-prewarming"));
813 assert!(err.contains("engine.disable-state-cache"));
814 }
815
816 #[test]
817 fn default_backpressure_threshold_uses_parsed_persistence_args() {
818 let args = CommandParser::<EngineArgs>::parse_from([
819 "reth",
820 "--engine.persistence-threshold",
821 "100",
822 "--engine.memory-block-buffer-target",
823 "50",
824 ])
825 .args;
826
827 assert_eq!(args.persistence_backpressure_threshold(), 200);
828
829 let tree_config = args.tree_config();
830 assert_eq!(tree_config.persistence_threshold(), 100);
831 assert_eq!(tree_config.memory_block_buffer_target(), 50);
832 assert_eq!(tree_config.persistence_backpressure_threshold(), 200);
833 }
834
835 #[test]
836 fn default_backpressure_threshold_uses_global_default_when_larger() {
837 let args = CommandParser::<EngineArgs>::parse_from([
838 "reth",
839 "--engine.persistence-threshold",
840 "4",
841 ])
842 .args;
843
844 assert_eq!(
845 args.persistence_backpressure_threshold(),
846 DefaultEngineValues::get_global().persistence_backpressure_threshold
847 );
848 }
849
850 #[test]
851 fn explicit_backpressure_threshold_overrides_calculated_default() {
852 let args = CommandParser::<EngineArgs>::parse_from([
853 "reth",
854 "--engine.persistence-threshold",
855 "100",
856 "--engine.memory-block-buffer-target",
857 "50",
858 "--engine.persistence-backpressure-threshold",
859 "101",
860 ])
861 .args;
862
863 assert_eq!(args.persistence_backpressure_threshold(), 101);
864 }
865
866 #[test]
867 fn sender_recovery_cache_is_disabled_by_default_and_can_be_enabled() {
868 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
869 assert!(!args.sender_recovery_cache_enabled);
870
871 let args =
872 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.sender-recovery-cache"])
873 .args;
874 assert!(args.sender_recovery_cache_enabled);
875 }
876
877 #[test]
878 #[allow(deprecated)]
879 fn engine_args() {
880 let args = EngineArgs {
881 persistence_threshold: 100,
882 persistence_backpressure_threshold: Some(101),
883 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
884 memory_block_buffer_target: Some(50),
885 invalid_header_hit_eviction_threshold: 7,
886 legacy_state_root_task_enabled: true,
887 caching_and_prewarming_enabled: true,
888 state_cache_disabled: true,
889 prewarming_disabled: true,
890 txpool_prewarming_enabled: false,
892 sender_recovery_cache_enabled: true,
893 parallel_sparse_trie_enabled: true,
894 parallel_sparse_trie_disabled: false,
895 state_provider_metrics: true,
896 cross_block_cache_size: 256,
897 state_root_task_compare_updates: true,
898 accept_execution_requests_hash: true,
899 multiproof_chunk_size: 512,
900 reserved_cpu_cores: 4,
901 precompile_cache_enabled: true,
902 precompile_cache_disabled: true,
903 state_root_fallback: true,
904 always_process_payload_attributes_on_canonical_head: true,
905 allow_unwind_canonical_header: true,
906 storage_worker_count: Some(16),
907 account_worker_count: Some(8),
908 prewarming_threads: Some(4),
909 cache_metrics_disabled: true,
910 slow_block_threshold: None,
911 disable_sparse_trie_cache_pruning: true,
912 state_root_task_timeout: Some(Duration::from_secs(2)),
913 share_execution_cache_with_payload_builder: false,
914 share_sparse_trie_with_payload_builder: false,
915 suppress_persistence_during_build: false,
916 bal_parallel_execution_disabled: true,
917 bal_parallel_state_root_disabled: true,
918 disable_bal_batch_io: true,
919 #[cfg(feature = "trie-debug")]
920 proof_jitter: None,
921 };
922
923 let parsed_args = CommandParser::<EngineArgs>::parse_from([
924 "reth",
925 "--engine.persistence-threshold",
926 "100",
927 "--engine.persistence-backpressure-threshold",
928 "101",
929 "--engine.memory-block-buffer-target",
930 "50",
931 "--engine.invalid-header-cache-hit-eviction-threshold",
932 "7",
933 "--engine.legacy-state-root",
934 "--engine.disable-state-cache",
935 "--engine.disable-prewarming",
936 "--engine.sender-recovery-cache",
937 "--engine.state-provider-metrics",
938 "--engine.cross-block-cache-size",
939 "256",
940 "--engine.state-root-task-compare-updates",
941 "--engine.accept-execution-requests-hash",
942 "--engine.multiproof-chunk-size",
943 "512",
944 "--engine.reserved-cpu-cores",
945 "4",
946 "--engine.disable-precompile-cache",
947 "--engine.state-root-fallback",
948 "--engine.always-process-payload-attributes-on-canonical-head",
949 "--engine.allow-unwind-canonical-header",
950 "--engine.storage-worker-count",
951 "16",
952 "--engine.account-worker-count",
953 "8",
954 "--engine.prewarming-threads",
955 "4",
956 "--engine.disable-cache-metrics",
957 "--engine.disable-sparse-trie-cache-pruning",
958 "--engine.state-root-task-timeout",
959 "2s",
960 "--engine.disable-bal-parallel-execution",
961 "--engine.disable-bal-parallel-state-root",
962 "--engine.disable-bal-batch-io",
963 ])
964 .args;
965
966 assert_eq!(parsed_args, args);
967 }
968
969 #[test]
970 fn validate_rejects_invalid_backpressure_threshold() {
971 let args = EngineArgs {
972 persistence_threshold: 4,
973 persistence_backpressure_threshold: Some(4),
974 ..EngineArgs::default()
975 };
976
977 let err = args.validate().unwrap_err().to_string();
978 assert!(err.contains("engine.persistence-backpressure-threshold"));
979 assert!(err.contains("engine.persistence-threshold"));
980 }
981
982 #[test]
983 fn validate_memory_block_buffer_target() {
984 let args = EngineArgs {
985 persistence_threshold: 4,
986 memory_block_buffer_target: Some(4),
987 ..EngineArgs::default()
988 };
989 args.validate().unwrap();
990
991 let args = EngineArgs { memory_block_buffer_target: Some(5), ..args };
992 let err = args.validate().unwrap_err().to_string();
993 assert!(err.contains("engine.memory-block-buffer-target"));
994 assert!(err.contains("engine.persistence-threshold"));
995 }
996
997 #[cfg(feature = "partial-persistence")]
998 #[test]
999 fn test_parse_num_state_masking_blocks() {
1000 let args = CommandParser::<EngineArgs>::parse_from([
1001 "reth",
1002 "--engine.persistence-threshold",
1003 "13",
1004 "--engine.num-state-masking-blocks",
1005 "7",
1006 ])
1007 .args;
1008
1009 assert_eq!(args.tree_config().num_state_masking_blocks(), 7);
1010 }
1011
1012 #[cfg(not(feature = "partial-persistence"))]
1013 #[test]
1014 fn num_state_masking_blocks_is_hidden_without_partial_persistence() {
1015 assert!(CommandParser::<EngineArgs>::try_parse_from([
1016 "reth",
1017 "--engine.num-state-masking-blocks",
1018 "1",
1019 ])
1020 .is_err());
1021 }
1022
1023 #[test]
1024 fn validate_rejects_state_masking_window_at_or_above_threshold() {
1025 let args = EngineArgs {
1026 persistence_threshold: 4,
1027 num_state_masking_blocks: 2,
1028 memory_block_buffer_target: Some(2),
1029 ..EngineArgs::default()
1030 };
1031
1032 let err = args.validate().unwrap_err().to_string();
1033 assert!(err.contains("engine.num-state-masking-blocks"));
1034 }
1035
1036 #[test]
1037 fn validate_rejects_overflowing_state_masking_window() {
1038 let args = EngineArgs {
1039 persistence_threshold: 7,
1040 num_state_masking_blocks: u64::MAX,
1041 ..EngineArgs::default()
1042 };
1043
1044 let err = args.validate().unwrap_err().to_string();
1045 assert!(err.contains("engine.num-state-masking-blocks"));
1046 }
1047
1048 #[test]
1049 fn default_memory_block_buffer_target_is_bounded_by_persistence_threshold() {
1050 let args = CommandParser::<EngineArgs>::parse_from([
1051 "reth",
1052 "--engine.persistence-threshold",
1053 "4",
1054 ])
1055 .args;
1056
1057 assert_eq!(args.memory_block_buffer_target, None);
1058 assert_eq!(args.memory_block_buffer_target(), 4);
1059 assert_eq!(args.tree_config().memory_block_buffer_target(), 4);
1060 args.validate().unwrap();
1061 }
1062
1063 #[test]
1064 fn parse_rejects_zero_multiproof_chunk_size() {
1065 let result = CommandParser::<EngineArgs>::try_parse_from([
1066 "reth",
1067 "--engine.multiproof-chunk-size",
1068 "0",
1069 ]);
1070
1071 assert!(result.is_err());
1072 }
1073
1074 #[test]
1075 fn validate_rejects_bal_parallel_execution_without_bal_parallel_state_root() {
1076 let args = EngineArgs {
1077 bal_parallel_execution_disabled: false,
1078 bal_parallel_state_root_disabled: true,
1079 ..EngineArgs::default()
1080 };
1081
1082 let err = args.validate().unwrap_err().to_string();
1083 assert!(err.contains("engine.disable-bal-parallel-state-root"));
1084 assert!(err.contains("engine.disable-bal-parallel-execution"));
1085 }
1086
1087 #[test]
1088 fn test_parse_slow_block_threshold() {
1089 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1091 assert_eq!(args.slow_block_threshold, None);
1092
1093 let args =
1095 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
1096 .args;
1097 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
1098
1099 let args = CommandParser::<EngineArgs>::parse_from([
1101 "reth",
1102 "--engine.slow-block-threshold",
1103 "500",
1104 ])
1105 .args;
1106 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
1107
1108 let args = CommandParser::<EngineArgs>::parse_from([
1109 "reth",
1110 "--engine.slow-block-threshold",
1111 "500ms",
1112 ])
1113 .args;
1114 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
1115 }
1116
1117 #[test]
1118 fn test_parse_invalid_header_hit_eviction_threshold() {
1119 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1120 assert_eq!(
1121 args.invalid_header_hit_eviction_threshold,
1122 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1123 );
1124 assert_eq!(
1125 args.tree_config().invalid_header_hit_eviction_threshold(),
1126 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1127 );
1128
1129 let args = CommandParser::<EngineArgs>::parse_from([
1130 "reth",
1131 "--engine.invalid-header-cache-hit-eviction-threshold",
1132 "0",
1133 ])
1134 .args;
1135 assert_eq!(args.invalid_header_hit_eviction_threshold, 0);
1136 assert_eq!(args.tree_config().invalid_header_hit_eviction_threshold(), 0);
1137 }
1138
1139 #[test]
1140 fn test_parse_share_sparse_trie_flag() {
1141 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1142 assert!(!args.share_sparse_trie_with_payload_builder);
1143 assert!(!args.tree_config().share_sparse_trie_with_payload_builder());
1144
1145 let args = CommandParser::<EngineArgs>::parse_from([
1146 "reth",
1147 "--engine.share-sparse-trie-with-payload-builder",
1148 ])
1149 .args;
1150 assert!(args.share_sparse_trie_with_payload_builder);
1151 assert!(args.tree_config().share_sparse_trie_with_payload_builder());
1152 }
1153}