reth_node_core/args/
engine.rs

1//! clap [Args](clap::Args) for engine purposes
2
3use clap::{builder::Resettable, Args};
4use reth_engine_primitives::{TreeConfig, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE};
5use std::sync::OnceLock;
6
7use crate::node_config::{
8    DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
9    DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
10};
11
12/// Global static engine defaults
13static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
14
15/// Default values for engine that can be customized
16///
17/// Global defaults can be set via [`DefaultEngineValues::try_init`].
18#[derive(Debug, Clone)]
19pub struct DefaultEngineValues {
20    persistence_threshold: u64,
21    memory_block_buffer_target: u64,
22    legacy_state_root_task_enabled: bool,
23    state_cache_disabled: bool,
24    prewarming_disabled: bool,
25    parallel_sparse_trie_disabled: bool,
26    state_provider_metrics: bool,
27    cross_block_cache_size: usize,
28    state_root_task_compare_updates: bool,
29    accept_execution_requests_hash: bool,
30    multiproof_chunking_enabled: bool,
31    multiproof_chunk_size: usize,
32    reserved_cpu_cores: usize,
33    precompile_cache_disabled: bool,
34    state_root_fallback: bool,
35    always_process_payload_attributes_on_canonical_head: bool,
36    allow_unwind_canonical_header: bool,
37    storage_worker_count: Option<usize>,
38    account_worker_count: Option<usize>,
39    enable_proof_v2: bool,
40    cache_metrics_disabled: bool,
41}
42
43impl DefaultEngineValues {
44    /// Initialize the global engine defaults with this configuration
45    pub fn try_init(self) -> Result<(), Self> {
46        ENGINE_DEFAULTS.set(self)
47    }
48
49    /// Get a reference to the global engine defaults
50    pub fn get_global() -> &'static Self {
51        ENGINE_DEFAULTS.get_or_init(Self::default)
52    }
53
54    /// Set the default persistence threshold
55    pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
56        self.persistence_threshold = v;
57        self
58    }
59
60    /// Set the default memory block buffer target
61    pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
62        self.memory_block_buffer_target = v;
63        self
64    }
65
66    /// Set whether to enable legacy state root task by default
67    pub const fn with_legacy_state_root_task_enabled(mut self, v: bool) -> Self {
68        self.legacy_state_root_task_enabled = v;
69        self
70    }
71
72    /// Set whether to disable state cache by default
73    pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
74        self.state_cache_disabled = v;
75        self
76    }
77
78    /// Set whether to disable prewarming by default
79    pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
80        self.prewarming_disabled = v;
81        self
82    }
83
84    /// Set whether to disable parallel sparse trie by default
85    pub const fn with_parallel_sparse_trie_disabled(mut self, v: bool) -> Self {
86        self.parallel_sparse_trie_disabled = v;
87        self
88    }
89
90    /// Set whether to enable state provider metrics by default
91    pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
92        self.state_provider_metrics = v;
93        self
94    }
95
96    /// Set the default cross-block cache size in MB
97    pub const fn with_cross_block_cache_size(mut self, v: usize) -> Self {
98        self.cross_block_cache_size = v;
99        self
100    }
101
102    /// Set whether to compare state root task updates by default
103    pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
104        self.state_root_task_compare_updates = v;
105        self
106    }
107
108    /// Set whether to accept execution requests hash by default
109    pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
110        self.accept_execution_requests_hash = v;
111        self
112    }
113
114    /// Set whether to enable multiproof chunking by default
115    pub const fn with_multiproof_chunking_enabled(mut self, v: bool) -> Self {
116        self.multiproof_chunking_enabled = v;
117        self
118    }
119
120    /// Set the default multiproof chunk size
121    pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
122        self.multiproof_chunk_size = v;
123        self
124    }
125
126    /// Set the default number of reserved CPU cores
127    pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
128        self.reserved_cpu_cores = v;
129        self
130    }
131
132    /// Set whether to disable precompile cache by default
133    pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
134        self.precompile_cache_disabled = v;
135        self
136    }
137
138    /// Set whether to enable state root fallback by default
139    pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
140        self.state_root_fallback = v;
141        self
142    }
143
144    /// Set whether to always process payload attributes on canonical head by default
145    pub const fn with_always_process_payload_attributes_on_canonical_head(
146        mut self,
147        v: bool,
148    ) -> Self {
149        self.always_process_payload_attributes_on_canonical_head = v;
150        self
151    }
152
153    /// Set whether to allow unwinding canonical header by default
154    pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
155        self.allow_unwind_canonical_header = v;
156        self
157    }
158
159    /// Set the default storage worker count
160    pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
161        self.storage_worker_count = v;
162        self
163    }
164
165    /// Set the default account worker count
166    pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
167        self.account_worker_count = v;
168        self
169    }
170
171    /// Set whether to enable proof V2 by default
172    pub const fn with_enable_proof_v2(mut self, v: bool) -> Self {
173        self.enable_proof_v2 = v;
174        self
175    }
176
177    /// Set whether to disable cache metrics by default
178    pub const fn with_cache_metrics_disabled(mut self, v: bool) -> Self {
179        self.cache_metrics_disabled = v;
180        self
181    }
182}
183
184impl Default for DefaultEngineValues {
185    fn default() -> Self {
186        Self {
187            persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
188            memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
189            legacy_state_root_task_enabled: false,
190            state_cache_disabled: false,
191            prewarming_disabled: false,
192            parallel_sparse_trie_disabled: false,
193            state_provider_metrics: false,
194            cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
195            state_root_task_compare_updates: false,
196            accept_execution_requests_hash: false,
197            multiproof_chunking_enabled: true,
198            multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
199            reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
200            precompile_cache_disabled: false,
201            state_root_fallback: false,
202            always_process_payload_attributes_on_canonical_head: false,
203            allow_unwind_canonical_header: false,
204            storage_worker_count: None,
205            account_worker_count: None,
206            enable_proof_v2: false,
207            cache_metrics_disabled: false,
208        }
209    }
210}
211
212/// Parameters for configuring the engine driver.
213#[derive(Debug, Clone, Args, PartialEq, Eq)]
214#[command(next_help_heading = "Engine")]
215pub struct EngineArgs {
216    /// Configure persistence threshold for the engine. This determines how many canonical blocks
217    /// must be in-memory, ahead of the last persisted block, before flushing canonical blocks to
218    /// disk again.
219    ///
220    /// To persist blocks as fast as the node receives them, set this value to zero. This will
221    /// cause more frequent DB writes.
222    #[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
223    pub persistence_threshold: u64,
224
225    /// Configure the target number of blocks to keep in memory.
226    #[arg(long = "engine.memory-block-buffer-target", default_value_t = DefaultEngineValues::get_global().memory_block_buffer_target)]
227    pub memory_block_buffer_target: u64,
228
229    /// Enable legacy state root
230    #[arg(long = "engine.legacy-state-root", default_value_t = DefaultEngineValues::get_global().legacy_state_root_task_enabled)]
231    pub legacy_state_root_task_enabled: bool,
232
233    /// CAUTION: This CLI flag has no effect anymore, use --engine.disable-caching-and-prewarming
234    /// if you want to disable caching and prewarming
235    #[arg(long = "engine.caching-and-prewarming", default_value = "true", hide = true)]
236    #[deprecated]
237    pub caching_and_prewarming_enabled: bool,
238
239    /// Disable state cache
240    #[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
241    pub state_cache_disabled: bool,
242
243    /// Disable parallel prewarming
244    #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
245    pub prewarming_disabled: bool,
246
247    /// CAUTION: This CLI flag has no effect anymore, use --engine.disable-parallel-sparse-trie
248    /// if you want to disable usage of the `ParallelSparseTrie`.
249    #[deprecated]
250    #[arg(long = "engine.parallel-sparse-trie", default_value = "true", hide = true)]
251    pub parallel_sparse_trie_enabled: bool,
252
253    /// Disable the parallel sparse trie in the engine.
254    #[arg(long = "engine.disable-parallel-sparse-trie", default_value_t = DefaultEngineValues::get_global().parallel_sparse_trie_disabled)]
255    pub parallel_sparse_trie_disabled: bool,
256
257    /// Enable state provider latency metrics. This allows the engine to collect and report stats
258    /// about how long state provider calls took during execution, but this does introduce slight
259    /// overhead to state provider calls.
260    #[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
261    pub state_provider_metrics: bool,
262
263    /// Configure the size of cross-block cache in megabytes
264    #[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
265    pub cross_block_cache_size: usize,
266
267    /// Enable comparing trie updates from the state root task to the trie updates from the regular
268    /// state root calculation.
269    #[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
270    pub state_root_task_compare_updates: bool,
271
272    /// Enables accepting requests hash instead of an array of requests in `engine_newPayloadV4`.
273    #[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
274    pub accept_execution_requests_hash: bool,
275
276    /// Whether multiproof task should chunk proof targets.
277    #[arg(long = "engine.multiproof-chunking", default_value_t = DefaultEngineValues::get_global().multiproof_chunking_enabled)]
278    pub multiproof_chunking_enabled: bool,
279
280    /// Multiproof task chunk size for proof targets.
281    #[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size)]
282    pub multiproof_chunk_size: usize,
283
284    /// Configure the number of reserved CPU cores for non-reth processes
285    #[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
286    pub reserved_cpu_cores: usize,
287
288    /// CAUTION: This CLI flag has no effect anymore, use --engine.disable-precompile-cache
289    /// if you want to disable precompile cache
290    #[arg(long = "engine.precompile-cache", default_value = "true", hide = true)]
291    #[deprecated]
292    pub precompile_cache_enabled: bool,
293
294    /// Disable precompile cache
295    #[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
296    pub precompile_cache_disabled: bool,
297
298    /// Enable state root fallback, useful for testing
299    #[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
300    pub state_root_fallback: bool,
301
302    /// Always process payload attributes and begin a payload build process even if
303    /// `forkchoiceState.headBlockHash` is already the canonical head or an ancestor. See
304    /// `TreeConfig::always_process_payload_attributes_on_canonical_head` for more details.
305    ///
306    /// Note: This is a no-op on OP Stack.
307    #[arg(
308        long = "engine.always-process-payload-attributes-on-canonical-head",
309        default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
310    )]
311    pub always_process_payload_attributes_on_canonical_head: bool,
312
313    /// Allow unwinding canonical header to ancestor during forkchoice updates.
314    /// See `TreeConfig::unwind_canonical_header` for more details.
315    #[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
316    pub allow_unwind_canonical_header: bool,
317
318    /// Configure the number of storage proof workers in the Tokio blocking pool.
319    /// If not specified, defaults to 2x available parallelism, clamped between 2 and 64.
320    #[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
321    pub storage_worker_count: Option<usize>,
322
323    /// Configure the number of account proof workers in the Tokio blocking pool.
324    /// If not specified, defaults to the same count as storage workers.
325    #[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
326    pub account_worker_count: Option<usize>,
327
328    /// Enable V2 storage proofs for state root calculations
329    #[arg(long = "engine.enable-proof-v2", default_value_t = DefaultEngineValues::get_global().enable_proof_v2)]
330    pub enable_proof_v2: bool,
331
332    /// Disable cache metrics recording, which can take up to 50ms with large cached state.
333    #[arg(long = "engine.disable-cache-metrics", default_value_t = DefaultEngineValues::get_global().cache_metrics_disabled)]
334    pub cache_metrics_disabled: bool,
335}
336
337#[allow(deprecated)]
338impl Default for EngineArgs {
339    fn default() -> Self {
340        let DefaultEngineValues {
341            persistence_threshold,
342            memory_block_buffer_target,
343            legacy_state_root_task_enabled,
344            state_cache_disabled,
345            prewarming_disabled,
346            parallel_sparse_trie_disabled,
347            state_provider_metrics,
348            cross_block_cache_size,
349            state_root_task_compare_updates,
350            accept_execution_requests_hash,
351            multiproof_chunking_enabled,
352            multiproof_chunk_size,
353            reserved_cpu_cores,
354            precompile_cache_disabled,
355            state_root_fallback,
356            always_process_payload_attributes_on_canonical_head,
357            allow_unwind_canonical_header,
358            storage_worker_count,
359            account_worker_count,
360            enable_proof_v2,
361            cache_metrics_disabled,
362        } = DefaultEngineValues::get_global().clone();
363        Self {
364            persistence_threshold,
365            memory_block_buffer_target,
366            legacy_state_root_task_enabled,
367            state_root_task_compare_updates,
368            caching_and_prewarming_enabled: true,
369            state_cache_disabled,
370            prewarming_disabled,
371            parallel_sparse_trie_enabled: true,
372            parallel_sparse_trie_disabled,
373            state_provider_metrics,
374            cross_block_cache_size,
375            accept_execution_requests_hash,
376            multiproof_chunking_enabled,
377            multiproof_chunk_size,
378            reserved_cpu_cores,
379            precompile_cache_enabled: true,
380            precompile_cache_disabled,
381            state_root_fallback,
382            always_process_payload_attributes_on_canonical_head,
383            allow_unwind_canonical_header,
384            storage_worker_count,
385            account_worker_count,
386            enable_proof_v2,
387            cache_metrics_disabled,
388        }
389    }
390}
391
392impl EngineArgs {
393    /// Creates a [`TreeConfig`] from the engine arguments.
394    pub fn tree_config(&self) -> TreeConfig {
395        let mut config = TreeConfig::default()
396            .with_persistence_threshold(self.persistence_threshold)
397            .with_memory_block_buffer_target(self.memory_block_buffer_target)
398            .with_legacy_state_root(self.legacy_state_root_task_enabled)
399            .without_state_cache(self.state_cache_disabled)
400            .without_prewarming(self.prewarming_disabled)
401            .with_disable_parallel_sparse_trie(self.parallel_sparse_trie_disabled)
402            .with_state_provider_metrics(self.state_provider_metrics)
403            .with_always_compare_trie_updates(self.state_root_task_compare_updates)
404            .with_cross_block_cache_size(self.cross_block_cache_size * 1024 * 1024)
405            .with_multiproof_chunking_enabled(self.multiproof_chunking_enabled)
406            .with_multiproof_chunk_size(self.multiproof_chunk_size)
407            .with_reserved_cpu_cores(self.reserved_cpu_cores)
408            .without_precompile_cache(self.precompile_cache_disabled)
409            .with_state_root_fallback(self.state_root_fallback)
410            .with_always_process_payload_attributes_on_canonical_head(
411                self.always_process_payload_attributes_on_canonical_head,
412            )
413            .with_unwind_canonical_header(self.allow_unwind_canonical_header);
414
415        if let Some(count) = self.storage_worker_count {
416            config = config.with_storage_worker_count(count);
417        }
418
419        if let Some(count) = self.account_worker_count {
420            config = config.with_account_worker_count(count);
421        }
422
423        config = config.with_enable_proof_v2(self.enable_proof_v2);
424        config = config.without_cache_metrics(self.cache_metrics_disabled);
425
426        config
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use super::*;
433    use clap::Parser;
434
435    /// A helper type to parse Args more easily
436    #[derive(Parser)]
437    struct CommandParser<T: Args> {
438        #[command(flatten)]
439        args: T,
440    }
441
442    #[test]
443    fn test_parse_engine_args() {
444        let default_args = EngineArgs::default();
445        let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
446        assert_eq!(args, default_args);
447    }
448
449    #[test]
450    #[allow(deprecated)]
451    fn engine_args() {
452        let args = EngineArgs {
453            persistence_threshold: 100,
454            memory_block_buffer_target: 50,
455            legacy_state_root_task_enabled: true,
456            caching_and_prewarming_enabled: true,
457            state_cache_disabled: true,
458            prewarming_disabled: true,
459            parallel_sparse_trie_enabled: true,
460            parallel_sparse_trie_disabled: true,
461            state_provider_metrics: true,
462            cross_block_cache_size: 256,
463            state_root_task_compare_updates: true,
464            accept_execution_requests_hash: true,
465            multiproof_chunking_enabled: true,
466            multiproof_chunk_size: 512,
467            reserved_cpu_cores: 4,
468            precompile_cache_enabled: true,
469            precompile_cache_disabled: true,
470            state_root_fallback: true,
471            always_process_payload_attributes_on_canonical_head: true,
472            allow_unwind_canonical_header: true,
473            storage_worker_count: Some(16),
474            account_worker_count: Some(8),
475            enable_proof_v2: false,
476            cache_metrics_disabled: true,
477        };
478
479        let parsed_args = CommandParser::<EngineArgs>::parse_from([
480            "reth",
481            "--engine.persistence-threshold",
482            "100",
483            "--engine.memory-block-buffer-target",
484            "50",
485            "--engine.legacy-state-root",
486            "--engine.disable-state-cache",
487            "--engine.disable-prewarming",
488            "--engine.disable-parallel-sparse-trie",
489            "--engine.state-provider-metrics",
490            "--engine.cross-block-cache-size",
491            "256",
492            "--engine.state-root-task-compare-updates",
493            "--engine.accept-execution-requests-hash",
494            "--engine.multiproof-chunking",
495            "--engine.multiproof-chunk-size",
496            "512",
497            "--engine.reserved-cpu-cores",
498            "4",
499            "--engine.disable-precompile-cache",
500            "--engine.state-root-fallback",
501            "--engine.always-process-payload-attributes-on-canonical-head",
502            "--engine.allow-unwind-canonical-header",
503            "--engine.storage-worker-count",
504            "16",
505            "--engine.account-worker-count",
506            "8",
507            "--engine.disable-cache-metrics",
508        ])
509        .args;
510
511        assert_eq!(parsed_args, args);
512    }
513}