Skip to main content

reth_engine_primitives/
config.rs

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