1use clap::{builder::Resettable, Args};
4use reth_cli_util::{parse_duration_from_secs_or_ms, parsers::format_duration_as_secs_or_ms};
5use reth_engine_primitives::{
6 TreeConfig, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE, DEFAULT_SPARSE_TRIE_MAX_HOT_ACCOUNTS,
7 DEFAULT_SPARSE_TRIE_MAX_HOT_SLOTS,
8};
9use std::{sync::OnceLock, time::Duration};
10
11use crate::node_config::{
12 DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
13 DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
14};
15
16static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
18
19#[derive(Debug, Clone)]
23pub struct DefaultEngineValues {
24 persistence_threshold: u64,
25 memory_block_buffer_target: u64,
26 legacy_state_root_task_enabled: bool,
27 state_cache_disabled: bool,
28 prewarming_disabled: bool,
29 state_provider_metrics: bool,
30 cross_block_cache_size: usize,
31 state_root_task_compare_updates: bool,
32 accept_execution_requests_hash: bool,
33 multiproof_chunk_size: usize,
34 reserved_cpu_cores: usize,
35 precompile_cache_disabled: bool,
36 state_root_fallback: bool,
37 always_process_payload_attributes_on_canonical_head: bool,
38 allow_unwind_canonical_header: bool,
39 storage_worker_count: Option<usize>,
40 account_worker_count: Option<usize>,
41 prewarming_threads: Option<usize>,
42 cache_metrics_disabled: bool,
43 sparse_trie_max_hot_slots: usize,
44 sparse_trie_max_hot_accounts: usize,
45 slow_block_threshold: Option<Duration>,
46 disable_sparse_trie_cache_pruning: bool,
47 state_root_task_timeout: Option<String>,
48}
49
50impl DefaultEngineValues {
51 pub fn try_init(self) -> Result<(), Self> {
53 ENGINE_DEFAULTS.set(self)
54 }
55
56 pub fn get_global() -> &'static Self {
58 ENGINE_DEFAULTS.get_or_init(Self::default)
59 }
60
61 pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
63 self.persistence_threshold = v;
64 self
65 }
66
67 pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
69 self.memory_block_buffer_target = v;
70 self
71 }
72
73 pub const fn with_legacy_state_root_task_enabled(mut self, v: bool) -> Self {
75 self.legacy_state_root_task_enabled = v;
76 self
77 }
78
79 pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
81 self.state_cache_disabled = v;
82 self
83 }
84
85 pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
87 self.prewarming_disabled = v;
88 self
89 }
90
91 pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
93 self.state_provider_metrics = v;
94 self
95 }
96
97 pub const fn with_cross_block_cache_size(mut self, v: usize) -> Self {
99 self.cross_block_cache_size = v;
100 self
101 }
102
103 pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
105 self.state_root_task_compare_updates = v;
106 self
107 }
108
109 pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
111 self.accept_execution_requests_hash = v;
112 self
113 }
114
115 pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
117 self.multiproof_chunk_size = v;
118 self
119 }
120
121 pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
123 self.reserved_cpu_cores = v;
124 self
125 }
126
127 pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
129 self.precompile_cache_disabled = v;
130 self
131 }
132
133 pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
135 self.state_root_fallback = v;
136 self
137 }
138
139 pub const fn with_always_process_payload_attributes_on_canonical_head(
141 mut self,
142 v: bool,
143 ) -> Self {
144 self.always_process_payload_attributes_on_canonical_head = v;
145 self
146 }
147
148 pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
150 self.allow_unwind_canonical_header = v;
151 self
152 }
153
154 pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
156 self.storage_worker_count = v;
157 self
158 }
159
160 pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
162 self.account_worker_count = v;
163 self
164 }
165
166 pub const fn with_prewarming_threads(mut self, v: Option<usize>) -> Self {
168 self.prewarming_threads = v;
169 self
170 }
171
172 pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
174 self.cache_metrics_disabled = v;
175 self
176 }
177
178 pub const fn with_sparse_trie_max_hot_slots(mut self, v: usize) -> Self {
180 self.sparse_trie_max_hot_slots = v;
181 self
182 }
183
184 pub const fn with_sparse_trie_max_hot_accounts(mut self, v: usize) -> Self {
186 self.sparse_trie_max_hot_accounts = v;
187 self
188 }
189
190 pub const fn with_slow_block_threshold(mut self, v: Option<Duration>) -> Self {
192 self.slow_block_threshold = v;
193 self
194 }
195
196 pub const fn with_disable_sparse_trie_cache_pruning(mut self, v: bool) -> Self {
198 self.disable_sparse_trie_cache_pruning = v;
199 self
200 }
201
202 pub fn with_state_root_task_timeout(mut self, v: Option<String>) -> Self {
204 self.state_root_task_timeout = v;
205 self
206 }
207}
208
209impl Default for DefaultEngineValues {
210 fn default() -> Self {
211 Self {
212 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
213 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
214 legacy_state_root_task_enabled: false,
215 state_cache_disabled: false,
216 prewarming_disabled: false,
217 state_provider_metrics: false,
218 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
219 state_root_task_compare_updates: false,
220 accept_execution_requests_hash: false,
221 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
222 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
223 precompile_cache_disabled: false,
224 state_root_fallback: false,
225 always_process_payload_attributes_on_canonical_head: false,
226 allow_unwind_canonical_header: false,
227 storage_worker_count: None,
228 account_worker_count: None,
229 prewarming_threads: None,
230 cache_metrics_disabled: false,
231 sparse_trie_max_hot_slots: DEFAULT_SPARSE_TRIE_MAX_HOT_SLOTS,
232 sparse_trie_max_hot_accounts: DEFAULT_SPARSE_TRIE_MAX_HOT_ACCOUNTS,
233 slow_block_threshold: None,
234 disable_sparse_trie_cache_pruning: false,
235 state_root_task_timeout: Some("1s".to_string()),
236 }
237 }
238}
239
240#[derive(Debug, Clone, Args, PartialEq, Eq)]
242#[command(next_help_heading = "Engine")]
243pub struct EngineArgs {
244 #[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
251 pub persistence_threshold: u64,
252
253 #[arg(long = "engine.memory-block-buffer-target", default_value_t = DefaultEngineValues::get_global().memory_block_buffer_target)]
255 pub memory_block_buffer_target: u64,
256
257 #[arg(long = "engine.legacy-state-root", default_value_t = DefaultEngineValues::get_global().legacy_state_root_task_enabled)]
259 pub legacy_state_root_task_enabled: bool,
260
261 #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
264 #[deprecated]
265 pub caching_and_prewarming_enabled: bool,
266
267 #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
269 pub state_cache_disabled: bool,
270
271 #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
273 pub prewarming_disabled: bool,
274
275 #[deprecated]
277 #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
278 pub parallel_sparse_trie_enabled: bool,
279
280 #[deprecated]
282 #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
283 pub parallel_sparse_trie_disabled: bool,
284
285 #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
289 pub state_provider_metrics: bool,
290
291 #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
293 pub cross_block_cache_size: usize,
294
295 #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
298 pub state_root_task_compare_updates: bool,
299
300 #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
302 pub accept_execution_requests_hash: bool,
303
304 #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size)]
306 pub multiproof_chunk_size: usize,
307
308 #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
310 pub reserved_cpu_cores: usize,
311
312 #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
315 #[deprecated]
316 pub precompile_cache_enabled: bool,
317
318 #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
320 pub precompile_cache_disabled: bool,
321
322 #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
324 pub state_root_fallback: bool,
325
326 #[arg(
332 long = "engine.always-process-payload-attributes-on-canonical-head",
333 default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
334 )]
335 pub always_process_payload_attributes_on_canonical_head: bool,
336
337 #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
340 pub allow_unwind_canonical_header: bool,
341
342 #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
345 pub storage_worker_count: Option<usize>,
346
347 #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
350 pub account_worker_count: Option<usize>,
351
352 #[arg(long = "engine.prewarming-threads", default_value = Resettable::from(DefaultEngineValues::get_global().prewarming_threads.map(|v| v.to_string().into())))]
355 pub prewarming_threads: Option<usize>,
356
357 #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
359 pub cache_metrics_disabled: bool,
360
361 #[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)]
363 pub sparse_trie_max_hot_slots: usize,
364
365 #[arg(long = "engine.sparse-trie-max-hot-accounts", default_value_t = DefaultEngineValues::get_global().sparse_trie_max_hot_accounts)]
367 pub sparse_trie_max_hot_accounts: usize,
368
369 #[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())))]
378 pub slow_block_threshold: Option<Duration>,
379
380 #[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
384 pub disable_sparse_trie_cache_pruning: bool,
385
386 #[arg(
395 long = "engine.state-root-task-timeout",
396 value_parser = humantime::parse_duration,
397 default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("1s"),
398 )]
399 pub state_root_task_timeout: Option<Duration>,
400
401 #[cfg(feature = "trie-debug")]
408 #[arg(
409 long = "engine.proof-jitter",
410 value_parser = humantime::parse_duration,
411 )]
412 pub proof_jitter: Option<Duration>,
413}
414
415#[allow(deprecated)]
416impl Default for EngineArgs {
417 fn default() -> Self {
418 let DefaultEngineValues {
419 persistence_threshold,
420 memory_block_buffer_target,
421 legacy_state_root_task_enabled,
422 state_cache_disabled,
423 prewarming_disabled,
424 state_provider_metrics,
425 cross_block_cache_size,
426 state_root_task_compare_updates,
427 accept_execution_requests_hash,
428 multiproof_chunk_size,
429 reserved_cpu_cores,
430 precompile_cache_disabled,
431 state_root_fallback,
432 always_process_payload_attributes_on_canonical_head,
433 allow_unwind_canonical_header,
434 storage_worker_count,
435 account_worker_count,
436 prewarming_threads,
437 cache_metrics_disabled,
438 sparse_trie_max_hot_slots,
439 sparse_trie_max_hot_accounts,
440 slow_block_threshold,
441 disable_sparse_trie_cache_pruning,
442 state_root_task_timeout,
443 } = DefaultEngineValues::get_global().clone();
444 Self {
445 persistence_threshold,
446 memory_block_buffer_target,
447 legacy_state_root_task_enabled,
448 state_root_task_compare_updates,
449 caching_and_prewarming_enabled: true,
450 state_cache_disabled,
451 prewarming_disabled,
452 parallel_sparse_trie_enabled: true,
453 parallel_sparse_trie_disabled: false,
454 state_provider_metrics,
455 cross_block_cache_size,
456 accept_execution_requests_hash,
457 multiproof_chunk_size,
458 reserved_cpu_cores,
459 precompile_cache_enabled: true,
460 precompile_cache_disabled,
461 state_root_fallback,
462 always_process_payload_attributes_on_canonical_head,
463 allow_unwind_canonical_header,
464 storage_worker_count,
465 account_worker_count,
466 prewarming_threads,
467 cache_metrics_disabled,
468 sparse_trie_max_hot_slots,
469 sparse_trie_max_hot_accounts,
470 slow_block_threshold,
471 disable_sparse_trie_cache_pruning,
472 state_root_task_timeout: state_root_task_timeout
473 .as_deref()
474 .map(|s| humantime::parse_duration(s).expect("valid default duration")),
475 #[cfg(feature = "trie-debug")]
476 proof_jitter: None,
477 }
478 }
479}
480
481impl EngineArgs {
482 pub fn tree_config(&self) -> TreeConfig {
484 let config = TreeConfig::default()
485 .with_persistence_threshold(self.persistence_threshold)
486 .with_memory_block_buffer_target(self.memory_block_buffer_target)
487 .with_legacy_state_root(self.legacy_state_root_task_enabled)
488 .without_state_cache(self.state_cache_disabled)
489 .without_prewarming(self.prewarming_disabled)
490 .with_state_provider_metrics(self.state_provider_metrics)
491 .with_always_compare_trie_updates(self.state_root_task_compare_updates)
492 .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
493 .with_multiproof_chunk_size(self.multiproof_chunk_size)
494 .with_reserved_cpu_cores(self.reserved_cpu_cores)
495 .without_precompile_cache(self.precompile_cache_disabled)
496 .with_state_root_fallback(self.state_root_fallback)
497 .with_always_process_payload_attributes_on_canonical_head(
498 self.always_process_payload_attributes_on_canonical_head,
499 )
500 .with_unwind_canonical_header(self.allow_unwind_canonical_header)
501 .without_cache_metrics(self.cache_metrics_disabled)
502 .with_sparse_trie_max_hot_slots(self.sparse_trie_max_hot_slots)
503 .with_sparse_trie_max_hot_accounts(self.sparse_trie_max_hot_accounts)
504 .with_slow_block_threshold(self.slow_block_threshold)
505 .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
506 .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()));
507 #[cfg(feature = "trie-debug")]
508 let config = config.with_proof_jitter(self.proof_jitter);
509 config
510 }
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516 use clap::Parser;
517
518 #[derive(Parser)]
520 struct CommandParser<T: Args> {
521 #[command(flatten)]
522 args: T,
523 }
524
525 #[test]
526 fn test_parse_engine_args() {
527 let default_args = EngineArgs::default();
528 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
529 assert_eq!(args, default_args);
530 }
531
532 #[test]
533 #[allow(deprecated)]
534 fn engine_args() {
535 let args = EngineArgs {
536 persistence_threshold: 100,
537 memory_block_buffer_target: 50,
538 legacy_state_root_task_enabled: true,
539 caching_and_prewarming_enabled: true,
540 state_cache_disabled: true,
541 prewarming_disabled: true,
542 parallel_sparse_trie_enabled: true,
543 parallel_sparse_trie_disabled: false,
544 state_provider_metrics: true,
545 cross_block_cache_size: 256,
546 state_root_task_compare_updates: true,
547 accept_execution_requests_hash: true,
548 multiproof_chunk_size: 512,
549 reserved_cpu_cores: 4,
550 precompile_cache_enabled: true,
551 precompile_cache_disabled: true,
552 state_root_fallback: true,
553 always_process_payload_attributes_on_canonical_head: true,
554 allow_unwind_canonical_header: true,
555 storage_worker_count: Some(16),
556 account_worker_count: Some(8),
557 prewarming_threads: Some(4),
558 cache_metrics_disabled: true,
559 sparse_trie_max_hot_slots: 100,
560 sparse_trie_max_hot_accounts: 500,
561 slow_block_threshold: None,
562 disable_sparse_trie_cache_pruning: true,
563 state_root_task_timeout: Some(Duration::from_secs(2)),
564 #[cfg(feature = "trie-debug")]
565 proof_jitter: None,
566 };
567
568 let parsed_args = CommandParser::<EngineArgs>::parse_from([
569 "reth",
570 "--engine.persistence-threshold",
571 "100",
572 "--engine.memory-block-buffer-target",
573 "50",
574 "--engine.legacy-state-root",
575 "--engine.disable-state-cache",
576 "--engine.disable-prewarming",
577 "--engine.state-provider-metrics",
578 "--engine.cross-block-cache-size",
579 "256",
580 "--engine.state-root-task-compare-updates",
581 "--engine.accept-execution-requests-hash",
582 "--engine.multiproof-chunk-size",
583 "512",
584 "--engine.reserved-cpu-cores",
585 "4",
586 "--engine.disable-precompile-cache",
587 "--engine.state-root-fallback",
588 "--engine.always-process-payload-attributes-on-canonical-head",
589 "--engine.allow-unwind-canonical-header",
590 "--engine.storage-worker-count",
591 "16",
592 "--engine.account-worker-count",
593 "8",
594 "--engine.prewarming-threads",
595 "4",
596 "--engine.disable-cache-metrics",
597 "--engine.sparse-trie-max-hot-slots",
598 "100",
599 "--engine.sparse-trie-max-hot-accounts",
600 "500",
601 "--engine.disable-sparse-trie-cache-pruning",
602 "--engine.state-root-task-timeout",
603 "2s",
604 ])
605 .args;
606
607 assert_eq!(parsed_args, args);
608 }
609
610 #[test]
611 fn test_parse_slow_block_threshold() {
612 let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
614 assert_eq!(args.slow_block_threshold, None);
615
616 let args =
618 CommandParser::<EngineArgs>::parse_from(["reth", "--engine.slow-block-threshold", "0"])
619 .args;
620 assert_eq!(args.slow_block_threshold, Some(Duration::ZERO));
621
622 let args = CommandParser::<EngineArgs>::parse_from([
624 "reth",
625 "--engine.slow-block-threshold",
626 "500",
627 ])
628 .args;
629 assert_eq!(args.slow_block_threshold, Some(Duration::from_secs(500)));
630
631 let args = CommandParser::<EngineArgs>::parse_from([
632 "reth",
633 "--engine.slow-block-threshold",
634 "500ms",
635 ])
636 .args;
637 assert_eq!(args.slow_block_threshold, Some(Duration::from_millis(500)));
638 }
639}