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_PERSISTENCE_BACKPRESSURE_THRESHOLD, DEFAULT_SPARSE_TRIE_MAX_HOT_ACCOUNTS,
12 DEFAULT_SPARSE_TRIE_MAX_HOT_SLOTS,
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 memory_block_buffer_target: u64,
32 invalid_header_hit_eviction_threshold: u8,
33 legacy_state_root_task_enabled: bool,
34 state_cache_disabled: bool,
35 prewarming_disabled: bool,
36 state_provider_metrics: bool,
37 cross_block_cache_size: usize,
38 state_root_task_compare_updates: bool,
39 accept_execution_requests_hash: bool,
40 multiproof_chunk_size: usize,
41 reserved_cpu_cores: usize,
42 precompile_cache_disabled: bool,
43 state_root_fallback: bool,
44 always_process_payload_attributes_on_canonical_head: bool,
45 allow_unwind_canonical_header: bool,
46 storage_worker_count: Option<usize>,
47 account_worker_count: Option<usize>,
48 prewarming_threads: Option<usize>,
49 cache_metrics_disabled: bool,
50 sparse_trie_max_hot_slots: usize,
51 sparse_trie_max_hot_accounts: usize,
52 slow_block_threshold: Option<Duration>,
53 disable_sparse_trie_cache_pruning: bool,
54 state_root_task_timeout: Option<String>,
55 share_execution_cache_with_payload_builder: bool,
56 share_sparse_trie_with_payload_builder: bool,
57 suppress_persistence_during_build: bool,
58 bal_parallel_execution_disabled: bool,
59 bal_parallel_state_root_disabled: bool,
60}
61
62impl DefaultEngineValues {
63 pub fn try_init(self) -> Result<(), Self> {
65 ENGINE_DEFAULTS.set(self)
66 }
67
68 pub fn get_global() -> &'static Self {
70 ENGINE_DEFAULTS.get_or_init(Self::default)
71 }
72
73 pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
75 self.persistence_threshold = v;
76 self
77 }
78
79 pub const fn with_persistence_backpressure_threshold(mut self, v: u64) -> Self {
81 self.persistence_backpressure_threshold = v;
82 self
83 }
84
85 pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
87 self.memory_block_buffer_target = v;
88 self
89 }
90
91 pub const fn with_invalid_header_hit_eviction_threshold(mut self, v: u8) -> Self {
93 self.invalid_header_hit_eviction_threshold = v;
94 self
95 }
96
97 pub const fn with_legacy_state_root_task_enabled(mut self, v: bool) -> Self {
99 self.legacy_state_root_task_enabled = v;
100 self
101 }
102
103 pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
105 self.state_cache_disabled = v;
106 self
107 }
108
109 pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
111 self.prewarming_disabled = v;
112 self
113 }
114
115 pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
117 self.state_provider_metrics = v;
118 self
119 }
120
121 pub const fn with_cross_block_cache_size(mut self, v: usize) -> Self {
123 self.cross_block_cache_size = v;
124 self
125 }
126
127 pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
129 self.state_root_task_compare_updates = v;
130 self
131 }
132
133 pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
135 self.accept_execution_requests_hash = v;
136 self
137 }
138
139 pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
141 self.multiproof_chunk_size = v;
142 self
143 }
144
145 pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
147 self.reserved_cpu_cores = v;
148 self
149 }
150
151 pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
153 self.precompile_cache_disabled = v;
154 self
155 }
156
157 pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
159 self.state_root_fallback = v;
160 self
161 }
162
163 pub const fn with_always_process_payload_attributes_on_canonical_head(
165 mut self,
166 v: bool,
167 ) -> Self {
168 self.always_process_payload_attributes_on_canonical_head = v;
169 self
170 }
171
172 pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
174 self.allow_unwind_canonical_header = v;
175 self
176 }
177
178 pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
180 self.storage_worker_count = v;
181 self
182 }
183
184 pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
186 self.account_worker_count = v;
187 self
188 }
189
190 pub const fn with_prewarming_threads(mut self, v: Option<usize>) -> Self {
192 self.prewarming_threads = v;
193 self
194 }
195
196 pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
198 self.cache_metrics_disabled = v;
199 self
200 }
201
202 pub const fn with_sparse_trie_max_hot_slots(mut self, v: usize) -> Self {
204 self.sparse_trie_max_hot_slots = v;
205 self
206 }
207
208 pub const fn with_sparse_trie_max_hot_accounts(mut self, v: usize) -> Self {
210 self.sparse_trie_max_hot_accounts = v;
211 self
212 }
213
214 pub const fn with_slow_block_threshold(mut self, v: Option<Duration>) -> Self {
216 self.slow_block_threshold = v;
217 self
218 }
219
220 pub const fn with_disable_sparse_trie_cache_pruning(mut self, v: bool) -> Self {
222 self.disable_sparse_trie_cache_pruning = v;
223 self
224 }
225
226 pub fn with_state_root_task_timeout(mut self, v: Option<String>) -> Self {
228 self.state_root_task_timeout = v;
229 self
230 }
231
232 pub const fn with_share_execution_cache_with_payload_builder(mut self, v: bool) -> Self {
234 self.share_execution_cache_with_payload_builder = v;
235 self
236 }
237
238 pub const fn with_share_sparse_trie_with_payload_builder(mut self, v: bool) -> Self {
240 self.share_sparse_trie_with_payload_builder = v;
241 self
242 }
243
244 pub const fn with_suppress_persistence_during_build(mut self, v: bool) -> Self {
246 self.suppress_persistence_during_build = v;
247 self
248 }
249
250 pub const fn with_bal_parallel_execution_disabled(mut self, v: bool) -> Self {
252 self.bal_parallel_execution_disabled = v;
253 self
254 }
255
256 pub const fn with_bal_parallel_state_root_disabled(mut self, v: bool) -> Self {
258 self.bal_parallel_state_root_disabled = v;
259 self
260 }
261}
262
263impl Default for DefaultEngineValues {
264 fn default() -> Self {
265 Self {
266 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
267 persistence_backpressure_threshold: DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
268 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
269 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
270 legacy_state_root_task_enabled: false,
271 state_cache_disabled: false,
272 prewarming_disabled: false,
273 state_provider_metrics: false,
274 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
275 state_root_task_compare_updates: false,
276 accept_execution_requests_hash: false,
277 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
278 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
279 precompile_cache_disabled: false,
280 state_root_fallback: false,
281 always_process_payload_attributes_on_canonical_head: false,
282 allow_unwind_canonical_header: false,
283 storage_worker_count: None,
284 account_worker_count: None,
285 prewarming_threads: None,
286 cache_metrics_disabled: false,
287 sparse_trie_max_hot_slots: DEFAULT_SPARSE_TRIE_MAX_HOT_SLOTS,
288 sparse_trie_max_hot_accounts: DEFAULT_SPARSE_TRIE_MAX_HOT_ACCOUNTS,
289 slow_block_threshold: None,
290 disable_sparse_trie_cache_pruning: false,
291 state_root_task_timeout: Some("4s".to_string()),
292 share_execution_cache_with_payload_builder: false,
293 share_sparse_trie_with_payload_builder: false,
294 suppress_persistence_during_build: false,
295 bal_parallel_execution_disabled: false,
296 bal_parallel_state_root_disabled: false,
297 }
298 }
299}
300
301fn default_persistence_backpressure_threshold(persistence_threshold: u64) -> u64 {
302 DefaultEngineValues::get_global()
303 .persistence_backpressure_threshold
304 .max(persistence_threshold.saturating_mul(2))
305}
306
307#[derive(Debug, Clone, Args, PartialEq, Eq)]
309#[command(next_help_heading = "Engine")]
310pub struct EngineArgs {
311 #[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
318 pub persistence_threshold: u64,
319
320 #[arg(long = "engine.persistence-backpressure-threshold")]
327 pub persistence_backpressure_threshold: Option<u64>,
328
329 #[arg(long = "engine.memory-block-buffer-target", default_value_t = DefaultEngineValues::get_global().memory_block_buffer_target)]
331 pub memory_block_buffer_target: u64,
332
333 #[arg(long = "engine.invalid-header-cache-hit-eviction-threshold", default_value_t = DefaultEngineValues::get_global().invalid_header_hit_eviction_threshold)]
339 pub invalid_header_hit_eviction_threshold: u8,
340
341 #[arg(long = "engine.legacy-state-root", default_value_t = DefaultEngineValues::get_global().legacy_state_root_task_enabled)]
343 pub legacy_state_root_task_enabled: bool,
344
345 #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
348 #[deprecated]
349 pub caching_and_prewarming_enabled: bool,
350
351 #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
353 pub state_cache_disabled: bool,
354
355 #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
357 pub prewarming_disabled: bool,
358
359 #[deprecated]
361 #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
362 pub parallel_sparse_trie_enabled: bool,
363
364 #[deprecated]
366 #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
367 pub parallel_sparse_trie_disabled: bool,
368
369 #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
373 pub state_provider_metrics: bool,
374
375 #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
377 pub cross_block_cache_size: usize,
378
379 #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
382 pub state_root_task_compare_updates: bool,
383
384 #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
386 pub accept_execution_requests_hash: bool,
387
388 #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size, value_parser = RangedU64ValueParser::<usize>::new().range(1..))]
390 pub multiproof_chunk_size: usize,
391
392 #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
394 pub reserved_cpu_cores: usize,
395
396 #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
399 #[deprecated]
400 pub precompile_cache_enabled: bool,
401
402 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
404 pub precompile_cache_disabled: bool,
405
406 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
408 pub state_root_fallback: bool,
409
410 #[arg(
416 long = "engine.always-process-payload-attributes-on-canonical-head",
417 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
418 )]
419 pub always_process_payload_attributes_on_canonical_head: bool,
420
421 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
424 pub allow_unwind_canonical_header: bool,
425
426 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
429 pub storage_worker_count: Option<usize>,
430
431 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
434 pub account_worker_count: Option<usize>,
435
436 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
439 pub prewarming_threads: Option<usize>,
440
441 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
443 pub cache_metrics_disabled: bool,
444
445 #[arg(long = "engine.sparse-trie-max-hot-slots", alias = "engine.sparse-trie-max-storage-tries", default_value_t = DefaultEngineValues::get_global().sparse_trie_max_hot_slots)]
447 pub sparse_trie_max_hot_slots: usize,
448
449 #[arg(long = "engine.sparse-trie-max-hot-accounts", default_value_t = DefaultEngineValues::get_global().sparse_trie_max_hot_accounts)]
451 pub sparse_trie_max_hot_accounts: usize,
452
453 #[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())))]
462 pub slow_block_threshold: Option<Duration>,
463
464 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
468 pub disable_sparse_trie_cache_pruning: bool,
469
470 #[arg(
479 long = "engine.state-root-task-timeout",
480 value_parser = humantime::parse_duration,
481 default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("4s"),
482 )]
483 pub state_root_task_timeout: Option<Duration>,
484
485 #[arg(
493 long = "engine.share-execution-cache-with-payload-builder",
494 default_value_t = DefaultEngineValues::get_global().share_execution_cache_with_payload_builder,
495 )]
496 pub share_execution_cache_with_payload_builder: bool,
497
498 #[arg(
510 long = "engine.share-sparse-trie-with-payload-builder",
511 default_value_t = DefaultEngineValues::get_global().share_sparse_trie_with_payload_builder,
512 )]
513 pub share_sparse_trie_with_payload_builder: bool,
514
515 #[arg(
521 long = "engine.suppress-persistence-during-build",
522 default_value_t = DefaultEngineValues::get_global().suppress_persistence_during_build,
523 )]
524 pub suppress_persistence_during_build: bool,
525
526 #[arg(long = "engine.disable-bal-parallel-execution", default_value_t = DefaultEngineValues::get_global().bal_parallel_execution_disabled)]
528 pub bal_parallel_execution_disabled: bool,
529
530 #[arg(long = "engine.disable-bal-parallel-state-root", default_value_t = DefaultEngineValues::get_global().bal_parallel_state_root_disabled)]
533 pub bal_parallel_state_root_disabled: bool,
534
535 #[arg(long = "engine.disable-bal-batch-io", default_value_t = false)]
538 pub disable_bal_batch_io: bool,
539
540 #[cfg(feature = "trie-debug")]
547 #[arg(
548 long = "engine.proof-jitter",
549 value_parser = humantime::parse_duration,
550 )]
551 pub proof_jitter: Option<Duration>,
552}
553
554#[allow(deprecated)]
555impl Default for EngineArgs {
556 fn default() -> Self {
557 let DefaultEngineValues {
558 persistence_threshold,
559 persistence_backpressure_threshold: _,
560 memory_block_buffer_target,
561 invalid_header_hit_eviction_threshold,
562 legacy_state_root_task_enabled,
563 state_cache_disabled,
564 prewarming_disabled,
565 state_provider_metrics,
566 cross_block_cache_size,
567 state_root_task_compare_updates,
568 accept_execution_requests_hash,
569 multiproof_chunk_size,
570 reserved_cpu_cores,
571 precompile_cache_disabled,
572 state_root_fallback,
573 always_process_payload_attributes_on_canonical_head,
574 allow_unwind_canonical_header,
575 storage_worker_count,
576 account_worker_count,
577 prewarming_threads,
578 cache_metrics_disabled,
579 sparse_trie_max_hot_slots,
580 sparse_trie_max_hot_accounts,
581 slow_block_threshold,
582 disable_sparse_trie_cache_pruning,
583 state_root_task_timeout,
584 share_execution_cache_with_payload_builder,
585 share_sparse_trie_with_payload_builder,
586 suppress_persistence_during_build,
587 bal_parallel_execution_disabled,
588 bal_parallel_state_root_disabled,
589 } = DefaultEngineValues::get_global().clone();
590 Self {
591 persistence_threshold,
592 persistence_backpressure_threshold: None,
593 memory_block_buffer_target,
594 invalid_header_hit_eviction_threshold,
595 legacy_state_root_task_enabled,
596 state_root_task_compare_updates,
597 caching_and_prewarming_enabled: true,
598 state_cache_disabled,
599 prewarming_disabled,
600 parallel_sparse_trie_enabled: true,
601 parallel_sparse_trie_disabled: false,
602 state_provider_metrics,
603 cross_block_cache_size,
604 accept_execution_requests_hash,
605 multiproof_chunk_size,
606 reserved_cpu_cores,
607 precompile_cache_enabled: true,
608 precompile_cache_disabled,
609 state_root_fallback,
610 always_process_payload_attributes_on_canonical_head,
611 allow_unwind_canonical_header,
612 storage_worker_count,
613 account_worker_count,
614 prewarming_threads,
615 cache_metrics_disabled,
616 sparse_trie_max_hot_slots,
617 sparse_trie_max_hot_accounts,
618 slow_block_threshold,
619 disable_sparse_trie_cache_pruning,
620 state_root_task_timeout: state_root_task_timeout
621 .as_deref()
622 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
623 share_execution_cache_with_payload_builder,
624 share_sparse_trie_with_payload_builder,
625 suppress_persistence_during_build,
626 bal_parallel_execution_disabled,
627 bal_parallel_state_root_disabled,
628 disable_bal_batch_io: false,
629 #[cfg(feature = "trie-debug")]
630 proof_jitter: None,
631 }
632 }
633}
634
635impl EngineArgs {
636 pub fn persistence_backpressure_threshold(&self) -> u64 {
638 self.persistence_backpressure_threshold.unwrap_or_else(|| {
639 default_persistence_backpressure_threshold(self.persistence_threshold)
640 })
641 }
642
643 pub fn validate(&self) -> eyre::Result<()> {
645 let persistence_backpressure_threshold = self.persistence_backpressure_threshold();
646 ensure!(
647 persistence_backpressure_threshold > self.persistence_threshold,
648 "--engine.persistence-backpressure-threshold ({}) must be greater than --engine.persistence-threshold ({})",
649 persistence_backpressure_threshold,
650 self.persistence_threshold
651 );
652 ensure!(
653 self.bal_parallel_execution_disabled || !self.bal_parallel_state_root_disabled,
654 "--engine.disable-bal-parallel-state-root requires --engine.disable-bal-parallel-execution because BAL parallel execution depends on BAL prewarm state-root updates"
655 );
656 Ok(())
657 }
658
659 pub fn tree_config(&self) -> TreeConfig {
661 let config = TreeConfig::default()
662 .with_persistence_backpressure_threshold(self.persistence_backpressure_threshold())
663 .with_persistence_threshold(self.persistence_threshold)
664 .with_memory_block_buffer_target(self.memory_block_buffer_target)
665 .with_invalid_header_hit_eviction_threshold(self.invalid_header_hit_eviction_threshold)
666 .with_legacy_state_root(self.legacy_state_root_task_enabled)
667 .without_state_cache(self.state_cache_disabled)
668 .without_prewarming(self.prewarming_disabled)
669 .with_state_provider_metrics(self.state_provider_metrics)
670 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
671 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
672 .with_multiproof_chunk_size(self.multiproof_chunk_size)
673 .with_reserved_cpu_cores(self.reserved_cpu_cores)
674 .without_precompile_cache(self.precompile_cache_disabled)
675 .with_state_root_fallback(self.state_root_fallback)
676 .with_always_process_payload_attributes_on_canonical_head(
677 self.always_process_payload_attributes_on_canonical_head,
678 )
679 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
680 .without_cache_metrics(self.cache_metrics_disabled)
681 .with_sparse_trie_max_hot_slots(self.sparse_trie_max_hot_slots)
682 .with_sparse_trie_max_hot_accounts(self.sparse_trie_max_hot_accounts)
683 .with_slow_block_threshold(self.slow_block_threshold)
684 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
685 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
686 .with_share_execution_cache_with_payload_builder(
687 self.share_execution_cache_with_payload_builder,
688 )
689 .with_share_sparse_trie_with_payload_builder(
690 self.share_sparse_trie_with_payload_builder,
691 )
692 .with_suppress_persistence_during_build(self.suppress_persistence_during_build)
693 .without_bal_parallel_execution(self.bal_parallel_execution_disabled)
694 .without_bal_parallel_state_root(self.bal_parallel_state_root_disabled)
695 .without_bal_batch_io(self.disable_bal_batch_io);
696 #[cfg(feature = "trie-debug")]
697 let config = config.with_proof_jitter(self.proof_jitter);
698 config
699 }
700}
701
702#[cfg(test)]
703mod tests {
704 use super::*;
705 use clap::Parser;
706
707 #[derive(Parser)]
709 struct CommandParser<T: Args> {
710 #[command(flatten)]
711 args: T,
712 }
713
714 #[test]
715 fn test_parse_engine_args() {
716 let default_args = EngineArgs::default();
717 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
718 assert_eq!(args, default_args);
719 assert_eq!(
720 args.persistence_backpressure_threshold(),
721 DefaultEngineValues::get_global().persistence_backpressure_threshold
722 );
723 }
724
725 #[test]
726 fn default_backpressure_threshold_uses_parsed_persistence_args() {
727 let args = CommandParser::<EngineArgs>::parse_from([
728 "reth",
729 "--engine.persistence-threshold",
730 "100",
731 "--engine.memory-block-buffer-target",
732 "50",
733 ])
734 .args;
735
736 assert_eq!(args.persistence_backpressure_threshold(), 200);
737
738 let tree_config = args.tree_config();
739 assert_eq!(tree_config.persistence_threshold(), 100);
740 assert_eq!(tree_config.memory_block_buffer_target(), 50);
741 assert_eq!(tree_config.persistence_backpressure_threshold(), 200);
742 }
743
744 #[test]
745 fn default_backpressure_threshold_uses_global_default_when_larger() {
746 let args = CommandParser::<EngineArgs>::parse_from([
747 "reth",
748 "--engine.persistence-threshold",
749 "4",
750 ])
751 .args;
752
753 assert_eq!(
754 args.persistence_backpressure_threshold(),
755 DefaultEngineValues::get_global().persistence_backpressure_threshold
756 );
757 }
758
759 #[test]
760 fn explicit_backpressure_threshold_overrides_calculated_default() {
761 let args = CommandParser::<EngineArgs>::parse_from([
762 "reth",
763 "--engine.persistence-threshold",
764 "100",
765 "--engine.memory-block-buffer-target",
766 "50",
767 "--engine.persistence-backpressure-threshold",
768 "101",
769 ])
770 .args;
771
772 assert_eq!(args.persistence_backpressure_threshold(), 101);
773 }
774
775 #[test]
776 #[allow(deprecated)]
777 fn engine_args() {
778 let args = EngineArgs {
779 persistence_threshold: 100,
780 persistence_backpressure_threshold: Some(101),
781 memory_block_buffer_target: 50,
782 invalid_header_hit_eviction_threshold: 7,
783 legacy_state_root_task_enabled: true,
784 caching_and_prewarming_enabled: true,
785 state_cache_disabled: true,
786 prewarming_disabled: true,
787 parallel_sparse_trie_enabled: true,
788 parallel_sparse_trie_disabled: false,
789 state_provider_metrics: true,
790 cross_block_cache_size: 256,
791 state_root_task_compare_updates: true,
792 accept_execution_requests_hash: true,
793 multiproof_chunk_size: 512,
794 reserved_cpu_cores: 4,
795 precompile_cache_enabled: true,
796 precompile_cache_disabled: true,
797 state_root_fallback: true,
798 always_process_payload_attributes_on_canonical_head: true,
799 allow_unwind_canonical_header: true,
800 storage_worker_count: Some(16),
801 account_worker_count: Some(8),
802 prewarming_threads: Some(4),
803 cache_metrics_disabled: true,
804 sparse_trie_max_hot_slots: 100,
805 sparse_trie_max_hot_accounts: 500,
806 slow_block_threshold: None,
807 disable_sparse_trie_cache_pruning: true,
808 state_root_task_timeout: Some(Duration::from_secs(2)),
809 share_execution_cache_with_payload_builder: false,
810 share_sparse_trie_with_payload_builder: false,
811 suppress_persistence_during_build: false,
812 bal_parallel_execution_disabled: true,
813 bal_parallel_state_root_disabled: true,
814 disable_bal_batch_io: true,
815 #[cfg(feature = "trie-debug")]
816 proof_jitter: None,
817 };
818
819 let parsed_args = CommandParser::<EngineArgs>::parse_from([
820 "reth",
821 "--engine.persistence-threshold",
822 "100",
823 "--engine.persistence-backpressure-threshold",
824 "101",
825 "--engine.memory-block-buffer-target",
826 "50",
827 "--engine.invalid-header-cache-hit-eviction-threshold",
828 "7",
829 "--engine.legacy-state-root",
830 "--engine.disable-state-cache",
831 "--engine.disable-prewarming",
832 "--engine.state-provider-metrics",
833 "--engine.cross-block-cache-size",
834 "256",
835 "--engine.state-root-task-compare-updates",
836 "--engine.accept-execution-requests-hash",
837 "--engine.multiproof-chunk-size",
838 "512",
839 "--engine.reserved-cpu-cores",
840 "4",
841 "--engine.disable-precompile-cache",
842 "--engine.state-root-fallback",
843 "--engine.always-process-payload-attributes-on-canonical-head",
844 "--engine.allow-unwind-canonical-header",
845 "--engine.storage-worker-count",
846 "16",
847 "--engine.account-worker-count",
848 "8",
849 "--engine.prewarming-threads",
850 "4",
851 "--engine.disable-cache-metrics",
852 "--engine.sparse-trie-max-hot-slots",
853 "100",
854 "--engine.sparse-trie-max-hot-accounts",
855 "500",
856 "--engine.disable-sparse-trie-cache-pruning",
857 "--engine.state-root-task-timeout",
858 "2s",
859 "--engine.disable-bal-parallel-execution",
860 "--engine.disable-bal-parallel-state-root",
861 "--engine.disable-bal-batch-io",
862 ])
863 .args;
864
865 assert_eq!(parsed_args, args);
866 }
867
868 #[test]
869 fn validate_rejects_invalid_backpressure_threshold() {
870 let args = EngineArgs {
871 persistence_threshold: 4,
872 persistence_backpressure_threshold: Some(4),
873 ..EngineArgs::default()
874 };
875
876 let err = args.validate().unwrap_err().to_string();
877 assert!(err.contains("engine.persistence-backpressure-threshold"));
878 assert!(err.contains("engine.persistence-threshold"));
879 }
880
881 #[test]
882 fn parse_rejects_zero_multiproof_chunk_size() {
883 let result = CommandParser::<EngineArgs>::try_parse_from([
884 "reth",
885 "--engine.multiproof-chunk-size",
886 "0",
887 ]);
888
889 assert!(result.is_err());
890 }
891
892 #[test]
893 fn validate_rejects_bal_parallel_execution_without_bal_parallel_state_root() {
894 let args = EngineArgs {
895 bal_parallel_execution_disabled: false,
896 bal_parallel_state_root_disabled: true,
897 ..EngineArgs::default()
898 };
899
900 let err = args.validate().unwrap_err().to_string();
901 assert!(err.contains("engine.disable-bal-parallel-state-root"));
902 assert!(err.contains("engine.disable-bal-parallel-execution"));
903 }
904
905 #[test]
906 fn test_parse_slow_block_threshold() {
907 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
909 assert_eq!(args.slow_block_threshold, None);
910
911 let args =
913 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
914 .args;
915 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
916
917 let args = CommandParser::<EngineArgs>::parse_from([
919 "reth",
920 "--engine.slow-block-threshold",
921 "500",
922 ])
923 .args;
924 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
925
926 let args = CommandParser::<EngineArgs>::parse_from([
927 "reth",
928 "--engine.slow-block-threshold",
929 "500ms",
930 ])
931 .args;
932 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
933 }
934
935 #[test]
936 fn test_parse_invalid_header_hit_eviction_threshold() {
937 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
938 assert_eq!(
939 args.invalid_header_hit_eviction_threshold,
940 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
941 );
942 assert_eq!(
943 args.tree_config().invalid_header_hit_eviction_threshold(),
944 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
945 );
946
947 let args = CommandParser::<EngineArgs>::parse_from([
948 "reth",
949 "--engine.invalid-header-cache-hit-eviction-threshold",
950 "0",
951 ])
952 .args;
953 assert_eq!(args.invalid_header_hit_eviction_threshold, 0);
954 assert_eq!(args.tree_config().invalid_header_hit_eviction_threshold(), 0);
955 }
956
957 #[test]
958 fn test_parse_share_sparse_trie_flag() {
959 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
960 assert!(!args.share_sparse_trie_with_payload_builder);
961 assert!(!args.tree_config().share_sparse_trie_with_payload_builder());
962
963 let args = CommandParser::<EngineArgs>::parse_from([
964 "reth",
965 "--engine.share-sparse-trie-with-payload-builder",
966 ])
967 .args;
968 assert!(args.share_sparse_trie_with_payload_builder);
969 assert!(args.tree_config().share_sparse_trie_with_payload_builder());
970 }
971}