reth_engine_primitives/
config.rs

1//! Engine tree configuration.
2
3use alloy_eips::merge::EPOCH_SLOTS;
4
5/// Triggers persistence when the number of canonical blocks in memory exceeds this threshold.
6pub const DEFAULT_PERSISTENCE_THRESHOLD: u64 = 2;
7
8/// How close to the canonical head we persist blocks.
9pub const DEFAULT_MEMORY_BLOCK_BUFFER_TARGET: u64 = 0;
10
11/// Minimum number of workers we allow configuring explicitly.
12pub const MIN_WORKER_COUNT: usize = 32;
13
14/// Returns the default number of storage worker threads based on available parallelism.
15fn default_storage_worker_count() -> usize {
16    #[cfg(feature = "std")]
17    {
18        std::thread::available_parallelism().map_or(8, |n| n.get() * 2).min(MIN_WORKER_COUNT)
19    }
20    #[cfg(not(feature = "std"))]
21    {
22        8
23    }
24}
25
26/// Returns the default number of account worker threads.
27///
28/// Account workers coordinate storage proof collection and account trie traversal.
29/// They are set to the same count as storage workers for simplicity.
30fn default_account_worker_count() -> usize {
31    default_storage_worker_count()
32}
33
34/// The size of proof targets chunk to spawn in one multiproof calculation.
35pub const DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE: usize = 60;
36
37/// The size of proof targets chunk to spawn in one multiproof calculation when V2 proofs are
38/// enabled. This is 4x the default chunk size to take advantage of more efficient V2 proof
39/// computation.
40pub const DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE_V2: usize = DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE * 4;
41
42/// Default number of reserved CPU cores for non-reth processes.
43///
44/// This will be deducted from the thread count of main reth global threadpool.
45pub const DEFAULT_RESERVED_CPU_CORES: usize = 1;
46
47/// Default maximum concurrency for prewarm task.
48pub const DEFAULT_PREWARM_MAX_CONCURRENCY: usize = 16;
49
50const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = EPOCH_SLOTS as u32 * 2;
51const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
52const DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE: usize = 4;
53const DEFAULT_CROSS_BLOCK_CACHE_SIZE: usize = default_cross_block_cache_size();
54
55const fn default_cross_block_cache_size() -> usize {
56    if cfg!(test) {
57        1024 * 1024 // 1 MB in tests
58    } else if cfg!(target_pointer_width = "32") {
59        usize::MAX // max possible on wasm32 / 32-bit
60    } else {
61        4 * 1024 * 1024 * 1024 // 4 GB on 64-bit
62    }
63}
64
65/// Determines if the host has enough parallelism to run the payload processor.
66///
67/// It requires at least 5 parallel threads:
68/// - Engine in main thread that spawns the state root task.
69/// - Multiproof task in payload processor
70/// - Sparse Trie task in payload processor
71/// - Multiproof computation spawned in payload processor
72/// - Storage root computation spawned in trie parallel proof
73pub fn has_enough_parallelism() -> bool {
74    #[cfg(feature = "std")]
75    {
76        std::thread::available_parallelism().is_ok_and(|num| num.get() >= 5)
77    }
78    #[cfg(not(feature = "std"))]
79    false
80}
81
82/// The configuration of the engine tree.
83#[derive(Debug, Clone)]
84pub struct TreeConfig {
85    /// Maximum number of blocks to be kept only in memory without triggering
86    /// persistence.
87    persistence_threshold: u64,
88    /// How close to the canonical head we persist blocks. Represents the ideal
89    /// number of most recent blocks to keep in memory for quick access and reorgs.
90    ///
91    /// Note: this should be less than or equal to `persistence_threshold`.
92    memory_block_buffer_target: u64,
93    /// Number of pending blocks that cannot be executed due to missing parent and
94    /// are kept in cache.
95    block_buffer_limit: u32,
96    /// Number of invalid headers to keep in cache.
97    max_invalid_header_cache_length: u32,
98    /// Maximum number of blocks to execute sequentially in a batch.
99    ///
100    /// This is used as a cutoff to prevent long-running sequential block execution when we receive
101    /// a batch of downloaded blocks.
102    max_execute_block_batch_size: usize,
103    /// Whether to use the legacy state root calculation method instead of the
104    /// new state root task.
105    legacy_state_root: bool,
106    /// Whether to always compare trie updates from the state root task to the trie updates from
107    /// the regular state root calculation.
108    always_compare_trie_updates: bool,
109    /// Whether to disable state cache.
110    disable_state_cache: bool,
111    /// Whether to disable parallel prewarming.
112    disable_prewarming: bool,
113    /// Whether to disable the parallel sparse trie state root algorithm.
114    disable_parallel_sparse_trie: bool,
115    /// Whether to enable state provider metrics.
116    state_provider_metrics: bool,
117    /// Cross-block cache size in bytes.
118    cross_block_cache_size: usize,
119    /// Whether the host has enough parallelism to run state root task.
120    has_enough_parallelism: bool,
121    /// Whether multiproof task should chunk proof targets.
122    multiproof_chunking_enabled: bool,
123    /// Multiproof task chunk size for proof targets.
124    multiproof_chunk_size: usize,
125    /// Number of reserved CPU cores for non-reth processes
126    reserved_cpu_cores: usize,
127    /// Whether to disable the precompile cache
128    precompile_cache_disabled: bool,
129    /// Whether to use state root fallback for testing
130    state_root_fallback: bool,
131    /// Whether to always process payload attributes and begin a payload build process
132    /// even if `forkchoiceState.headBlockHash` is already the canonical head or an ancestor.
133    ///
134    /// The Engine API specification generally states that client software "MUST NOT begin a
135    /// payload build process if `forkchoiceState.headBlockHash` references a `VALID`
136    /// ancestor of the head of canonical chain".
137    /// See: <https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#engine_forkchoiceupdatedv1> (Rule 2)
138    ///
139    /// This flag allows overriding that behavior.
140    /// This is useful for specific chain configurations (e.g., OP Stack where proposers
141    /// can reorg their own chain), various custom chains, or for development/testing purposes
142    /// where immediate payload regeneration is desired despite the head not changing or moving to
143    /// an ancestor.
144    always_process_payload_attributes_on_canonical_head: bool,
145    /// Maximum concurrency for the prewarm task.
146    prewarm_max_concurrency: usize,
147    /// Whether to unwind canonical header to ancestor during forkchoice updates.
148    allow_unwind_canonical_header: bool,
149    /// Number of storage proof worker threads.
150    storage_worker_count: usize,
151    /// Number of account proof worker threads.
152    account_worker_count: usize,
153    /// Whether to enable V2 storage proofs.
154    enable_proof_v2: bool,
155    /// Whether to disable cache metrics recording (can be expensive with large cached state).
156    disable_cache_metrics: bool,
157}
158
159impl Default for TreeConfig {
160    fn default() -> Self {
161        Self {
162            persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
163            memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
164            block_buffer_limit: DEFAULT_BLOCK_BUFFER_LIMIT,
165            max_invalid_header_cache_length: DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
166            max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE,
167            legacy_state_root: false,
168            always_compare_trie_updates: false,
169            disable_state_cache: false,
170            disable_prewarming: false,
171            disable_parallel_sparse_trie: false,
172            state_provider_metrics: false,
173            cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE,
174            has_enough_parallelism: has_enough_parallelism(),
175            multiproof_chunking_enabled: true,
176            multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
177            reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
178            precompile_cache_disabled: false,
179            state_root_fallback: false,
180            always_process_payload_attributes_on_canonical_head: false,
181            prewarm_max_concurrency: DEFAULT_PREWARM_MAX_CONCURRENCY,
182            allow_unwind_canonical_header: false,
183            storage_worker_count: default_storage_worker_count(),
184            account_worker_count: default_account_worker_count(),
185            enable_proof_v2: false,
186            disable_cache_metrics: false,
187        }
188    }
189}
190
191impl TreeConfig {
192    /// Create engine tree configuration.
193    #[expect(clippy::too_many_arguments)]
194    pub const fn new(
195        persistence_threshold: u64,
196        memory_block_buffer_target: u64,
197        block_buffer_limit: u32,
198        max_invalid_header_cache_length: u32,
199        max_execute_block_batch_size: usize,
200        legacy_state_root: bool,
201        always_compare_trie_updates: bool,
202        disable_state_cache: bool,
203        disable_prewarming: bool,
204        disable_parallel_sparse_trie: bool,
205        state_provider_metrics: bool,
206        cross_block_cache_size: usize,
207        has_enough_parallelism: bool,
208        multiproof_chunking_enabled: bool,
209        multiproof_chunk_size: usize,
210        reserved_cpu_cores: usize,
211        precompile_cache_disabled: bool,
212        state_root_fallback: bool,
213        always_process_payload_attributes_on_canonical_head: bool,
214        prewarm_max_concurrency: usize,
215        allow_unwind_canonical_header: bool,
216        storage_worker_count: usize,
217        account_worker_count: usize,
218        enable_proof_v2: bool,
219        disable_cache_metrics: bool,
220    ) -> Self {
221        Self {
222            persistence_threshold,
223            memory_block_buffer_target,
224            block_buffer_limit,
225            max_invalid_header_cache_length,
226            max_execute_block_batch_size,
227            legacy_state_root,
228            always_compare_trie_updates,
229            disable_state_cache,
230            disable_prewarming,
231            disable_parallel_sparse_trie,
232            state_provider_metrics,
233            cross_block_cache_size,
234            has_enough_parallelism,
235            multiproof_chunking_enabled,
236            multiproof_chunk_size,
237            reserved_cpu_cores,
238            precompile_cache_disabled,
239            state_root_fallback,
240            always_process_payload_attributes_on_canonical_head,
241            prewarm_max_concurrency,
242            allow_unwind_canonical_header,
243            storage_worker_count,
244            account_worker_count,
245            enable_proof_v2,
246            disable_cache_metrics,
247        }
248    }
249
250    /// Return the persistence threshold.
251    pub const fn persistence_threshold(&self) -> u64 {
252        self.persistence_threshold
253    }
254
255    /// Return the memory block buffer target.
256    pub const fn memory_block_buffer_target(&self) -> u64 {
257        self.memory_block_buffer_target
258    }
259
260    /// Return the block buffer limit.
261    pub const fn block_buffer_limit(&self) -> u32 {
262        self.block_buffer_limit
263    }
264
265    /// Return the maximum invalid cache header length.
266    pub const fn max_invalid_header_cache_length(&self) -> u32 {
267        self.max_invalid_header_cache_length
268    }
269
270    /// Return the maximum execute block batch size.
271    pub const fn max_execute_block_batch_size(&self) -> usize {
272        self.max_execute_block_batch_size
273    }
274
275    /// Return whether the multiproof task chunking is enabled.
276    pub const fn multiproof_chunking_enabled(&self) -> bool {
277        self.multiproof_chunking_enabled
278    }
279
280    /// Return the multiproof task chunk size.
281    pub const fn multiproof_chunk_size(&self) -> usize {
282        self.multiproof_chunk_size
283    }
284
285    /// Return the multiproof task chunk size, using the V2 default if V2 proofs are enabled
286    /// and the chunk size is at the default value.
287    pub const fn effective_multiproof_chunk_size(&self) -> usize {
288        if self.enable_proof_v2 && self.multiproof_chunk_size == DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE
289        {
290            DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE_V2
291        } else {
292            self.multiproof_chunk_size
293        }
294    }
295
296    /// Return the number of reserved CPU cores for non-reth processes
297    pub const fn reserved_cpu_cores(&self) -> usize {
298        self.reserved_cpu_cores
299    }
300
301    /// Returns whether to use the legacy state root calculation method instead
302    /// of the new state root task
303    pub const fn legacy_state_root(&self) -> bool {
304        self.legacy_state_root
305    }
306
307    /// Returns whether or not state provider metrics are enabled.
308    pub const fn state_provider_metrics(&self) -> bool {
309        self.state_provider_metrics
310    }
311
312    /// Returns whether or not the parallel sparse trie is disabled.
313    pub const fn disable_parallel_sparse_trie(&self) -> bool {
314        self.disable_parallel_sparse_trie
315    }
316
317    /// Returns whether or not state cache is disabled.
318    pub const fn disable_state_cache(&self) -> bool {
319        self.disable_state_cache
320    }
321
322    /// Returns whether or not parallel prewarming is disabled.
323    pub const fn disable_prewarming(&self) -> bool {
324        self.disable_prewarming
325    }
326
327    /// Returns whether to always compare trie updates from the state root task to the trie updates
328    /// from the regular state root calculation.
329    pub const fn always_compare_trie_updates(&self) -> bool {
330        self.always_compare_trie_updates
331    }
332
333    /// Returns the cross-block cache size.
334    pub const fn cross_block_cache_size(&self) -> usize {
335        self.cross_block_cache_size
336    }
337
338    /// Returns whether precompile cache is disabled.
339    pub const fn precompile_cache_disabled(&self) -> bool {
340        self.precompile_cache_disabled
341    }
342
343    /// Returns whether to use state root fallback.
344    pub const fn state_root_fallback(&self) -> bool {
345        self.state_root_fallback
346    }
347
348    /// Sets whether to always process payload attributes when the FCU head is already canonical.
349    pub const fn with_always_process_payload_attributes_on_canonical_head(
350        mut self,
351        always_process_payload_attributes_on_canonical_head: bool,
352    ) -> Self {
353        self.always_process_payload_attributes_on_canonical_head =
354            always_process_payload_attributes_on_canonical_head;
355        self
356    }
357
358    /// Returns true if payload attributes should always be processed even when the FCU head is
359    /// canonical.
360    pub const fn always_process_payload_attributes_on_canonical_head(&self) -> bool {
361        self.always_process_payload_attributes_on_canonical_head
362    }
363
364    /// Returns true if canonical header should be unwound to ancestor during forkchoice updates.
365    pub const fn unwind_canonical_header(&self) -> bool {
366        self.allow_unwind_canonical_header
367    }
368
369    /// Setter for persistence threshold.
370    pub const fn with_persistence_threshold(mut self, persistence_threshold: u64) -> Self {
371        self.persistence_threshold = persistence_threshold;
372        self
373    }
374
375    /// Setter for memory block buffer target.
376    pub const fn with_memory_block_buffer_target(
377        mut self,
378        memory_block_buffer_target: u64,
379    ) -> Self {
380        self.memory_block_buffer_target = memory_block_buffer_target;
381        self
382    }
383
384    /// Setter for block buffer limit.
385    pub const fn with_block_buffer_limit(mut self, block_buffer_limit: u32) -> Self {
386        self.block_buffer_limit = block_buffer_limit;
387        self
388    }
389
390    /// Setter for maximum invalid header cache length.
391    pub const fn with_max_invalid_header_cache_length(
392        mut self,
393        max_invalid_header_cache_length: u32,
394    ) -> Self {
395        self.max_invalid_header_cache_length = max_invalid_header_cache_length;
396        self
397    }
398
399    /// Setter for maximum execute block batch size.
400    pub const fn with_max_execute_block_batch_size(
401        mut self,
402        max_execute_block_batch_size: usize,
403    ) -> Self {
404        self.max_execute_block_batch_size = max_execute_block_batch_size;
405        self
406    }
407
408    /// Setter for whether to use the legacy state root calculation method.
409    pub const fn with_legacy_state_root(mut self, legacy_state_root: bool) -> Self {
410        self.legacy_state_root = legacy_state_root;
411        self
412    }
413
414    /// Setter for whether to disable state cache.
415    pub const fn without_state_cache(mut self, disable_state_cache: bool) -> Self {
416        self.disable_state_cache = disable_state_cache;
417        self
418    }
419
420    /// Setter for whether to disable parallel prewarming.
421    pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self {
422        self.disable_prewarming = disable_prewarming;
423        self
424    }
425
426    /// Setter for whether to always compare trie updates from the state root task to the trie
427    /// updates from the regular state root calculation.
428    pub const fn with_always_compare_trie_updates(
429        mut self,
430        always_compare_trie_updates: bool,
431    ) -> Self {
432        self.always_compare_trie_updates = always_compare_trie_updates;
433        self
434    }
435
436    /// Setter for cross block cache size.
437    pub const fn with_cross_block_cache_size(mut self, cross_block_cache_size: usize) -> Self {
438        self.cross_block_cache_size = cross_block_cache_size;
439        self
440    }
441
442    /// Setter for has enough parallelism.
443    pub const fn with_has_enough_parallelism(mut self, has_enough_parallelism: bool) -> Self {
444        self.has_enough_parallelism = has_enough_parallelism;
445        self
446    }
447
448    /// Setter for state provider metrics.
449    pub const fn with_state_provider_metrics(mut self, state_provider_metrics: bool) -> Self {
450        self.state_provider_metrics = state_provider_metrics;
451        self
452    }
453
454    /// Setter for whether to disable the parallel sparse trie
455    pub const fn with_disable_parallel_sparse_trie(
456        mut self,
457        disable_parallel_sparse_trie: bool,
458    ) -> Self {
459        self.disable_parallel_sparse_trie = disable_parallel_sparse_trie;
460        self
461    }
462
463    /// Setter for whether multiproof task should chunk proof targets.
464    pub const fn with_multiproof_chunking_enabled(
465        mut self,
466        multiproof_chunking_enabled: bool,
467    ) -> Self {
468        self.multiproof_chunking_enabled = multiproof_chunking_enabled;
469        self
470    }
471
472    /// Setter for multiproof task chunk size for proof targets.
473    pub const fn with_multiproof_chunk_size(mut self, multiproof_chunk_size: usize) -> Self {
474        self.multiproof_chunk_size = multiproof_chunk_size;
475        self
476    }
477
478    /// Setter for the number of reserved CPU cores for any non-reth processes
479    pub const fn with_reserved_cpu_cores(mut self, reserved_cpu_cores: usize) -> Self {
480        self.reserved_cpu_cores = reserved_cpu_cores;
481        self
482    }
483
484    /// Setter for whether to disable the precompile cache.
485    pub const fn without_precompile_cache(mut self, precompile_cache_disabled: bool) -> Self {
486        self.precompile_cache_disabled = precompile_cache_disabled;
487        self
488    }
489
490    /// Setter for whether to use state root fallback, useful for testing.
491    pub const fn with_state_root_fallback(mut self, state_root_fallback: bool) -> Self {
492        self.state_root_fallback = state_root_fallback;
493        self
494    }
495
496    /// Setter for whether to unwind canonical header to ancestor during forkchoice updates.
497    pub const fn with_unwind_canonical_header(mut self, unwind_canonical_header: bool) -> Self {
498        self.allow_unwind_canonical_header = unwind_canonical_header;
499        self
500    }
501
502    /// Whether or not to use state root task
503    pub const fn use_state_root_task(&self) -> bool {
504        self.has_enough_parallelism && !self.legacy_state_root
505    }
506
507    /// Setter for prewarm max concurrency.
508    pub const fn with_prewarm_max_concurrency(mut self, prewarm_max_concurrency: usize) -> Self {
509        self.prewarm_max_concurrency = prewarm_max_concurrency;
510        self
511    }
512
513    /// Return the prewarm max concurrency.
514    pub const fn prewarm_max_concurrency(&self) -> usize {
515        self.prewarm_max_concurrency
516    }
517
518    /// Return the number of storage proof worker threads.
519    pub const fn storage_worker_count(&self) -> usize {
520        self.storage_worker_count
521    }
522
523    /// Setter for the number of storage proof worker threads.
524    pub fn with_storage_worker_count(mut self, storage_worker_count: usize) -> Self {
525        self.storage_worker_count = storage_worker_count.max(MIN_WORKER_COUNT);
526        self
527    }
528
529    /// Return the number of account proof worker threads.
530    pub const fn account_worker_count(&self) -> usize {
531        self.account_worker_count
532    }
533
534    /// Setter for the number of account proof worker threads.
535    pub fn with_account_worker_count(mut self, account_worker_count: usize) -> Self {
536        self.account_worker_count = account_worker_count.max(MIN_WORKER_COUNT);
537        self
538    }
539
540    /// Return whether V2 storage proofs are enabled.
541    pub const fn enable_proof_v2(&self) -> bool {
542        self.enable_proof_v2
543    }
544
545    /// Setter for whether to enable V2 storage proofs.
546    pub const fn with_enable_proof_v2(mut self, enable_proof_v2: bool) -> Self {
547        self.enable_proof_v2 = enable_proof_v2;
548        self
549    }
550
551    /// Returns whether cache metrics recording is disabled.
552    pub const fn disable_cache_metrics(&self) -> bool {
553        self.disable_cache_metrics
554    }
555
556    /// Setter for whether to disable cache metrics recording.
557    pub const fn without_cache_metrics(mut self, disable_cache_metrics: bool) -> Self {
558        self.disable_cache_metrics = disable_cache_metrics;
559        self
560    }
561}