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