Skip to main content

reth_node_core/args/
engine.rs

1//! clap [Args](clap::Args) for engine purposes
2
3use 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
16/// Global static engine defaults
17static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
18
19/// Default values for engine that can be customized
20///
21/// Global defaults can be set via [`DefaultEngineValues::try_init`].
22#[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    /// Initialize the global engine defaults with this configuration
52    pub fn try_init(self) -> Result<(), Self> {
53        ENGINE_DEFAULTS.set(self)
54    }
55
56    /// Get a reference to the global engine defaults
57    pub fn get_global() -> &'static Self {
58        ENGINE_DEFAULTS.get_or_init(Self::default)
59    }
60
61    /// Set the default persistence threshold
62    pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
63        self.persistence_threshold = v;
64        self
65    }
66
67    /// Set the default memory block buffer target
68    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    /// Set whether to enable legacy state root task by default
74    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    /// Set whether to disable state cache by default
80    pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
81        self.state_cache_disabled = v;
82        self
83    }
84
85    /// Set whether to disable prewarming by default
86    pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
87        self.prewarming_disabled = v;
88        self
89    }
90
91    /// Set whether to enable state provider metrics by default
92    pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
93        self.state_provider_metrics = v;
94        self
95    }
96
97    /// Set the default cross-block cache size in MB
98    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    /// Set whether to compare state root task updates by default
104    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    /// Set whether to accept execution requests hash by default
110    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    /// Set the default multiproof chunk size
116    pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
117        self.multiproof_chunk_size = v;
118        self
119    }
120
121    /// Set the default number of reserved CPU cores
122    pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
123        self.reserved_cpu_cores = v;
124        self
125    }
126
127    /// Set whether to disable precompile cache by default
128    pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
129        self.precompile_cache_disabled = v;
130        self
131    }
132
133    /// Set whether to enable state root fallback by default
134    pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
135        self.state_root_fallback = v;
136        self
137    }
138
139    /// Set whether to always process payload attributes on canonical head by default
140    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    /// Set whether to allow unwinding canonical header by default
149    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    /// Set the default storage worker count
155    pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
156        self.storage_worker_count = v;
157        self
158    }
159
160    /// Set the default account worker count
161    pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
162        self.account_worker_count = v;
163        self
164    }
165
166    /// Set the default prewarming thread count
167    pub const fn with_prewarming_threads(mut self, v: Option<usize>) -> Self {
168        self.prewarming_threads = v;
169        self
170    }
171
172    /// Set whether to disable cache metrics by default
173    pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
174        self.cache_metrics_disabled = v;
175        self
176    }
177
178    /// Set the LFU hot-slot capacity for sparse trie pruning by default
179    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    /// Set the LFU hot-account capacity for sparse trie pruning by default
185    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    /// Set the default slow block threshold.
191    pub const fn with_slow_block_threshold(mut self, v: Option<Duration>) -> Self {
192        self.slow_block_threshold = v;
193        self
194    }
195
196    /// Set whether to disable sparse trie cache pruning by default
197    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    /// Set the default state root task timeout
203    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/// Parameters for configuring the engine driver.
241#[derive(Debug, Clone, Args, PartialEq, Eq)]
242#[command(next_help_heading = "Engine")]
243pub struct EngineArgs {
244    /// Configure persistence threshold for the engine. This determines how many canonical blocks
245    /// must be in-memory, ahead of the last persisted block, before flushing canonical blocks to
246    /// disk again.
247    ///
248    /// To persist blocks as fast as the node receives them, set this value to zero. This will
249    /// cause more frequent DB writes.
250    #[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
251    pub persistence_threshold: u64,
252
253    /// Configure the target number of blocks to keep in memory.
254    #[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    /// Enable legacy state root
258    #[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    /// CAUTION: This CLI flag has no effect anymore, use --engine.disable-caching-and-prewarming
262    /// if you want to disable caching and prewarming
263    #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
264    #[deprecated]
265    pub caching_and_prewarming_enabled: bool,
266
267    /// Disable state cache
268    #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
269    pub state_cache_disabled: bool,
270
271    /// Disable parallel prewarming
272    #[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    /// CAUTION: This CLI flag has no effect anymore. The parallel sparse trie is always enabled.
276    #[deprecated]
277    #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
278    pub parallel_sparse_trie_enabled: bool,
279
280    /// CAUTION: This CLI flag has no effect anymore. The parallel sparse trie is always enabled.
281    #[deprecated]
282    #[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false", hide = true)]
283    pub parallel_sparse_trie_disabled: bool,
284
285    /// Enable state provider latency metrics. This allows the engine to collect and report stats
286    /// about how long state provider calls took during execution, but this does introduce slight
287    /// overhead to state provider calls.
288    #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
289    pub state_provider_metrics: bool,
290
291    /// Configure the size of cross-block cache in megabytes
292    #[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    /// Enable comparing trie updates from the state root task to the trie updates from the regular
296    /// state root calculation.
297    #[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    /// Enables accepting requests hash instead of an array of requests in `engine_newPayloadV4`.
301    #[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    /// Multiproof task chunk size for proof targets.
305    #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size)]
306    pub multiproof_chunk_size: usize,
307
308    /// Configure the number of reserved CPU cores for non-reth processes
309    #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
310    pub reserved_cpu_cores: usize,
311
312    /// CAUTION: This CLI flag has no effect anymore, use --engine.disable-precompile-cache
313    /// if you want to disable precompile cache
314    #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
315    #[deprecated]
316    pub precompile_cache_enabled: bool,
317
318    /// Disable precompile cache
319    #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
320    pub precompile_cache_disabled: bool,
321
322    /// Enable state root fallback, useful for testing
323    #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
324    pub state_root_fallback: bool,
325
326    /// Always process payload attributes and begin a payload build process even if
327    /// `forkchoiceState.headBlockHash` is already the canonical head or an ancestor. See
328    /// `TreeConfig::always_process_payload_attributes_on_canonical_head` for more details.
329    ///
330    /// Note: This is a no-op on OP Stack.
331    #[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    /// Allow unwinding canonical header to ancestor during forkchoice updates.
338    /// See `TreeConfig::unwind_canonical_header` for more details.
339    #[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    /// Configure the number of storage proof workers in the Tokio blocking pool.
343    /// If not specified, defaults to 2x available parallelism.
344    #[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    /// Configure the number of account proof workers in the Tokio blocking pool.
348    /// If not specified, defaults to the same count as storage workers.
349    #[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    /// Configure the number of prewarming threads.
353    /// If not specified, defaults to available parallelism.
354    #[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    /// Disable cache metrics recording, which can take up to 50ms with large cached state.
358    #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
359    pub cache_metrics_disabled: bool,
360
361    /// LFU hot-slot capacity: max storage slots retained across sparse trie prune cycles.
362    #[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    /// LFU hot-account capacity: max account addresses retained across sparse trie prune cycles.
366    #[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    /// Configure the slow block logging threshold in milliseconds.
370    ///
371    /// When set, blocks that take longer than this threshold to execute will be logged
372    /// with detailed metrics including timing, state operations, and cache statistics.
373    ///
374    /// Set to 0 to log all blocks (useful for debugging/profiling).
375    ///
376    /// When not set, slow block logging is disabled (default).
377    #[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    /// Fully disable sparse trie cache pruning. When set, the cached sparse trie is preserved
381    /// without any node pruning or storage trie eviction between blocks. Useful for benchmarking
382    /// the effects of retaining the full trie cache.
383    #[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    /// Configure the timeout for the state root task before spawning a sequential fallback.
387    /// If the state root task takes longer than this, a sequential computation starts in
388    /// parallel and whichever finishes first is used.
389    ///
390    /// --engine.state-root-task-timeout 1s
391    /// --engine.state-root-task-timeout 400ms
392    ///
393    /// Set to 0s to disable.
394    #[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    /// Add random jitter before each proof computation (trie-debug only).
402    /// Each proof worker sleeps for a random duration up to this value before
403    /// starting work. Useful for stress-testing timing-sensitive proof logic.
404    ///
405    /// --engine.proof-jitter 100ms
406    /// --engine.proof-jitter 1s
407    #[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    /// Creates a [`TreeConfig`] from the engine arguments.
483    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    /// A helper type to parse Args more easily
519    #[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        // Test default value (None - disabled)
613        let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
614        assert_eq!(args.slow_block_threshold, None);
615
616        // Test setting to 0 (log all blocks)
617        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        // Test setting to custom value
623        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}