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 state_cache_disabled: bool,
34 prewarming_disabled: bool,
35 state_provider_metrics: bool,
36 cross_block_cache_size: usize,
37 state_root_task_compare_updates: bool,
38 accept_execution_requests_hash: bool,
39 multiproof_chunk_size: usize,
40 reserved_cpu_cores: usize,
41 precompile_cache_disabled: bool,
42 state_root_fallback: bool,
43 always_process_payload_attributes_on_canonical_head: bool,
44 allow_unwind_canonical_header: bool,
45 storage_worker_count: Option<usize>,
46 account_worker_count: Option<usize>,
47 prewarming_threads: Option<usize>,
48 cache_metrics_disabled: bool,
49 sparse_trie_max_hot_slots: usize,
50 sparse_trie_max_hot_accounts: usize,
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_memory_block_buffer_target(mut self, v: u64) -> Self {
86 self.memory_block_buffer_target = v;
87 self
88 }
89
90 pub const fn with_invalid_header_hit_eviction_threshold(mut self, v: u8) -> Self {
92 self.invalid_header_hit_eviction_threshold = v;
93 self
94 }
95
96 pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
98 self.state_cache_disabled = v;
99 self
100 }
101
102 pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
104 self.prewarming_disabled = v;
105 self
106 }
107
108 pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
110 self.state_provider_metrics = v;
111 self
112 }
113
114 pub const fn with_cross_block_cache_size(mut self, v: usize) -> Self {
116 self.cross_block_cache_size = v;
117 self
118 }
119
120 pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
122 self.state_root_task_compare_updates = v;
123 self
124 }
125
126 pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
128 self.accept_execution_requests_hash = v;
129 self
130 }
131
132 pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
134 self.multiproof_chunk_size = v;
135 self
136 }
137
138 pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
140 self.reserved_cpu_cores = v;
141 self
142 }
143
144 pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
146 self.precompile_cache_disabled = v;
147 self
148 }
149
150 pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
152 self.state_root_fallback = v;
153 self
154 }
155
156 pub const fn with_always_process_payload_attributes_on_canonical_head(
158 mut self,
159 v: bool,
160 ) -> Self {
161 self.always_process_payload_attributes_on_canonical_head = v;
162 self
163 }
164
165 pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
167 self.allow_unwind_canonical_header = v;
168 self
169 }
170
171 pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
173 self.storage_worker_count = v;
174 self
175 }
176
177 pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
179 self.account_worker_count = v;
180 self
181 }
182
183 pub const fn with_prewarming_threads(mut self, v: Option<usize>) -> Self {
185 self.prewarming_threads = v;
186 self
187 }
188
189 pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
191 self.cache_metrics_disabled = v;
192 self
193 }
194
195 pub const fn with_sparse_trie_max_hot_slots(mut self, v: usize) -> Self {
197 self.sparse_trie_max_hot_slots = v;
198 self
199 }
200
201 pub const fn with_sparse_trie_max_hot_accounts(mut self, v: usize) -> Self {
203 self.sparse_trie_max_hot_accounts = v;
204 self
205 }
206
207 pub const fn with_slow_block_threshold(mut self, v: Option<Duration>) -> Self {
209 self.slow_block_threshold = v;
210 self
211 }
212
213 pub const fn with_disable_sparse_trie_cache_pruning(mut self, v: bool) -> Self {
215 self.disable_sparse_trie_cache_pruning = v;
216 self
217 }
218
219 pub fn with_state_root_task_timeout(mut self, v: Option<String>) -> Self {
221 self.state_root_task_timeout = v;
222 self
223 }
224
225 pub const fn with_share_execution_cache_with_payload_builder(mut self, v: bool) -> Self {
227 self.share_execution_cache_with_payload_builder = v;
228 self
229 }
230
231 pub const fn with_share_sparse_trie_with_payload_builder(mut self, v: bool) -> Self {
233 self.share_sparse_trie_with_payload_builder = v;
234 self
235 }
236
237 pub const fn with_suppress_persistence_during_build(mut self, v: bool) -> Self {
239 self.suppress_persistence_during_build = v;
240 self
241 }
242
243 pub const fn with_bal_parallel_execution_disabled(mut self, v: bool) -> Self {
245 self.bal_parallel_execution_disabled = v;
246 self
247 }
248
249 pub const fn with_bal_parallel_state_root_disabled(mut self, v: bool) -> Self {
251 self.bal_parallel_state_root_disabled = v;
252 self
253 }
254}
255
256impl Default for DefaultEngineValues {
257 fn default() -> Self {
258 Self {
259 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
260 persistence_backpressure_threshold: DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
261 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
262 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
263 state_cache_disabled: false,
264 prewarming_disabled: false,
265 state_provider_metrics: false,
266 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
267 state_root_task_compare_updates: false,
268 accept_execution_requests_hash: false,
269 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
270 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
271 precompile_cache_disabled: false,
272 state_root_fallback: false,
273 always_process_payload_attributes_on_canonical_head: false,
274 allow_unwind_canonical_header: false,
275 storage_worker_count: None,
276 account_worker_count: None,
277 prewarming_threads: None,
278 cache_metrics_disabled: false,
279 sparse_trie_max_hot_slots: DEFAULT_SPARSE_TRIE_MAX_HOT_SLOTS,
280 sparse_trie_max_hot_accounts: DEFAULT_SPARSE_TRIE_MAX_HOT_ACCOUNTS,
281 slow_block_threshold: None,
282 disable_sparse_trie_cache_pruning: false,
283 state_root_task_timeout: Some("4s".to_string()),
284 share_execution_cache_with_payload_builder: false,
285 share_sparse_trie_with_payload_builder: false,
286 suppress_persistence_during_build: false,
287 bal_parallel_execution_disabled: false,
288 bal_parallel_state_root_disabled: false,
289 }
290 }
291}
292
293fn default_persistence_backpressure_threshold(persistence_threshold: u64) -> u64 {
294 DefaultEngineValues::get_global()
295 .persistence_backpressure_threshold
296 .max(persistence_threshold.saturating_mul(2))
297}
298
299#[derive(Debug, Clone, Args, PartialEq, Eq)]
301#[command(next_help_heading = "Engine")]
302pub struct EngineArgs {
303 #[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
310 pub persistence_threshold: u64,
311
312 #[arg(long = "engine.persistence-backpressure-threshold")]
319 pub persistence_backpressure_threshold: Option<u64>,
320
321 #[arg(long = "engine.memory-block-buffer-target", default_value_t = DefaultEngineValues::get_global().memory_block_buffer_target)]
323 pub memory_block_buffer_target: u64,
324
325 #[arg(long = "engine.invalid-header-cache-hit-eviction-threshold", default_value_t = DefaultEngineValues::get_global().invalid_header_hit_eviction_threshold)]
331 pub invalid_header_hit_eviction_threshold: u8,
332
333 #[arg(long = "engine.legacy-state-root", default_value_t = false, hide = true)]
336 #[deprecated]
337 pub legacy_state_root_task_enabled: bool,
338
339 #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
342 #[deprecated]
343 pub caching_and_prewarming_enabled: bool,
344
345 #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
347 pub state_cache_disabled: bool,
348
349 #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
351 pub prewarming_disabled: bool,
352
353 #[deprecated]
355 #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
356 pub parallel_sparse_trie_enabled: bool,
357
358 #[deprecated]
360 #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
361 pub parallel_sparse_trie_disabled: bool,
362
363 #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
367 pub state_provider_metrics: bool,
368
369 #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
371 pub cross_block_cache_size: usize,
372
373 #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
376 pub state_root_task_compare_updates: bool,
377
378 #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
380 pub accept_execution_requests_hash: bool,
381
382 #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size, value_parser = RangedU64ValueParser::<usize>::new().range(1..))]
384 pub multiproof_chunk_size: usize,
385
386 #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
388 pub reserved_cpu_cores: usize,
389
390 #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
393 #[deprecated]
394 pub precompile_cache_enabled: bool,
395
396 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
398 pub precompile_cache_disabled: bool,
399
400 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
402 pub state_root_fallback: bool,
403
404 #[arg(
410 long = "engine.always-process-payload-attributes-on-canonical-head",
411 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
412 )]
413 pub always_process_payload_attributes_on_canonical_head: bool,
414
415 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
418 pub allow_unwind_canonical_header: bool,
419
420 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
423 pub storage_worker_count: Option<usize>,
424
425 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
428 pub account_worker_count: Option<usize>,
429
430 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
433 pub prewarming_threads: Option<usize>,
434
435 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
437 pub cache_metrics_disabled: bool,
438
439 #[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)]
441 pub sparse_trie_max_hot_slots: usize,
442
443 #[arg(long = "engine.sparse-trie-max-hot-accounts", default_value_t = DefaultEngineValues::get_global().sparse_trie_max_hot_accounts)]
445 pub sparse_trie_max_hot_accounts: usize,
446
447 #[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())))]
456 pub slow_block_threshold: Option<Duration>,
457
458 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
462 pub disable_sparse_trie_cache_pruning: bool,
463
464 #[arg(
473 long = "engine.state-root-task-timeout",
474 value_parser = humantime::parse_duration,
475 default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("4s"),
476 )]
477 pub state_root_task_timeout: Option<Duration>,
478
479 #[arg(
487 long = "engine.share-execution-cache-with-payload-builder",
488 default_value_t = DefaultEngineValues::get_global().share_execution_cache_with_payload_builder,
489 )]
490 pub share_execution_cache_with_payload_builder: bool,
491
492 #[arg(
504 long = "engine.share-sparse-trie-with-payload-builder",
505 default_value_t = DefaultEngineValues::get_global().share_sparse_trie_with_payload_builder,
506 )]
507 pub share_sparse_trie_with_payload_builder: bool,
508
509 #[arg(
515 long = "engine.suppress-persistence-during-build",
516 default_value_t = DefaultEngineValues::get_global().suppress_persistence_during_build,
517 )]
518 pub suppress_persistence_during_build: bool,
519
520 #[arg(long = "engine.disable-bal-parallel-execution", default_value_t = DefaultEngineValues::get_global().bal_parallel_execution_disabled)]
522 pub bal_parallel_execution_disabled: bool,
523
524 #[arg(long = "engine.disable-bal-parallel-state-root", default_value_t = DefaultEngineValues::get_global().bal_parallel_state_root_disabled)]
527 pub bal_parallel_state_root_disabled: bool,
528
529 #[arg(long = "engine.disable-bal-batch-io", default_value_t = false)]
532 pub disable_bal_batch_io: bool,
533
534 #[cfg(feature = "trie-debug")]
541 #[arg(
542 long = "engine.proof-jitter",
543 value_parser = humantime::parse_duration,
544 )]
545 pub proof_jitter: Option<Duration>,
546}
547
548#[allow(deprecated)]
549impl Default for EngineArgs {
550 fn default() -> Self {
551 let DefaultEngineValues {
552 persistence_threshold,
553 persistence_backpressure_threshold: _,
554 memory_block_buffer_target,
555 invalid_header_hit_eviction_threshold,
556 state_cache_disabled,
557 prewarming_disabled,
558 state_provider_metrics,
559 cross_block_cache_size,
560 state_root_task_compare_updates,
561 accept_execution_requests_hash,
562 multiproof_chunk_size,
563 reserved_cpu_cores,
564 precompile_cache_disabled,
565 state_root_fallback,
566 always_process_payload_attributes_on_canonical_head,
567 allow_unwind_canonical_header,
568 storage_worker_count,
569 account_worker_count,
570 prewarming_threads,
571 cache_metrics_disabled,
572 sparse_trie_max_hot_slots,
573 sparse_trie_max_hot_accounts,
574 slow_block_threshold,
575 disable_sparse_trie_cache_pruning,
576 state_root_task_timeout,
577 share_execution_cache_with_payload_builder,
578 share_sparse_trie_with_payload_builder,
579 suppress_persistence_during_build,
580 bal_parallel_execution_disabled,
581 bal_parallel_state_root_disabled,
582 } = DefaultEngineValues::get_global().clone();
583 Self {
584 persistence_threshold,
585 persistence_backpressure_threshold: None,
586 memory_block_buffer_target,
587 invalid_header_hit_eviction_threshold,
588 state_root_task_compare_updates,
589 legacy_state_root_task_enabled: false,
590 caching_and_prewarming_enabled: true,
591 state_cache_disabled,
592 prewarming_disabled,
593 parallel_sparse_trie_enabled: true,
594 parallel_sparse_trie_disabled: false,
595 state_provider_metrics,
596 cross_block_cache_size,
597 accept_execution_requests_hash,
598 multiproof_chunk_size,
599 reserved_cpu_cores,
600 precompile_cache_enabled: true,
601 precompile_cache_disabled,
602 state_root_fallback,
603 always_process_payload_attributes_on_canonical_head,
604 allow_unwind_canonical_header,
605 storage_worker_count,
606 account_worker_count,
607 prewarming_threads,
608 cache_metrics_disabled,
609 sparse_trie_max_hot_slots,
610 sparse_trie_max_hot_accounts,
611 slow_block_threshold,
612 disable_sparse_trie_cache_pruning,
613 state_root_task_timeout: state_root_task_timeout
614 .as_deref()
615 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
616 share_execution_cache_with_payload_builder,
617 share_sparse_trie_with_payload_builder,
618 suppress_persistence_during_build,
619 bal_parallel_execution_disabled,
620 bal_parallel_state_root_disabled,
621 disable_bal_batch_io: false,
622 #[cfg(feature = "trie-debug")]
623 proof_jitter: None,
624 }
625 }
626}
627
628impl EngineArgs {
629 pub fn persistence_backpressure_threshold(&self) -> u64 {
631 self.persistence_backpressure_threshold.unwrap_or_else(|| {
632 default_persistence_backpressure_threshold(self.persistence_threshold)
633 })
634 }
635
636 pub fn validate(&self) -> eyre::Result<()> {
638 let persistence_backpressure_threshold = self.persistence_backpressure_threshold();
639 ensure!(
640 persistence_backpressure_threshold > self.persistence_threshold,
641 "--engine.persistence-backpressure-threshold ({}) must be greater than --engine.persistence-threshold ({})",
642 persistence_backpressure_threshold,
643 self.persistence_threshold
644 );
645 ensure!(
646 self.bal_parallel_execution_disabled || !self.bal_parallel_state_root_disabled,
647 "--engine.disable-bal-parallel-state-root requires --engine.disable-bal-parallel-execution because BAL parallel execution depends on BAL prewarm state-root updates"
648 );
649 Ok(())
650 }
651
652 pub fn tree_config(&self) -> TreeConfig {
654 #[allow(deprecated)]
655 if self.legacy_state_root_task_enabled {
656 tracing::warn!(target: "reth::cli", "--engine.legacy-state-root has no effect anymore, use --engine.state-root-fallback to force synchronous state root computation");
657 }
658 let config = TreeConfig::default()
659 .with_persistence_backpressure_threshold(self.persistence_backpressure_threshold())
660 .with_persistence_threshold(self.persistence_threshold)
661 .with_memory_block_buffer_target(self.memory_block_buffer_target)
662 .with_invalid_header_hit_eviction_threshold(self.invalid_header_hit_eviction_threshold)
663 .without_state_cache(self.state_cache_disabled)
664 .without_prewarming(self.prewarming_disabled)
665 .with_state_provider_metrics(self.state_provider_metrics)
666 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
667 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
668 .with_multiproof_chunk_size(self.multiproof_chunk_size)
669 .with_reserved_cpu_cores(self.reserved_cpu_cores)
670 .without_precompile_cache(self.precompile_cache_disabled)
671 .with_state_root_fallback(self.state_root_fallback)
672 .with_always_process_payload_attributes_on_canonical_head(
673 self.always_process_payload_attributes_on_canonical_head,
674 )
675 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
676 .without_cache_metrics(self.cache_metrics_disabled)
677 .with_sparse_trie_max_hot_slots(self.sparse_trie_max_hot_slots)
678 .with_sparse_trie_max_hot_accounts(self.sparse_trie_max_hot_accounts)
679 .with_slow_block_threshold(self.slow_block_threshold)
680 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
681 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
682 .with_share_execution_cache_with_payload_builder(
683 self.share_execution_cache_with_payload_builder,
684 )
685 .with_share_sparse_trie_with_payload_builder(
686 self.share_sparse_trie_with_payload_builder,
687 )
688 .with_suppress_persistence_during_build(self.suppress_persistence_during_build)
689 .without_bal_parallel_execution(self.bal_parallel_execution_disabled)
690 .without_bal_parallel_state_root(self.bal_parallel_state_root_disabled)
691 .without_bal_batch_io(self.disable_bal_batch_io);
692 #[cfg(feature = "trie-debug")]
693 let config = config.with_proof_jitter(self.proof_jitter);
694 config
695 }
696}
697
698#[cfg(test)]
699mod tests {
700 use super::*;
701 use clap::Parser;
702
703 #[derive(Parser)]
705 struct CommandParser<T: Args> {
706 #[command(flatten)]
707 args: T,
708 }
709
710 #[test]
711 fn test_parse_engine_args() {
712 let default_args = EngineArgs::default();
713 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
714 assert_eq!(args, default_args);
715 assert_eq!(
716 args.persistence_backpressure_threshold(),
717 DefaultEngineValues::get_global().persistence_backpressure_threshold
718 );
719 }
720
721 #[test]
722 fn default_backpressure_threshold_uses_parsed_persistence_args() {
723 let args = CommandParser::<EngineArgs>::parse_from([
724 "reth",
725 "--engine.persistence-threshold",
726 "100",
727 "--engine.memory-block-buffer-target",
728 "50",
729 ])
730 .args;
731
732 assert_eq!(args.persistence_backpressure_threshold(), 200);
733
734 let tree_config = args.tree_config();
735 assert_eq!(tree_config.persistence_threshold(), 100);
736 assert_eq!(tree_config.memory_block_buffer_target(), 50);
737 assert_eq!(tree_config.persistence_backpressure_threshold(), 200);
738 }
739
740 #[test]
741 fn default_backpressure_threshold_uses_global_default_when_larger() {
742 let args = CommandParser::<EngineArgs>::parse_from([
743 "reth",
744 "--engine.persistence-threshold",
745 "4",
746 ])
747 .args;
748
749 assert_eq!(
750 args.persistence_backpressure_threshold(),
751 DefaultEngineValues::get_global().persistence_backpressure_threshold
752 );
753 }
754
755 #[test]
756 fn explicit_backpressure_threshold_overrides_calculated_default() {
757 let args = CommandParser::<EngineArgs>::parse_from([
758 "reth",
759 "--engine.persistence-threshold",
760 "100",
761 "--engine.memory-block-buffer-target",
762 "50",
763 "--engine.persistence-backpressure-threshold",
764 "101",
765 ])
766 .args;
767
768 assert_eq!(args.persistence_backpressure_threshold(), 101);
769 }
770
771 #[test]
772 #[allow(deprecated)]
773 fn engine_args() {
774 let args = EngineArgs {
775 persistence_threshold: 100,
776 persistence_backpressure_threshold: Some(101),
777 memory_block_buffer_target: 50,
778 invalid_header_hit_eviction_threshold: 7,
779 legacy_state_root_task_enabled: true,
780 caching_and_prewarming_enabled: true,
781 state_cache_disabled: true,
782 prewarming_disabled: true,
783 parallel_sparse_trie_enabled: true,
784 parallel_sparse_trie_disabled: false,
785 state_provider_metrics: true,
786 cross_block_cache_size: 256,
787 state_root_task_compare_updates: true,
788 accept_execution_requests_hash: true,
789 multiproof_chunk_size: 512,
790 reserved_cpu_cores: 4,
791 precompile_cache_enabled: true,
792 precompile_cache_disabled: true,
793 state_root_fallback: true,
794 always_process_payload_attributes_on_canonical_head: true,
795 allow_unwind_canonical_header: true,
796 storage_worker_count: Some(16),
797 account_worker_count: Some(8),
798 prewarming_threads: Some(4),
799 cache_metrics_disabled: true,
800 sparse_trie_max_hot_slots: 100,
801 sparse_trie_max_hot_accounts: 500,
802 slow_block_threshold: None,
803 disable_sparse_trie_cache_pruning: true,
804 state_root_task_timeout: Some(Duration::from_secs(2)),
805 share_execution_cache_with_payload_builder: false,
806 share_sparse_trie_with_payload_builder: false,
807 suppress_persistence_during_build: false,
808 bal_parallel_execution_disabled: true,
809 bal_parallel_state_root_disabled: true,
810 disable_bal_batch_io: true,
811 #[cfg(feature = "trie-debug")]
812 proof_jitter: None,
813 };
814
815 let parsed_args = CommandParser::<EngineArgs>::parse_from([
816 "reth",
817 "--engine.persistence-threshold",
818 "100",
819 "--engine.persistence-backpressure-threshold",
820 "101",
821 "--engine.memory-block-buffer-target",
822 "50",
823 "--engine.invalid-header-cache-hit-eviction-threshold",
824 "7",
825 "--engine.legacy-state-root",
826 "--engine.disable-state-cache",
827 "--engine.disable-prewarming",
828 "--engine.state-provider-metrics",
829 "--engine.cross-block-cache-size",
830 "256",
831 "--engine.state-root-task-compare-updates",
832 "--engine.accept-execution-requests-hash",
833 "--engine.multiproof-chunk-size",
834 "512",
835 "--engine.reserved-cpu-cores",
836 "4",
837 "--engine.disable-precompile-cache",
838 "--engine.state-root-fallback",
839 "--engine.always-process-payload-attributes-on-canonical-head",
840 "--engine.allow-unwind-canonical-header",
841 "--engine.storage-worker-count",
842 "16",
843 "--engine.account-worker-count",
844 "8",
845 "--engine.prewarming-threads",
846 "4",
847 "--engine.disable-cache-metrics",
848 "--engine.sparse-trie-max-hot-slots",
849 "100",
850 "--engine.sparse-trie-max-hot-accounts",
851 "500",
852 "--engine.disable-sparse-trie-cache-pruning",
853 "--engine.state-root-task-timeout",
854 "2s",
855 "--engine.disable-bal-parallel-execution",
856 "--engine.disable-bal-parallel-state-root",
857 "--engine.disable-bal-batch-io",
858 ])
859 .args;
860
861 assert_eq!(parsed_args, args);
862 }
863
864 #[test]
865 fn validate_rejects_invalid_backpressure_threshold() {
866 let args = EngineArgs {
867 persistence_threshold: 4,
868 persistence_backpressure_threshold: Some(4),
869 ..EngineArgs::default()
870 };
871
872 let err = args.validate().unwrap_err().to_string();
873 assert!(err.contains("engine.persistence-backpressure-threshold"));
874 assert!(err.contains("engine.persistence-threshold"));
875 }
876
877 #[test]
878 fn parse_rejects_zero_multiproof_chunk_size() {
879 let result = CommandParser::<EngineArgs>::try_parse_from([
880 "reth",
881 "--engine.multiproof-chunk-size",
882 "0",
883 ]);
884
885 assert!(result.is_err());
886 }
887
888 #[test]
889 fn validate_rejects_bal_parallel_execution_without_bal_parallel_state_root() {
890 let args = EngineArgs {
891 bal_parallel_execution_disabled: false,
892 bal_parallel_state_root_disabled: true,
893 ..EngineArgs::default()
894 };
895
896 let err = args.validate().unwrap_err().to_string();
897 assert!(err.contains("engine.disable-bal-parallel-state-root"));
898 assert!(err.contains("engine.disable-bal-parallel-execution"));
899 }
900
901 #[test]
902 fn test_parse_slow_block_threshold() {
903 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
905 assert_eq!(args.slow_block_threshold, None);
906
907 let args =
909 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
910 .args;
911 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
912
913 let args = CommandParser::<EngineArgs>::parse_from([
915 "reth",
916 "--engine.slow-block-threshold",
917 "500",
918 ])
919 .args;
920 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
921
922 let args = CommandParser::<EngineArgs>::parse_from([
923 "reth",
924 "--engine.slow-block-threshold",
925 "500ms",
926 ])
927 .args;
928 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
929 }
930
931 #[test]
932 fn test_parse_invalid_header_hit_eviction_threshold() {
933 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
934 assert_eq!(
935 args.invalid_header_hit_eviction_threshold,
936 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
937 );
938 assert_eq!(
939 args.tree_config().invalid_header_hit_eviction_threshold(),
940 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
941 );
942
943 let args = CommandParser::<EngineArgs>::parse_from([
944 "reth",
945 "--engine.invalid-header-cache-hit-eviction-threshold",
946 "0",
947 ])
948 .args;
949 assert_eq!(args.invalid_header_hit_eviction_threshold, 0);
950 assert_eq!(args.tree_config().invalid_header_hit_eviction_threshold(), 0);
951 }
952
953 #[test]
954 fn test_parse_share_sparse_trie_flag() {
955 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
956 assert!(!args.share_sparse_trie_with_payload_builder);
957 assert!(!args.tree_config().share_sparse_trie_with_payload_builder());
958
959 let args = CommandParser::<EngineArgs>::parse_from([
960 "reth",
961 "--engine.share-sparse-trie-with-payload-builder",
962 ])
963 .args;
964 assert!(args.share_sparse_trie_with_payload_builder);
965 assert!(args.tree_config().share_sparse_trie_with_payload_builder());
966 }
967}