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