1use clap::{builder::Resettable, Args};
4use eyre::ensure;
5use reth_cli_util::{parse_duration_from_secs_or_ms, parsers::format_duration_as_secs_or_ms};
6use reth_engine_primitives::{
7 TreeConfig, DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
8 DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD, DEFAULT_SPARSE_TRIE_MAX_HOT_ACCOUNTS,
9 DEFAULT_SPARSE_TRIE_MAX_HOT_SLOTS,
10};
11use std::{sync::OnceLock, time::Duration};
12
13use crate::node_config::{
14 DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
15 DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
16};
17
18static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
20
21#[derive(Debug, Clone)]
25pub struct DefaultEngineValues {
26 persistence_threshold: u64,
27 persistence_backpressure_threshold: u64,
28 memory_block_buffer_target: u64,
29 invalid_header_hit_eviction_threshold: u8,
30 legacy_state_root_task_enabled: bool,
31 state_cache_disabled: bool,
32 prewarming_disabled: bool,
33 state_provider_metrics: bool,
34 cross_block_cache_size: usize,
35 state_root_task_compare_updates: bool,
36 accept_execution_requests_hash: bool,
37 multiproof_chunk_size: usize,
38 reserved_cpu_cores: usize,
39 precompile_cache_disabled: bool,
40 state_root_fallback: bool,
41 always_process_payload_attributes_on_canonical_head: bool,
42 allow_unwind_canonical_header: bool,
43 storage_worker_count: Option<usize>,
44 account_worker_count: Option<usize>,
45 prewarming_threads: Option<usize>,
46 cache_metrics_disabled: bool,
47 sparse_trie_max_hot_slots: usize,
48 sparse_trie_max_hot_accounts: usize,
49 slow_block_threshold: Option<Duration>,
50 disable_sparse_trie_cache_pruning: bool,
51 state_root_task_timeout: Option<String>,
52 share_execution_cache_with_payload_builder: bool,
53 share_sparse_trie_with_payload_builder: bool,
54 suppress_persistence_during_build: bool,
55 bal_parallel_execution_disabled: bool,
56 bal_parallel_state_root_disabled: bool,
57}
58
59impl DefaultEngineValues {
60 pub fn try_init(self) -> Result<(), Self> {
62 ENGINE_DEFAULTS.set(self)
63 }
64
65 pub fn get_global() -> &'static Self {
67 ENGINE_DEFAULTS.get_or_init(Self::default)
68 }
69
70 pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
72 self.persistence_threshold = v;
73 self
74 }
75
76 pub const fn with_persistence_backpressure_threshold(mut self, v: u64) -> Self {
78 self.persistence_backpressure_threshold = v;
79 self
80 }
81
82 pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
84 self.memory_block_buffer_target = v;
85 self
86 }
87
88 pub const fn with_invalid_header_hit_eviction_threshold(mut self, v: u8) -> Self {
90 self.invalid_header_hit_eviction_threshold = v;
91 self
92 }
93
94 pub const fn with_legacy_state_root_task_enabled(mut self, v: bool) -> Self {
96 self.legacy_state_root_task_enabled = v;
97 self
98 }
99
100 pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
102 self.state_cache_disabled = v;
103 self
104 }
105
106 pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
108 self.prewarming_disabled = v;
109 self
110 }
111
112 pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
114 self.state_provider_metrics = v;
115 self
116 }
117
118 pub const fn with_cross_block_cache_size(mut self, v: usize) -> Self {
120 self.cross_block_cache_size = v;
121 self
122 }
123
124 pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
126 self.state_root_task_compare_updates = v;
127 self
128 }
129
130 pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
132 self.accept_execution_requests_hash = v;
133 self
134 }
135
136 pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
138 self.multiproof_chunk_size = v;
139 self
140 }
141
142 pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
144 self.reserved_cpu_cores = v;
145 self
146 }
147
148 pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
150 self.precompile_cache_disabled = v;
151 self
152 }
153
154 pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
156 self.state_root_fallback = v;
157 self
158 }
159
160 pub const fn with_always_process_payload_attributes_on_canonical_head(
162 mut self,
163 v: bool,
164 ) -> Self {
165 self.always_process_payload_attributes_on_canonical_head = v;
166 self
167 }
168
169 pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
171 self.allow_unwind_canonical_header = v;
172 self
173 }
174
175 pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
177 self.storage_worker_count = v;
178 self
179 }
180
181 pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
183 self.account_worker_count = v;
184 self
185 }
186
187 pub const fn with_prewarming_threads(mut self, v: Option<usize>) -> Self {
189 self.prewarming_threads = v;
190 self
191 }
192
193 pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
195 self.cache_metrics_disabled = v;
196 self
197 }
198
199 pub const fn with_sparse_trie_max_hot_slots(mut self, v: usize) -> Self {
201 self.sparse_trie_max_hot_slots = v;
202 self
203 }
204
205 pub const fn with_sparse_trie_max_hot_accounts(mut self, v: usize) -> Self {
207 self.sparse_trie_max_hot_accounts = v;
208 self
209 }
210
211 pub const fn with_slow_block_threshold(mut self, v: Option<Duration>) -> Self {
213 self.slow_block_threshold = v;
214 self
215 }
216
217 pub const fn with_disable_sparse_trie_cache_pruning(mut self, v: bool) -> Self {
219 self.disable_sparse_trie_cache_pruning = v;
220 self
221 }
222
223 pub fn with_state_root_task_timeout(mut self, v: Option<String>) -> Self {
225 self.state_root_task_timeout = v;
226 self
227 }
228
229 pub const fn with_share_execution_cache_with_payload_builder(mut self, v: bool) -> Self {
231 self.share_execution_cache_with_payload_builder = v;
232 self
233 }
234
235 pub const fn with_share_sparse_trie_with_payload_builder(mut self, v: bool) -> Self {
237 self.share_sparse_trie_with_payload_builder = v;
238 self
239 }
240
241 pub const fn with_suppress_persistence_during_build(mut self, v: bool) -> Self {
243 self.suppress_persistence_during_build = v;
244 self
245 }
246
247 pub const fn with_bal_parallel_execution_disabled(mut self, v: bool) -> Self {
249 self.bal_parallel_execution_disabled = v;
250 self
251 }
252
253 pub const fn with_bal_parallel_state_root_disabled(mut self, v: bool) -> Self {
255 self.bal_parallel_state_root_disabled = v;
256 self
257 }
258}
259
260impl Default for DefaultEngineValues {
261 fn default() -> Self {
262 Self {
263 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
264 persistence_backpressure_threshold: DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
265 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
266 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
267 legacy_state_root_task_enabled: false,
268 state_cache_disabled: false,
269 prewarming_disabled: false,
270 state_provider_metrics: false,
271 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
272 state_root_task_compare_updates: false,
273 accept_execution_requests_hash: false,
274 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
275 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
276 precompile_cache_disabled: false,
277 state_root_fallback: false,
278 always_process_payload_attributes_on_canonical_head: false,
279 allow_unwind_canonical_header: false,
280 storage_worker_count: None,
281 account_worker_count: None,
282 prewarming_threads: None,
283 cache_metrics_disabled: false,
284 sparse_trie_max_hot_slots: DEFAULT_SPARSE_TRIE_MAX_HOT_SLOTS,
285 sparse_trie_max_hot_accounts: DEFAULT_SPARSE_TRIE_MAX_HOT_ACCOUNTS,
286 slow_block_threshold: None,
287 disable_sparse_trie_cache_pruning: false,
288 state_root_task_timeout: Some("4s".to_string()),
289 share_execution_cache_with_payload_builder: false,
290 share_sparse_trie_with_payload_builder: false,
291 suppress_persistence_during_build: false,
292 bal_parallel_execution_disabled: false,
293 bal_parallel_state_root_disabled: false,
294 }
295 }
296}
297
298#[derive(Debug, Clone, Args, PartialEq, Eq)]
300#[command(next_help_heading = "Engine")]
301pub struct EngineArgs {
302 #[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
309 pub persistence_threshold: u64,
310
311 #[arg(long = "engine.persistence-backpressure-threshold", default_value_t = DefaultEngineValues::get_global().persistence_backpressure_threshold)]
315 pub persistence_backpressure_threshold: u64,
316
317 #[arg(long = "engine.memory-block-buffer-target", default_value_t = DefaultEngineValues::get_global().memory_block_buffer_target)]
319 pub memory_block_buffer_target: u64,
320
321 #[arg(long = "engine.invalid-header-cache-hit-eviction-threshold", default_value_t = DefaultEngineValues::get_global().invalid_header_hit_eviction_threshold)]
327 pub invalid_header_hit_eviction_threshold: u8,
328
329 #[arg(long = "engine.legacy-state-root", default_value_t = DefaultEngineValues::get_global().legacy_state_root_task_enabled)]
331 pub legacy_state_root_task_enabled: bool,
332
333 #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
336 #[deprecated]
337 pub caching_and_prewarming_enabled: bool,
338
339 #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
341 pub state_cache_disabled: bool,
342
343 #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
345 pub prewarming_disabled: bool,
346
347 #[deprecated]
349 #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
350 pub parallel_sparse_trie_enabled: bool,
351
352 #[deprecated]
354 #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
355 pub parallel_sparse_trie_disabled: bool,
356
357 #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
361 pub state_provider_metrics: bool,
362
363 #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
365 pub cross_block_cache_size: usize,
366
367 #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
370 pub state_root_task_compare_updates: bool,
371
372 #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
374 pub accept_execution_requests_hash: bool,
375
376 #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size)]
378 pub multiproof_chunk_size: usize,
379
380 #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
382 pub reserved_cpu_cores: usize,
383
384 #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
387 #[deprecated]
388 pub precompile_cache_enabled: bool,
389
390 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
392 pub precompile_cache_disabled: bool,
393
394 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
396 pub state_root_fallback: bool,
397
398 #[arg(
404 long = "engine.always-process-payload-attributes-on-canonical-head",
405 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
406 )]
407 pub always_process_payload_attributes_on_canonical_head: bool,
408
409 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
412 pub allow_unwind_canonical_header: bool,
413
414 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
417 pub storage_worker_count: Option<usize>,
418
419 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
422 pub account_worker_count: Option<usize>,
423
424 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
427 pub prewarming_threads: Option<usize>,
428
429 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
431 pub cache_metrics_disabled: bool,
432
433 #[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)]
435 pub sparse_trie_max_hot_slots: usize,
436
437 #[arg(long = "engine.sparse-trie-max-hot-accounts", default_value_t = DefaultEngineValues::get_global().sparse_trie_max_hot_accounts)]
439 pub sparse_trie_max_hot_accounts: usize,
440
441 #[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())))]
450 pub slow_block_threshold: Option<Duration>,
451
452 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
456 pub disable_sparse_trie_cache_pruning: bool,
457
458 #[arg(
467 long = "engine.state-root-task-timeout",
468 value_parser = humantime::parse_duration,
469 default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("4s"),
470 )]
471 pub state_root_task_timeout: Option<Duration>,
472
473 #[arg(
481 long = "engine.share-execution-cache-with-payload-builder",
482 default_value_t = DefaultEngineValues::get_global().share_execution_cache_with_payload_builder,
483 )]
484 pub share_execution_cache_with_payload_builder: bool,
485
486 #[arg(
498 long = "engine.share-sparse-trie-with-payload-builder",
499 default_value_t = DefaultEngineValues::get_global().share_sparse_trie_with_payload_builder,
500 )]
501 pub share_sparse_trie_with_payload_builder: bool,
502
503 #[arg(
509 long = "engine.suppress-persistence-during-build",
510 default_value_t = DefaultEngineValues::get_global().suppress_persistence_during_build,
511 )]
512 pub suppress_persistence_during_build: bool,
513
514 #[arg(long = "engine.disable-bal-parallel-execution", default_value_t = DefaultEngineValues::get_global().bal_parallel_execution_disabled)]
516 pub bal_parallel_execution_disabled: bool,
517
518 #[arg(long = "engine.disable-bal-parallel-state-root", default_value_t = DefaultEngineValues::get_global().bal_parallel_state_root_disabled)]
521 pub bal_parallel_state_root_disabled: bool,
522
523 #[arg(long = "engine.disable-bal-batch-io", default_value_t = false)]
526 pub disable_bal_batch_io: bool,
527
528 #[cfg(feature = "trie-debug")]
535 #[arg(
536 long = "engine.proof-jitter",
537 value_parser = humantime::parse_duration,
538 )]
539 pub proof_jitter: Option<Duration>,
540}
541
542#[allow(deprecated)]
543impl Default for EngineArgs {
544 fn default() -> Self {
545 let DefaultEngineValues {
546 persistence_threshold,
547 persistence_backpressure_threshold,
548 memory_block_buffer_target,
549 invalid_header_hit_eviction_threshold,
550 legacy_state_root_task_enabled,
551 state_cache_disabled,
552 prewarming_disabled,
553 state_provider_metrics,
554 cross_block_cache_size,
555 state_root_task_compare_updates,
556 accept_execution_requests_hash,
557 multiproof_chunk_size,
558 reserved_cpu_cores,
559 precompile_cache_disabled,
560 state_root_fallback,
561 always_process_payload_attributes_on_canonical_head,
562 allow_unwind_canonical_header,
563 storage_worker_count,
564 account_worker_count,
565 prewarming_threads,
566 cache_metrics_disabled,
567 sparse_trie_max_hot_slots,
568 sparse_trie_max_hot_accounts,
569 slow_block_threshold,
570 disable_sparse_trie_cache_pruning,
571 state_root_task_timeout,
572 share_execution_cache_with_payload_builder,
573 share_sparse_trie_with_payload_builder,
574 suppress_persistence_during_build,
575 bal_parallel_execution_disabled,
576 bal_parallel_state_root_disabled,
577 } = DefaultEngineValues::get_global().clone();
578 Self {
579 persistence_threshold,
580 persistence_backpressure_threshold,
581 memory_block_buffer_target,
582 invalid_header_hit_eviction_threshold,
583 legacy_state_root_task_enabled,
584 state_root_task_compare_updates,
585 caching_and_prewarming_enabled: true,
586 state_cache_disabled,
587 prewarming_disabled,
588 parallel_sparse_trie_enabled: true,
589 parallel_sparse_trie_disabled: false,
590 state_provider_metrics,
591 cross_block_cache_size,
592 accept_execution_requests_hash,
593 multiproof_chunk_size,
594 reserved_cpu_cores,
595 precompile_cache_enabled: true,
596 precompile_cache_disabled,
597 state_root_fallback,
598 always_process_payload_attributes_on_canonical_head,
599 allow_unwind_canonical_header,
600 storage_worker_count,
601 account_worker_count,
602 prewarming_threads,
603 cache_metrics_disabled,
604 sparse_trie_max_hot_slots,
605 sparse_trie_max_hot_accounts,
606 slow_block_threshold,
607 disable_sparse_trie_cache_pruning,
608 state_root_task_timeout: state_root_task_timeout
609 .as_deref()
610 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
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 disable_bal_batch_io: false,
617 #[cfg(feature = "trie-debug")]
618 proof_jitter: None,
619 }
620 }
621}
622
623impl EngineArgs {
624 pub fn validate(&self) -> eyre::Result<()> {
626 ensure!(
627 self.persistence_backpressure_threshold > self.persistence_threshold,
628 "--engine.persistence-backpressure-threshold ({}) must be greater than --engine.persistence-threshold ({})",
629 self.persistence_backpressure_threshold,
630 self.persistence_threshold
631 );
632 ensure!(
633 self.bal_parallel_execution_disabled || !self.bal_parallel_state_root_disabled,
634 "--engine.disable-bal-parallel-state-root requires --engine.disable-bal-parallel-execution because BAL parallel execution depends on BAL prewarm state-root updates"
635 );
636 Ok(())
637 }
638
639 pub fn tree_config(&self) -> TreeConfig {
641 let config = TreeConfig::default()
642 .with_persistence_threshold(self.persistence_threshold)
643 .with_persistence_backpressure_threshold(self.persistence_backpressure_threshold)
644 .with_memory_block_buffer_target(self.memory_block_buffer_target)
645 .with_invalid_header_hit_eviction_threshold(self.invalid_header_hit_eviction_threshold)
646 .with_legacy_state_root(self.legacy_state_root_task_enabled)
647 .without_state_cache(self.state_cache_disabled)
648 .without_prewarming(self.prewarming_disabled)
649 .with_state_provider_metrics(self.state_provider_metrics)
650 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
651 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
652 .with_multiproof_chunk_size(self.multiproof_chunk_size)
653 .with_reserved_cpu_cores(self.reserved_cpu_cores)
654 .without_precompile_cache(self.precompile_cache_disabled)
655 .with_state_root_fallback(self.state_root_fallback)
656 .with_always_process_payload_attributes_on_canonical_head(
657 self.always_process_payload_attributes_on_canonical_head,
658 )
659 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
660 .without_cache_metrics(self.cache_metrics_disabled)
661 .with_sparse_trie_max_hot_slots(self.sparse_trie_max_hot_slots)
662 .with_sparse_trie_max_hot_accounts(self.sparse_trie_max_hot_accounts)
663 .with_slow_block_threshold(self.slow_block_threshold)
664 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
665 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
666 .with_share_execution_cache_with_payload_builder(
667 self.share_execution_cache_with_payload_builder,
668 )
669 .with_share_sparse_trie_with_payload_builder(
670 self.share_sparse_trie_with_payload_builder,
671 )
672 .with_suppress_persistence_during_build(self.suppress_persistence_during_build)
673 .without_bal_parallel_execution(self.bal_parallel_execution_disabled)
674 .without_bal_parallel_state_root(self.bal_parallel_state_root_disabled)
675 .without_bal_batch_io(self.disable_bal_batch_io);
676 #[cfg(feature = "trie-debug")]
677 let config = config.with_proof_jitter(self.proof_jitter);
678 config
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685 use clap::Parser;
686
687 #[derive(Parser)]
689 struct CommandParser<T: Args> {
690 #[command(flatten)]
691 args: T,
692 }
693
694 #[test]
695 fn test_parse_engine_args() {
696 let default_args = EngineArgs::default();
697 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
698 assert_eq!(args, default_args);
699 }
700
701 #[test]
702 #[allow(deprecated)]
703 fn engine_args() {
704 let args = EngineArgs {
705 persistence_threshold: 100,
706 persistence_backpressure_threshold: 101,
707 memory_block_buffer_target: 50,
708 invalid_header_hit_eviction_threshold: 7,
709 legacy_state_root_task_enabled: true,
710 caching_and_prewarming_enabled: true,
711 state_cache_disabled: true,
712 prewarming_disabled: true,
713 parallel_sparse_trie_enabled: true,
714 parallel_sparse_trie_disabled: false,
715 state_provider_metrics: true,
716 cross_block_cache_size: 256,
717 state_root_task_compare_updates: true,
718 accept_execution_requests_hash: true,
719 multiproof_chunk_size: 512,
720 reserved_cpu_cores: 4,
721 precompile_cache_enabled: true,
722 precompile_cache_disabled: true,
723 state_root_fallback: true,
724 always_process_payload_attributes_on_canonical_head: true,
725 allow_unwind_canonical_header: true,
726 storage_worker_count: Some(16),
727 account_worker_count: Some(8),
728 prewarming_threads: Some(4),
729 cache_metrics_disabled: true,
730 sparse_trie_max_hot_slots: 100,
731 sparse_trie_max_hot_accounts: 500,
732 slow_block_threshold: None,
733 disable_sparse_trie_cache_pruning: true,
734 state_root_task_timeout: Some(Duration::from_secs(2)),
735 share_execution_cache_with_payload_builder: false,
736 share_sparse_trie_with_payload_builder: false,
737 suppress_persistence_during_build: false,
738 bal_parallel_execution_disabled: true,
739 bal_parallel_state_root_disabled: true,
740 disable_bal_batch_io: true,
741 #[cfg(feature = "trie-debug")]
742 proof_jitter: None,
743 };
744
745 let parsed_args = CommandParser::<EngineArgs>::parse_from([
746 "reth",
747 "--engine.persistence-threshold",
748 "100",
749 "--engine.persistence-backpressure-threshold",
750 "101",
751 "--engine.memory-block-buffer-target",
752 "50",
753 "--engine.invalid-header-cache-hit-eviction-threshold",
754 "7",
755 "--engine.legacy-state-root",
756 "--engine.disable-state-cache",
757 "--engine.disable-prewarming",
758 "--engine.state-provider-metrics",
759 "--engine.cross-block-cache-size",
760 "256",
761 "--engine.state-root-task-compare-updates",
762 "--engine.accept-execution-requests-hash",
763 "--engine.multiproof-chunk-size",
764 "512",
765 "--engine.reserved-cpu-cores",
766 "4",
767 "--engine.disable-precompile-cache",
768 "--engine.state-root-fallback",
769 "--engine.always-process-payload-attributes-on-canonical-head",
770 "--engine.allow-unwind-canonical-header",
771 "--engine.storage-worker-count",
772 "16",
773 "--engine.account-worker-count",
774 "8",
775 "--engine.prewarming-threads",
776 "4",
777 "--engine.disable-cache-metrics",
778 "--engine.sparse-trie-max-hot-slots",
779 "100",
780 "--engine.sparse-trie-max-hot-accounts",
781 "500",
782 "--engine.disable-sparse-trie-cache-pruning",
783 "--engine.state-root-task-timeout",
784 "2s",
785 "--engine.disable-bal-parallel-execution",
786 "--engine.disable-bal-parallel-state-root",
787 "--engine.disable-bal-batch-io",
788 ])
789 .args;
790
791 assert_eq!(parsed_args, args);
792 }
793
794 #[test]
795 fn validate_rejects_invalid_backpressure_threshold() {
796 let args = EngineArgs {
797 persistence_threshold: 4,
798 persistence_backpressure_threshold: 4,
799 ..EngineArgs::default()
800 };
801
802 let err = args.validate().unwrap_err().to_string();
803 assert!(err.contains("engine.persistence-backpressure-threshold"));
804 assert!(err.contains("engine.persistence-threshold"));
805 }
806
807 #[test]
808 fn validate_rejects_bal_parallel_execution_without_bal_parallel_state_root() {
809 let args = EngineArgs {
810 bal_parallel_execution_disabled: false,
811 bal_parallel_state_root_disabled: true,
812 ..EngineArgs::default()
813 };
814
815 let err = args.validate().unwrap_err().to_string();
816 assert!(err.contains("engine.disable-bal-parallel-state-root"));
817 assert!(err.contains("engine.disable-bal-parallel-execution"));
818 }
819
820 #[test]
821 fn test_parse_slow_block_threshold() {
822 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
824 assert_eq!(args.slow_block_threshold, None);
825
826 let args =
828 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
829 .args;
830 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
831
832 let args = CommandParser::<EngineArgs>::parse_from([
834 "reth",
835 "--engine.slow-block-threshold",
836 "500",
837 ])
838 .args;
839 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
840
841 let args = CommandParser::<EngineArgs>::parse_from([
842 "reth",
843 "--engine.slow-block-threshold",
844 "500ms",
845 ])
846 .args;
847 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
848 }
849
850 #[test]
851 fn test_parse_invalid_header_hit_eviction_threshold() {
852 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
853 assert_eq!(
854 args.invalid_header_hit_eviction_threshold,
855 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
856 );
857 assert_eq!(
858 args.tree_config().invalid_header_hit_eviction_threshold(),
859 DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD
860 );
861
862 let args = CommandParser::<EngineArgs>::parse_from([
863 "reth",
864 "--engine.invalid-header-cache-hit-eviction-threshold",
865 "0",
866 ])
867 .args;
868 assert_eq!(args.invalid_header_hit_eviction_threshold, 0);
869 assert_eq!(args.tree_config().invalid_header_hit_eviction_threshold(), 0);
870 }
871
872 #[test]
873 fn test_parse_share_sparse_trie_flag() {
874 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
875 assert!(!args.share_sparse_trie_with_payload_builder);
876 assert!(!args.tree_config().share_sparse_trie_with_payload_builder());
877
878 let args = CommandParser::<EngineArgs>::parse_from([
879 "reth",
880 "--engine.share-sparse-trie-with-payload-builder",
881 ])
882 .args;
883 assert!(args.share_sparse_trie_with_payload_builder);
884 assert!(args.tree_config().share_sparse_trie_with_payload_builder());
885 }
886}