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(
317 long = "engine.persistence-threshold",
318 env = "RETH_ENGINE_PERSISTENCE_THRESHOLD",
319 default_value_t = DefaultEngineValues::get_global().persistence_threshold
320 )]
321 pub persistence_threshold: u64,
322
323 #[arg(long = "engine.persistence-backpressure-threshold")]
331 pub persistence_backpressure_threshold: Option<u64>,
332
333 #[arg(
336 long = "engine.num-state-masking-blocks",
337 env = "RETH_ENGINE_NUM_STATE_MASKING_BLOCKS",
338 default_value_t = DefaultEngineValues::get_global().num_state_masking_blocks
339 )]
340 pub num_state_masking_blocks: u64,
341
342 #[arg(long = "engine.memory-block-buffer-target")]
347 pub memory_block_buffer_target: Option<u64>,
348
349 #[arg(long = "engine.invalid-header-cache-hit-eviction-threshold", default_value_t = DefaultEngineValues::get_global().invalid_header_hit_eviction_threshold)]
355 pub invalid_header_hit_eviction_threshold: u8,
356
357 #[arg(long = "engine.legacy-state-root", default_value_t = false, hide = true)]
360 #[deprecated]
361 pub legacy_state_root_task_enabled: bool,
362
363 #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
366 #[deprecated]
367 pub caching_and_prewarming_enabled: bool,
368
369 #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
371 pub state_cache_disabled: bool,
372
373 #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
375 pub prewarming_disabled: bool,
376
377 #[arg(
379 long = "engine.txpool-prewarming",
380 env = "RETH_ENGINE_TXPOOL_PREWARMING",
381 default_value_t = DefaultEngineValues::get_global().txpool_prewarming_enabled
382 )]
383 pub txpool_prewarming_enabled: bool,
384
385 #[arg(
388 long = "engine.sender-recovery-cache",
389 env = "RETH_ENGINE_SENDER_RECOVERY_CACHE",
390 default_value_t = DefaultEngineValues::get_global().sender_recovery_cache_enabled
391 )]
392 pub sender_recovery_cache_enabled: bool,
393
394 #[deprecated]
396 #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
397 pub parallel_sparse_trie_enabled: bool,
398
399 #[deprecated]
401 #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
402 pub parallel_sparse_trie_disabled: bool,
403
404 #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
408 pub state_provider_metrics: bool,
409
410 #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
412 pub cross_block_cache_size: usize,
413
414 #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
417 pub state_root_task_compare_updates: bool,
418
419 #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
421 pub accept_execution_requests_hash: bool,
422
423 #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size, value_parser = RangedU64ValueParser::<usize>::new().range(1..))]
425 pub multiproof_chunk_size: usize,
426
427 #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
429 pub reserved_cpu_cores: usize,
430
431 #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
434 #[deprecated]
435 pub precompile_cache_enabled: bool,
436
437 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
439 pub precompile_cache_disabled: bool,
440
441 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
443 pub state_root_fallback: bool,
444
445 #[arg(
451 long = "engine.always-process-payload-attributes-on-canonical-head",
452 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
453 )]
454 pub always_process_payload_attributes_on_canonical_head: bool,
455
456 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
459 pub allow_unwind_canonical_header: bool,
460
461 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
464 pub storage_worker_count: Option<usize>,
465
466 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
469 pub account_worker_count: Option<usize>,
470
471 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
474 pub prewarming_threads: Option<usize>,
475
476 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
478 pub cache_metrics_disabled: bool,
479
480 #[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())))]
489 pub slow_block_threshold: Option<Duration>,
490
491 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
495 pub disable_sparse_trie_cache_pruning: bool,
496
497 #[arg(
506 long = "engine.state-root-task-timeout",
507 value_parser = humantime::parse_duration,
508 default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("4s"),
509 )]
510 pub state_root_task_timeout: Option<Duration>,
511
512 #[arg(
520 long = "engine.share-execution-cache-with-payload-builder",
521 default_value_t = DefaultEngineValues::get_global().share_execution_cache_with_payload_builder,
522 )]
523 pub share_execution_cache_with_payload_builder: bool,
524
525 #[arg(
537 long = "engine.share-sparse-trie-with-payload-builder",
538 default_value_t = DefaultEngineValues::get_global().share_sparse_trie_with_payload_builder,
539 )]
540 pub share_sparse_trie_with_payload_builder: bool,
541
542 #[arg(
548 long = "engine.suppress-persistence-during-build",
549 default_value_t = DefaultEngineValues::get_global().suppress_persistence_during_build,
550 )]
551 pub suppress_persistence_during_build: bool,
552
553 #[arg(long = "engine.disable-bal-parallel-execution", default_value_t = DefaultEngineValues::get_global().bal_parallel_execution_disabled)]
555 pub bal_parallel_execution_disabled: bool,
556
557 #[arg(long = "engine.disable-bal-parallel-state-root", default_value_t = DefaultEngineValues::get_global().bal_parallel_state_root_disabled)]
560 pub bal_parallel_state_root_disabled: bool,
561
562 #[arg(long = "engine.disable-bal-batch-io", default_value_t = false)]
565 pub disable_bal_batch_io: bool,
566
567 #[cfg(feature = "trie-debug")]
574 #[arg(
575 long = "engine.proof-jitter",
576 value_parser = humantime::parse_duration,
577 )]
578 pub proof_jitter: Option<Duration>,
579}
580
581#[allow(deprecated)]
582impl Default for EngineArgs {
583 fn default() -> Self {
584 let DefaultEngineValues {
585 persistence_threshold,
586 persistence_backpressure_threshold: _,
587 num_state_masking_blocks,
588 memory_block_buffer_target: _,
589 invalid_header_hit_eviction_threshold,
590 state_cache_disabled,
591 prewarming_disabled,
592 txpool_prewarming_enabled,
593 sender_recovery_cache_enabled,
594 state_provider_metrics,
595 cross_block_cache_size,
596 state_root_task_compare_updates,
597 accept_execution_requests_hash,
598 multiproof_chunk_size,
599 reserved_cpu_cores,
600 precompile_cache_disabled,
601 state_root_fallback,
602 always_process_payload_attributes_on_canonical_head,
603 allow_unwind_canonical_header,
604 storage_worker_count,
605 account_worker_count,
606 prewarming_threads,
607 cache_metrics_disabled,
608 slow_block_threshold,
609 disable_sparse_trie_cache_pruning,
610 state_root_task_timeout,
611 share_execution_cache_with_payload_builder,
612 share_sparse_trie_with_payload_builder,
613 suppress_persistence_during_build,
614 bal_parallel_execution_disabled,
615 bal_parallel_state_root_disabled,
616 } = DefaultEngineValues::get_global().clone();
617 Self {
618 persistence_threshold,
619 persistence_backpressure_threshold: None,
620 num_state_masking_blocks,
621 memory_block_buffer_target: None,
622 invalid_header_hit_eviction_threshold,
623 state_root_task_compare_updates,
624 legacy_state_root_task_enabled: false,
625 caching_and_prewarming_enabled: true,
626 state_cache_disabled,
627 prewarming_disabled,
628 txpool_prewarming_enabled,
629 sender_recovery_cache_enabled,
630 parallel_sparse_trie_enabled: true,
631 parallel_sparse_trie_disabled: false,
632 state_provider_metrics,
633 cross_block_cache_size,
634 accept_execution_requests_hash,
635 multiproof_chunk_size,
636 reserved_cpu_cores,
637 precompile_cache_enabled: true,
638 precompile_cache_disabled,
639 state_root_fallback,
640 always_process_payload_attributes_on_canonical_head,
641 allow_unwind_canonical_header,
642 storage_worker_count,
643 account_worker_count,
644 prewarming_threads,
645 cache_metrics_disabled,
646 slow_block_threshold,
647 disable_sparse_trie_cache_pruning,
648 state_root_task_timeout: state_root_task_timeout
649 .as_deref()
650 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
651 share_execution_cache_with_payload_builder,
652 share_sparse_trie_with_payload_builder,
653 suppress_persistence_during_build,
654 bal_parallel_execution_disabled,
655 bal_parallel_state_root_disabled,
656 disable_bal_batch_io: false,
657 #[cfg(feature = "trie-debug")]
658 proof_jitter: None,
659 }
660 }
661}
662
663impl EngineArgs {
664 pub fn memory_block_buffer_target(&self) -> u64 {
666 self.memory_block_buffer_target.unwrap_or_else(|| {
667 self.persistence_threshold
668 .min(DefaultEngineValues::get_global().memory_block_buffer_target)
669 })
670 }
671
672 pub fn persistence_backpressure_threshold(&self) -> u64 {
674 self.persistence_backpressure_threshold.unwrap_or_else(|| {
675 default_persistence_backpressure_threshold(self.persistence_threshold)
676 })
677 }
678
679 pub fn validate(&self) -> eyre::Result<()> {
681 let persistence_backpressure_threshold = self.persistence_backpressure_threshold();
682 let memory_block_buffer_target = self.memory_block_buffer_target();
683 ensure!(
684 persistence_backpressure_threshold > self.persistence_threshold,
685 "--engine.persistence-backpressure-threshold ({}) must be greater than --engine.persistence-threshold ({})",
686 persistence_backpressure_threshold,
687 self.persistence_threshold
688 );
689 ensure!(
690 memory_block_buffer_target <= self.persistence_threshold,
691 "--engine.memory-block-buffer-target ({}) must be less than or equal to --engine.persistence-threshold ({})",
692 memory_block_buffer_target,
693 self.persistence_threshold,
694 );
695 ensure!(
696 self.num_state_masking_blocks == 0 ||
697 matches!(
698 self.num_state_masking_blocks.checked_add(memory_block_buffer_target),
699 Some(window) if window < self.persistence_threshold
700 ),
701 "--engine.num-state-masking-blocks ({}) + --engine.memory-block-buffer-target ({}) must be less than --engine.persistence-threshold ({})",
702 self.num_state_masking_blocks,
703 memory_block_buffer_target,
704 self.persistence_threshold,
705 );
706 ensure!(
707 !self.state_cache_disabled || !self.txpool_prewarming_enabled,
708 "--engine.txpool-prewarming conflicts with --engine.disable-state-cache"
709 );
710 ensure!(
711 self.bal_parallel_execution_disabled || !self.bal_parallel_state_root_disabled,
712 "--engine.disable-bal-parallel-state-root requires --engine.disable-bal-parallel-execution because BAL parallel execution depends on BAL prewarm state-root updates"
713 );
714 Ok(())
715 }
716
717 pub fn tree_config(&self) -> TreeConfig {
719 #[allow(deprecated)]
720 if self.legacy_state_root_task_enabled {
721 tracing::warn!(target: "reth::cli", "--engine.legacy-state-root has no effect anymore, use --engine.state-root-fallback to force synchronous state root computation");
722 }
723 let config = TreeConfig::default()
724 .with_persistence_backpressure_threshold(self.persistence_backpressure_threshold())
725 .with_persistence_threshold(self.persistence_threshold)
726 .with_memory_block_buffer_target(self.memory_block_buffer_target())
727 .with_num_state_masking_blocks(self.num_state_masking_blocks)
728 .with_invalid_header_hit_eviction_threshold(self.invalid_header_hit_eviction_threshold)
729 .without_state_cache(self.state_cache_disabled)
730 .without_prewarming(self.prewarming_disabled)
731 .with_txpool_prewarming(self.txpool_prewarming_enabled)
732 .with_state_provider_metrics(self.state_provider_metrics)
733 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
734 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
735 .with_multiproof_chunk_size(self.multiproof_chunk_size)
736 .with_reserved_cpu_cores(self.reserved_cpu_cores)
737 .without_precompile_cache(self.precompile_cache_disabled)
738 .with_state_root_fallback(self.state_root_fallback)
739 .with_always_process_payload_attributes_on_canonical_head(
740 self.always_process_payload_attributes_on_canonical_head,
741 )
742 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
743 .without_cache_metrics(self.cache_metrics_disabled)
744 .with_slow_block_threshold(self.slow_block_threshold)
745 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
746 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
747 .with_share_execution_cache_with_payload_builder(
748 self.share_execution_cache_with_payload_builder,
749 )
750 .with_share_sparse_trie_with_payload_builder(
751 self.share_sparse_trie_with_payload_builder,
752 )
753 .with_suppress_persistence_during_build(self.suppress_persistence_during_build)
754 .without_bal_parallel_execution(self.bal_parallel_execution_disabled)
755 .without_bal_parallel_state_root(self.bal_parallel_state_root_disabled)
756 .without_bal_batch_io(self.disable_bal_batch_io);
757 #[cfg(feature = "trie-debug")]
758 let config = config.with_proof_jitter(self.proof_jitter);
759 config
760 }
761}
762
763#[cfg(test)]
764mod tests {
765 use super::*;
766 use clap::Parser;
767
768 #[derive(Parser)]
770 struct CommandParser<T: Args> {
771 #[command(flatten)]
772 args: T,
773 }
774
775 #[test]
776 fn test_parse_engine_args() {
777 let default_args = EngineArgs::default();
778 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
779 assert_eq!(args, default_args);
780 assert_eq!(args.persistence_threshold, 7);
781 assert_eq!(args.memory_block_buffer_target, None);
782 assert_eq!(args.memory_block_buffer_target(), 5);
783 assert_eq!(
784 args.persistence_backpressure_threshold(),
785 DefaultEngineValues::get_global().persistence_backpressure_threshold
786 );
787 }
788
789 #[test]
790 fn txpool_prewarming_is_disabled_by_default_and_can_be_enabled() {
791 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
792 assert!(!args.txpool_prewarming_enabled);
793 assert!(!args.tree_config().txpool_prewarming());
794
795 let args =
796 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.txpool-prewarming"]).args;
797 assert!(args.txpool_prewarming_enabled);
798 assert!(args.tree_config().txpool_prewarming());
799 }
800
801 #[test]
802 fn validate_rejects_txpool_prewarming_with_disabled_state_cache() {
803 let args = EngineArgs {
804 state_cache_disabled: true,
805 txpool_prewarming_enabled: true,
806 ..EngineArgs::default()
807 };
808
809 let err = args.validate().unwrap_err().to_string();
810 assert!(err.contains("engine.txpool-prewarming"));
811 assert!(err.contains("engine.disable-state-cache"));
812 }
813
814 #[test]
815 fn default_backpressure_threshold_uses_parsed_persistence_args() {
816 let args = CommandParser::<EngineArgs>::parse_from([
817 "reth",
818 "--engine.persistence-threshold",
819 "100",
820 "--engine.memory-block-buffer-target",
821 "50",
822 ])
823 .args;
824
825 assert_eq!(args.persistence_backpressure_threshold(), 200);
826
827 let tree_config = args.tree_config();
828 assert_eq!(tree_config.persistence_threshold(), 100);
829 assert_eq!(tree_config.memory_block_buffer_target(), 50);
830 assert_eq!(tree_config.persistence_backpressure_threshold(), 200);
831 }
832
833 #[test]
834 fn default_backpressure_threshold_uses_global_default_when_larger() {
835 let args = CommandParser::<EngineArgs>::parse_from([
836 "reth",
837 "--engine.persistence-threshold",
838 "4",
839 ])
840 .args;
841
842 assert_eq!(
843 args.persistence_backpressure_threshold(),
844 DefaultEngineValues::get_global().persistence_backpressure_threshold
845 );
846 }
847
848 #[test]
849 fn explicit_backpressure_threshold_overrides_calculated_default() {
850 let args = CommandParser::<EngineArgs>::parse_from([
851 "reth",
852 "--engine.persistence-threshold",
853 "100",
854 "--engine.memory-block-buffer-target",
855 "50",
856 "--engine.persistence-backpressure-threshold",
857 "101",
858 ])
859 .args;
860
861 assert_eq!(args.persistence_backpressure_threshold(), 101);
862 }
863
864 #[test]
865 fn sender_recovery_cache_is_disabled_by_default_and_can_be_enabled() {
866 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
867 assert!(!args.sender_recovery_cache_enabled);
868
869 let args =
870 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.sender-recovery-cache"])
871 .args;
872 assert!(args.sender_recovery_cache_enabled);
873 }
874
875 #[test]
876 #[allow(deprecated)]
877 fn engine_args() {
878 let args = EngineArgs {
879 persistence_threshold: 100,
880 persistence_backpressure_threshold: Some(101),
881 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
882 memory_block_buffer_target: Some(50),
883 invalid_header_hit_eviction_threshold: 7,
884 legacy_state_root_task_enabled: true,
885 caching_and_prewarming_enabled: true,
886 state_cache_disabled: true,
887 prewarming_disabled: true,
888 txpool_prewarming_enabled: false,
890 sender_recovery_cache_enabled: true,
891 parallel_sparse_trie_enabled: true,
892 parallel_sparse_trie_disabled: false,
893 state_provider_metrics: true,
894 cross_block_cache_size: 256,
895 state_root_task_compare_updates: true,
896 accept_execution_requests_hash: true,
897 multiproof_chunk_size: 512,
898 reserved_cpu_cores: 4,
899 precompile_cache_enabled: true,
900 precompile_cache_disabled: true,
901 state_root_fallback: true,
902 always_process_payload_attributes_on_canonical_head: true,
903 allow_unwind_canonical_header: true,
904 storage_worker_count: Some(16),
905 account_worker_count: Some(8),
906 prewarming_threads: Some(4),
907 cache_metrics_disabled: true,
908 slow_block_threshold: None,
909 disable_sparse_trie_cache_pruning: true,
910 state_root_task_timeout: Some(Duration::from_secs(2)),
911 share_execution_cache_with_payload_builder: false,
912 share_sparse_trie_with_payload_builder: false,
913 suppress_persistence_during_build: false,
914 bal_parallel_execution_disabled: true,
915 bal_parallel_state_root_disabled: true,
916 disable_bal_batch_io: true,
917 #[cfg(feature = "trie-debug")]
918 proof_jitter: None,
919 };
920
921 let parsed_args = CommandParser::<EngineArgs>::parse_from([
922 "reth",
923 "--engine.persistence-threshold",
924 "100",
925 "--engine.persistence-backpressure-threshold",
926 "101",
927 "--engine.memory-block-buffer-target",
928 "50",
929 "--engine.invalid-header-cache-hit-eviction-threshold",
930 "7",
931 "--engine.legacy-state-root",
932 "--engine.disable-state-cache",
933 "--engine.disable-prewarming",
934 "--engine.sender-recovery-cache",
935 "--engine.state-provider-metrics",
936 "--engine.cross-block-cache-size",
937 "256",
938 "--engine.state-root-task-compare-updates",
939 "--engine.accept-execution-requests-hash",
940 "--engine.multiproof-chunk-size",
941 "512",
942 "--engine.reserved-cpu-cores",
943 "4",
944 "--engine.disable-precompile-cache",
945 "--engine.state-root-fallback",
946 "--engine.always-process-payload-attributes-on-canonical-head",
947 "--engine.allow-unwind-canonical-header",
948 "--engine.storage-worker-count",
949 "16",
950 "--engine.account-worker-count",
951 "8",
952 "--engine.prewarming-threads",
953 "4",
954 "--engine.disable-cache-metrics",
955 "--engine.disable-sparse-trie-cache-pruning",
956 "--engine.state-root-task-timeout",
957 "2s",
958 "--engine.disable-bal-parallel-execution",
959 "--engine.disable-bal-parallel-state-root",
960 "--engine.disable-bal-batch-io",
961 ])
962 .args;
963
964 assert_eq!(parsed_args, args);
965 }
966
967 #[test]
968 fn validate_rejects_invalid_backpressure_threshold() {
969 let args = EngineArgs {
970 persistence_threshold: 4,
971 persistence_backpressure_threshold: Some(4),
972 ..EngineArgs::default()
973 };
974
975 let err = args.validate().unwrap_err().to_string();
976 assert!(err.contains("engine.persistence-backpressure-threshold"));
977 assert!(err.contains("engine.persistence-threshold"));
978 }
979
980 #[test]
981 fn validate_memory_block_buffer_target() {
982 let args = EngineArgs {
983 persistence_threshold: 4,
984 memory_block_buffer_target: Some(4),
985 ..EngineArgs::default()
986 };
987 args.validate().unwrap();
988
989 let args = EngineArgs { memory_block_buffer_target: Some(5), ..args };
990 let err = args.validate().unwrap_err().to_string();
991 assert!(err.contains("engine.memory-block-buffer-target"));
992 assert!(err.contains("engine.persistence-threshold"));
993 }
994
995 #[test]
996 fn test_parse_num_state_masking_blocks() {
997 let args = CommandParser::<EngineArgs>::parse_from([
998 "reth",
999 "--engine.persistence-threshold",
1000 "13",
1001 "--engine.num-state-masking-blocks",
1002 "7",
1003 ])
1004 .args;
1005
1006 assert_eq!(args.tree_config().num_state_masking_blocks(), 7);
1007 }
1008
1009 #[test]
1010 fn validate_rejects_state_masking_window_at_or_above_threshold() {
1011 let args = EngineArgs {
1012 persistence_threshold: 4,
1013 num_state_masking_blocks: 2,
1014 memory_block_buffer_target: Some(2),
1015 ..EngineArgs::default()
1016 };
1017
1018 let err = args.validate().unwrap_err().to_string();
1019 assert!(err.contains("engine.num-state-masking-blocks"));
1020 }
1021
1022 #[test]
1023 fn validate_rejects_overflowing_state_masking_window() {
1024 let args = EngineArgs {
1025 persistence_threshold: 7,
1026 num_state_masking_blocks: u64::MAX,
1027 ..EngineArgs::default()
1028 };
1029
1030 let err = args.validate().unwrap_err().to_string();
1031 assert!(err.contains("engine.num-state-masking-blocks"));
1032 }
1033
1034 #[test]
1035 fn default_memory_block_buffer_target_is_bounded_by_persistence_threshold() {
1036 let args = CommandParser::<EngineArgs>::parse_from([
1037 "reth",
1038 "--engine.persistence-threshold",
1039 "4",
1040 ])
1041 .args;
1042
1043 assert_eq!(args.memory_block_buffer_target, None);
1044 assert_eq!(args.memory_block_buffer_target(), 4);
1045 assert_eq!(args.tree_config().memory_block_buffer_target(), 4);
1046 args.validate().unwrap();
1047 }
1048
1049 #[test]
1050 fn parse_rejects_zero_multiproof_chunk_size() {
1051 let result = CommandParser::<EngineArgs>::try_parse_from([
1052 "reth",
1053 "--engine.multiproof-chunk-size",
1054 "0",
1055 ]);
1056
1057 assert!(result.is_err());
1058 }
1059
1060 #[test]
1061 fn validate_rejects_bal_parallel_execution_without_bal_parallel_state_root() {
1062 let args = EngineArgs {
1063 bal_parallel_execution_disabled: false,
1064 bal_parallel_state_root_disabled: true,
1065 ..EngineArgs::default()
1066 };
1067
1068 let err = args.validate().unwrap_err().to_string();
1069 assert!(err.contains("engine.disable-bal-parallel-state-root"));
1070 assert!(err.contains("engine.disable-bal-parallel-execution"));
1071 }
1072
1073 #[test]
1074 fn test_parse_slow_block_threshold() {
1075 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1077 assert_eq!(args.slow_block_threshold, None);
1078
1079 let args =
1081 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
1082 .args;
1083 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
1084
1085 let args = CommandParser::<EngineArgs>::parse_from([
1087 "reth",
1088 "--engine.slow-block-threshold",
1089 "500",
1090 ])
1091 .args;
1092 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
1093
1094 let args = CommandParser::<EngineArgs>::parse_from([
1095 "reth",
1096 "--engine.slow-block-threshold",
1097 "500ms",
1098 ])
1099 .args;
1100 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
1101 }
1102
1103 #[test]
1104 fn test_parse_invalid_header_hit_eviction_threshold() {
1105 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1106 assert_eq!(
1107 args.invalid_header_hit_eviction_threshold,
1108 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1109 );
1110 assert_eq!(
1111 args.tree_config().invalid_header_hit_eviction_threshold(),
1112 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
1113 );
1114
1115 let args = CommandParser::<EngineArgs>::parse_from([
1116 "reth",
1117 "--engine.invalid-header-cache-hit-eviction-threshold",
1118 "0",
1119 ])
1120 .args;
1121 assert_eq!(args.invalid_header_hit_eviction_threshold, 0);
1122 assert_eq!(args.tree_config().invalid_header_hit_eviction_threshold(), 0);
1123 }
1124
1125 #[test]
1126 fn test_parse_share_sparse_trie_flag() {
1127 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
1128 assert!(!args.share_sparse_trie_with_payload_builder);
1129 assert!(!args.tree_config().share_sparse_trie_with_payload_builder());
1130
1131 let args = CommandParser::<EngineArgs>::parse_from([
1132 "reth",
1133 "--engine.share-sparse-trie-with-payload-builder",
1134 ])
1135 .args;
1136 assert!(args.share_sparse_trie_with_payload_builder);
1137 assert!(args.tree_config().share_sparse_trie_with_payload_builder());
1138 }
1139}