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 = 7;
8
9/// Number of persisted blocks whose state/trie writes are masked by an in-memory suffix.
10pub const DEFAULT_NUM_STATE_MASKING_BLOCKS: u64 = 0;
11
12/// Maximum number of blocks beyond the in-memory buffer target awaiting persistence before engine
13/// API processing is stalled.
14pub const DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD: u64 = 16;
15
16/// How close to the canonical head we persist blocks.
17pub const DEFAULT_MEMORY_BLOCK_BUFFER_TARGET: u64 = 5;
18
19/// The size of proof targets chunk to spawn in one multiproof calculation.
20pub const DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE: usize = 5;
21
22/// Default number of cache hits before an invalid header entry is evicted and reprocessed.
23pub const DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD: u8 = 128;
24
25/// Gas threshold below which the small block chunk size is used.
26pub const SMALL_BLOCK_GAS_THRESHOLD: u64 = 20_000_000;
27
28/// Default number of reserved CPU cores for non-reth processes.
29///
30/// This will be deducted from the thread count of main reth global threadpool.
31pub const DEFAULT_RESERVED_CPU_CORES: usize = 1;
32
33/// Default depth for sparse trie pruning.
34///
35/// Nodes at this depth and below are converted to hash stubs to reduce memory.
36/// Depth 4 means we keep roughly 16^4 = 65536 potential branch paths at most.
37pub const DEFAULT_SPARSE_TRIE_PRUNE_DEPTH: usize = 4;
38
39/// Default timeout for the state root task before spawning a sequential fallback.
40pub const DEFAULT_STATE_ROOT_TASK_TIMEOUT: Duration = Duration::from_secs(1);
41
42const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = EPOCH_SLOTS as u32 * 2;
43const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
44const DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE: usize = 4;
45const DEFAULT_CROSS_BLOCK_CACHE_SIZE: usize = default_cross_block_cache_size();
46
47const fn assert_backpressure_threshold_invariant(
48    persistence_threshold: u64,
49    persistence_backpressure_threshold: u64,
50) {
51    debug_assert!(
52        persistence_backpressure_threshold > persistence_threshold,
53        "persistence_backpressure_threshold must be greater than persistence_threshold",
54    );
55}
56
57const fn assert_state_masking_invariant(
58    persistence_threshold: u64,
59    num_state_masking_blocks: u64,
60    memory_block_buffer_target: u64,
61) {
62    let valid_window = match num_state_masking_blocks.checked_add(memory_block_buffer_target) {
63        Some(window) => window < persistence_threshold,
64        None => false,
65    };
66    debug_assert!(
67        num_state_masking_blocks == 0 || valid_window,
68        "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold",
69    );
70}
71
72const fn default_cross_block_cache_size() -> usize {
73    if cfg!(test) {
74        1024 * 1024 // 1 MB in tests
75    } else if cfg!(target_pointer_width = "32") {
76        usize::MAX // max possible on wasm32 / 32-bit
77    } else {
78        4 * 1024 * 1024 * 1024 // 4 GB on 64-bit
79    }
80}
81
82/// Determines if the host has enough parallelism to run the payload processor.
83///
84/// It requires at least 5 parallel threads:
85/// - Engine in main thread that spawns the state root task.
86/// - Multiproof task in payload processor
87/// - Sparse Trie task in payload processor
88/// - Multiproof computation spawned in payload processor
89/// - Storage root computation spawned in trie parallel proof
90pub fn has_enough_parallelism() -> bool {
91    #[cfg(feature = "std")]
92    {
93        std::thread::available_parallelism().is_ok_and(|num| num.get() >= 5)
94    }
95    #[cfg(not(feature = "std"))]
96    false
97}
98
99/// The configuration of the engine tree.
100#[derive(Debug, Clone)]
101pub struct TreeConfig {
102    /// Maximum number of blocks to be kept only in memory without triggering
103    /// persistence.
104    persistence_threshold: u64,
105    /// Number of persisted blocks whose state/trie writes are masked instead of being durably
106    /// written in the current cycle.
107    num_state_masking_blocks: u64,
108    /// How close to the canonical head we persist blocks. Represents the ideal
109    /// number of most recent blocks to keep in memory for quick access and reorgs.
110    ///
111    /// Note: this should be less than or equal to `persistence_threshold`.
112    memory_block_buffer_target: u64,
113    /// Maximum number of blocks beyond the in-memory buffer target awaiting persistence before
114    /// engine API processing is stalled.
115    persistence_backpressure_threshold: u64,
116    /// Number of pending blocks that cannot be executed due to missing parent and
117    /// are kept in cache.
118    block_buffer_limit: u32,
119    /// Number of invalid headers to keep in cache.
120    max_invalid_header_cache_length: u32,
121    /// Number of cache hits before an invalid header entry is evicted and reprocessed.
122    ///
123    /// Setting this to `0` effectively disables the cache because entries are evicted on the
124    /// first lookup.
125    invalid_header_hit_eviction_threshold: u8,
126    /// Maximum number of blocks to execute sequentially in a batch.
127    ///
128    /// This is used as a cutoff to prevent long-running sequential block execution when we receive
129    /// a batch of downloaded blocks.
130    max_execute_block_batch_size: usize,
131    /// Whether to always compare trie updates from the state root task to the trie updates from
132    /// the regular state root calculation.
133    always_compare_trie_updates: bool,
134    /// Whether to disable state cache.
135    disable_state_cache: bool,
136    /// Whether to disable parallel prewarming.
137    disable_prewarming: bool,
138    /// Whether txpool-driven prewarming between payloads is enabled.
139    txpool_prewarming: bool,
140    /// Whether to enable state provider metrics.
141    state_provider_metrics: bool,
142    /// Cross-block cache size in bytes.
143    cross_block_cache_size: usize,
144    /// Whether the host has enough parallelism to run the state root task, see
145    /// [`has_enough_parallelism`].
146    ///
147    /// The state root task pipeline occupies at least 5 threads that block on each other (engine
148    /// main thread, multiproof task, sparse trie task, multiproof computation, storage root
149    /// computation). On hosts with fewer parallel threads these components can starve each other
150    /// and stall payload validation entirely, so state-root strategy selection
151    /// ([`Self::use_state_root_task`]) must keep falling back to synchronous state root
152    /// computation when this is `false`.
153    has_enough_parallelism: bool,
154    /// Multiproof task chunk size for proof targets.
155    multiproof_chunk_size: usize,
156    /// Number of reserved CPU cores for non-reth processes
157    reserved_cpu_cores: usize,
158    /// Whether to disable the precompile cache
159    precompile_cache_disabled: bool,
160    /// Whether to use state root fallback for testing
161    state_root_fallback: bool,
162    /// Whether to always process payload attributes and begin a payload build process
163    /// even if `forkchoiceState.headBlockHash` is already the canonical head or an ancestor.
164    ///
165    /// The Engine API specification generally states that client software "MUST NOT begin a
166    /// payload build process if `forkchoiceState.headBlockHash` references a `VALID`
167    /// ancestor of the head of canonical chain".
168    /// See: <https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#engine_forkchoiceupdatedv1> (Rule 2)
169    ///
170    /// This flag allows overriding that behavior.
171    /// This is useful for specific chain configurations (e.g., OP Stack where proposers
172    /// can reorg their own chain), various custom chains, or for development/testing purposes
173    /// where immediate payload regeneration is desired despite the head not changing or moving to
174    /// an ancestor.
175    always_process_payload_attributes_on_canonical_head: bool,
176    /// Whether to unwind canonical header to ancestor during forkchoice updates.
177    allow_unwind_canonical_header: bool,
178    /// Whether to disable cache metrics recording (can be expensive with large cached state).
179    disable_cache_metrics: bool,
180    /// Depth for sparse trie pruning after state root computation.
181    sparse_trie_prune_depth: usize,
182    /// When set, blocks whose total processing time (execution + state reads + state root +
183    /// DB commit) exceeds this duration trigger a structured `warn!` log with detailed timing,
184    /// state-operation counts, and cache hit-rate metrics. `Duration::ZERO` logs every block.
185    slow_block_threshold: Option<Duration>,
186    /// Whether to fully disable sparse trie cache pruning between blocks.
187    disable_sparse_trie_cache_pruning: bool,
188    /// Timeout for the state root task before spawning a sequential fallback computation.
189    /// If `Some`, after waiting this duration for the state root task, a sequential state root
190    /// computation is spawned in parallel and whichever finishes first is used.
191    /// If `None`, the timeout fallback is disabled.
192    state_root_task_timeout: Option<Duration>,
193    /// Whether to share execution cache with the payload builder.
194    share_execution_cache_with_payload_builder: bool,
195    /// Whether to share sparse trie with the payload builder.
196    share_sparse_trie_with_payload_builder: bool,
197    /// Whether to suppress persistence cycles while building a payload.
198    ///
199    /// When enabled, persistence is deferred while a payload job is active. This avoids
200    /// persistence I/O competing with block building on latency-sensitive chains.
201    suppress_persistence_during_build: bool,
202    /// Whether to disable BAL (Block Access List, EIP-7928) based parallel execution.
203    /// When disabled, uses the sequential execution path even when a BAL is available.
204    disable_bal_parallel_execution: bool,
205    /// Whether to disable BAL-driven parallel state root computation.
206    /// Only valid when BAL parallel execution is also disabled.
207    disable_bal_parallel_state_root: bool,
208    /// Whether to disable BAL (Block Access List) storage prefetch IO during prewarming.
209    /// When set, BAL storage slots are not read into the execution cache. BAL hashed-state
210    /// streaming for parallel state-root computation is controlled separately.
211    disable_bal_batch_io: bool,
212    /// Whether to skip trie state-root computation during engine validation.
213    ///
214    /// This trusts the block header's state root. It is intended for experiments that measure
215    /// execution without trie state-root work.
216    skip_state_root: bool,
217    /// Maximum random jitter applied before each proof computation (trie-debug only).
218    /// When set, each proof worker sleeps for a random duration up to this value
219    /// before starting a proof calculation.
220    #[cfg(feature = "trie-debug")]
221    proof_jitter: Option<Duration>,
222}
223
224impl Default for TreeConfig {
225    fn default() -> Self {
226        assert_backpressure_threshold_invariant(
227            DEFAULT_PERSISTENCE_THRESHOLD,
228            DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
229        );
230        assert_state_masking_invariant(
231            DEFAULT_PERSISTENCE_THRESHOLD,
232            DEFAULT_NUM_STATE_MASKING_BLOCKS,
233            DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
234        );
235        Self {
236            persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
237            num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
238            memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
239            persistence_backpressure_threshold: DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
240            block_buffer_limit: DEFAULT_BLOCK_BUFFER_LIMIT,
241            max_invalid_header_cache_length: DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
242            invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
243            max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE,
244            always_compare_trie_updates: false,
245            disable_state_cache: false,
246            disable_prewarming: false,
247            txpool_prewarming: false,
248            state_provider_metrics: false,
249            cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE,
250            has_enough_parallelism: has_enough_parallelism(),
251            multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
252            reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
253            precompile_cache_disabled: false,
254            state_root_fallback: false,
255            always_process_payload_attributes_on_canonical_head: false,
256            allow_unwind_canonical_header: false,
257            disable_cache_metrics: false,
258            sparse_trie_prune_depth: DEFAULT_SPARSE_TRIE_PRUNE_DEPTH,
259            slow_block_threshold: None,
260            disable_sparse_trie_cache_pruning: false,
261            state_root_task_timeout: Some(DEFAULT_STATE_ROOT_TASK_TIMEOUT),
262            share_execution_cache_with_payload_builder: false,
263            share_sparse_trie_with_payload_builder: false,
264            suppress_persistence_during_build: false,
265            disable_bal_parallel_execution: false,
266            disable_bal_parallel_state_root: false,
267            disable_bal_batch_io: false,
268            skip_state_root: false,
269            #[cfg(feature = "trie-debug")]
270            proof_jitter: None,
271        }
272    }
273}
274
275impl TreeConfig {
276    /// Create engine tree configuration.
277    #[expect(clippy::too_many_arguments)]
278    pub const fn new(
279        persistence_threshold: u64,
280        num_state_masking_blocks: u64,
281        memory_block_buffer_target: u64,
282        persistence_backpressure_threshold: u64,
283        block_buffer_limit: u32,
284        max_invalid_header_cache_length: u32,
285        invalid_header_hit_eviction_threshold: u8,
286        max_execute_block_batch_size: usize,
287        always_compare_trie_updates: bool,
288        disable_state_cache: bool,
289        disable_prewarming: bool,
290        state_provider_metrics: bool,
291        cross_block_cache_size: usize,
292        has_enough_parallelism: bool,
293        multiproof_chunk_size: usize,
294        reserved_cpu_cores: usize,
295        precompile_cache_disabled: bool,
296        state_root_fallback: bool,
297        always_process_payload_attributes_on_canonical_head: bool,
298        allow_unwind_canonical_header: bool,
299        disable_cache_metrics: bool,
300        sparse_trie_prune_depth: usize,
301        slow_block_threshold: Option<Duration>,
302        state_root_task_timeout: Option<Duration>,
303        share_execution_cache_with_payload_builder: bool,
304        share_sparse_trie_with_payload_builder: bool,
305    ) -> Self {
306        assert_backpressure_threshold_invariant(
307            persistence_threshold,
308            persistence_backpressure_threshold,
309        );
310        assert_state_masking_invariant(
311            persistence_threshold,
312            num_state_masking_blocks,
313            memory_block_buffer_target,
314        );
315        Self {
316            persistence_threshold,
317            num_state_masking_blocks,
318            memory_block_buffer_target,
319            persistence_backpressure_threshold,
320            block_buffer_limit,
321            max_invalid_header_cache_length,
322            invalid_header_hit_eviction_threshold,
323            max_execute_block_batch_size,
324            always_compare_trie_updates,
325            disable_state_cache,
326            disable_prewarming,
327            txpool_prewarming: false,
328            state_provider_metrics,
329            cross_block_cache_size,
330            has_enough_parallelism,
331            multiproof_chunk_size,
332            reserved_cpu_cores,
333            precompile_cache_disabled,
334            state_root_fallback,
335            always_process_payload_attributes_on_canonical_head,
336            allow_unwind_canonical_header,
337            disable_cache_metrics,
338            sparse_trie_prune_depth,
339            slow_block_threshold,
340            disable_sparse_trie_cache_pruning: false,
341            state_root_task_timeout,
342            share_execution_cache_with_payload_builder,
343            share_sparse_trie_with_payload_builder,
344            suppress_persistence_during_build: false,
345            disable_bal_parallel_execution: false,
346            disable_bal_parallel_state_root: false,
347            disable_bal_batch_io: false,
348            skip_state_root: false,
349            #[cfg(feature = "trie-debug")]
350            proof_jitter: None,
351        }
352    }
353
354    /// Return the persistence threshold.
355    pub const fn persistence_threshold(&self) -> u64 {
356        self.persistence_threshold
357    }
358
359    /// Return the number of persisted blocks whose state/trie writes are masked.
360    pub const fn num_state_masking_blocks(&self) -> u64 {
361        self.num_state_masking_blocks
362    }
363
364    /// Return the memory block buffer target.
365    pub const fn memory_block_buffer_target(&self) -> u64 {
366        self.memory_block_buffer_target
367    }
368
369    /// Return the persistence backpressure threshold.
370    pub const fn persistence_backpressure_threshold(&self) -> u64 {
371        self.persistence_backpressure_threshold
372    }
373
374    /// Return the block buffer limit.
375    pub const fn block_buffer_limit(&self) -> u32 {
376        self.block_buffer_limit
377    }
378
379    /// Return the maximum invalid cache header length.
380    pub const fn max_invalid_header_cache_length(&self) -> u32 {
381        self.max_invalid_header_cache_length
382    }
383
384    /// Return the invalid header cache hit eviction threshold.
385    ///
386    /// Setting this to `0` effectively disables the cache because entries are evicted on the
387    /// first lookup.
388    pub const fn invalid_header_hit_eviction_threshold(&self) -> u8 {
389        self.invalid_header_hit_eviction_threshold
390    }
391
392    /// Return the maximum execute block batch size.
393    pub const fn max_execute_block_batch_size(&self) -> usize {
394        self.max_execute_block_batch_size
395    }
396
397    /// Return the multiproof task chunk size.
398    pub const fn multiproof_chunk_size(&self) -> usize {
399        self.multiproof_chunk_size
400    }
401
402    /// Return the effective multiproof task chunk size.
403    pub const fn effective_multiproof_chunk_size(&self) -> usize {
404        self.multiproof_chunk_size
405    }
406
407    /// Return the number of reserved CPU cores for non-reth processes
408    pub const fn reserved_cpu_cores(&self) -> usize {
409        self.reserved_cpu_cores
410    }
411
412    /// Returns whether or not state provider metrics are enabled.
413    pub const fn state_provider_metrics(&self) -> bool {
414        self.state_provider_metrics
415    }
416
417    /// Returns whether or not state cache is disabled.
418    pub const fn disable_state_cache(&self) -> bool {
419        self.disable_state_cache
420    }
421
422    /// Returns whether or not parallel prewarming is disabled.
423    pub const fn disable_prewarming(&self) -> bool {
424        self.disable_prewarming
425    }
426
427    /// Returns whether txpool prewarming is enabled.
428    pub const fn txpool_prewarming(&self) -> bool {
429        self.txpool_prewarming
430    }
431
432    /// Returns whether to always compare trie updates from the state root task to the trie updates
433    /// from the regular state root calculation.
434    pub const fn always_compare_trie_updates(&self) -> bool {
435        self.always_compare_trie_updates
436    }
437
438    /// Returns the cross-block cache size.
439    pub const fn cross_block_cache_size(&self) -> usize {
440        self.cross_block_cache_size
441    }
442
443    /// Returns whether precompile cache is disabled.
444    pub const fn precompile_cache_disabled(&self) -> bool {
445        self.precompile_cache_disabled
446    }
447
448    /// Returns whether to use state root fallback.
449    pub const fn state_root_fallback(&self) -> bool {
450        self.state_root_fallback
451    }
452
453    /// Sets whether to always process payload attributes when the FCU head is already canonical.
454    pub const fn with_always_process_payload_attributes_on_canonical_head(
455        mut self,
456        always_process_payload_attributes_on_canonical_head: bool,
457    ) -> Self {
458        self.always_process_payload_attributes_on_canonical_head =
459            always_process_payload_attributes_on_canonical_head;
460        self
461    }
462
463    /// Returns true if payload attributes should always be processed even when the FCU head is
464    /// canonical.
465    pub const fn always_process_payload_attributes_on_canonical_head(&self) -> bool {
466        self.always_process_payload_attributes_on_canonical_head
467    }
468
469    /// Returns true if canonical header should be unwound to ancestor during forkchoice updates.
470    pub const fn unwind_canonical_header(&self) -> bool {
471        self.allow_unwind_canonical_header
472    }
473
474    /// Setter for persistence threshold.
475    pub const fn with_persistence_threshold(mut self, persistence_threshold: u64) -> Self {
476        self.persistence_threshold = persistence_threshold;
477        assert_backpressure_threshold_invariant(
478            self.persistence_threshold,
479            self.persistence_backpressure_threshold,
480        );
481        assert_state_masking_invariant(
482            self.persistence_threshold,
483            self.num_state_masking_blocks,
484            self.memory_block_buffer_target,
485        );
486        self
487    }
488
489    /// Setter for the number of persisted blocks whose state/trie writes are masked.
490    pub const fn with_num_state_masking_blocks(mut self, num_state_masking_blocks: u64) -> Self {
491        self.num_state_masking_blocks = num_state_masking_blocks;
492        assert_state_masking_invariant(
493            self.persistence_threshold,
494            self.num_state_masking_blocks,
495            self.memory_block_buffer_target,
496        );
497        self
498    }
499
500    /// Setter for memory block buffer target.
501    pub const fn with_memory_block_buffer_target(
502        mut self,
503        memory_block_buffer_target: u64,
504    ) -> Self {
505        self.memory_block_buffer_target = memory_block_buffer_target;
506        assert_state_masking_invariant(
507            self.persistence_threshold,
508            self.num_state_masking_blocks,
509            self.memory_block_buffer_target,
510        );
511        self
512    }
513
514    /// Setter for persistence backpressure threshold.
515    pub const fn with_persistence_backpressure_threshold(
516        mut self,
517        persistence_backpressure_threshold: u64,
518    ) -> Self {
519        self.persistence_backpressure_threshold = persistence_backpressure_threshold;
520        assert_backpressure_threshold_invariant(
521            self.persistence_threshold,
522            self.persistence_backpressure_threshold,
523        );
524        self
525    }
526
527    /// Setter for block buffer limit.
528    pub const fn with_block_buffer_limit(mut self, block_buffer_limit: u32) -> Self {
529        self.block_buffer_limit = block_buffer_limit;
530        self
531    }
532
533    /// Setter for maximum invalid header cache length.
534    pub const fn with_max_invalid_header_cache_length(
535        mut self,
536        max_invalid_header_cache_length: u32,
537    ) -> Self {
538        self.max_invalid_header_cache_length = max_invalid_header_cache_length;
539        self
540    }
541
542    /// Setter for the invalid header cache hit eviction threshold.
543    pub const fn with_invalid_header_hit_eviction_threshold(
544        mut self,
545        invalid_header_hit_eviction_threshold: u8,
546    ) -> Self {
547        self.invalid_header_hit_eviction_threshold = invalid_header_hit_eviction_threshold;
548        self
549    }
550
551    /// Setter for maximum execute block batch size.
552    pub const fn with_max_execute_block_batch_size(
553        mut self,
554        max_execute_block_batch_size: usize,
555    ) -> Self {
556        self.max_execute_block_batch_size = max_execute_block_batch_size;
557        self
558    }
559
560    /// Setter for whether to disable state cache.
561    pub const fn without_state_cache(mut self, disable_state_cache: bool) -> Self {
562        self.disable_state_cache = disable_state_cache;
563        self
564    }
565
566    /// Setter for whether to disable parallel prewarming.
567    pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self {
568        self.disable_prewarming = disable_prewarming;
569        self
570    }
571
572    /// Enables or disables txpool transaction prewarming.
573    pub const fn with_txpool_prewarming(mut self, enabled: bool) -> Self {
574        self.txpool_prewarming = enabled;
575        self
576    }
577
578    /// Setter for whether to always compare trie updates from the state root task to the trie
579    /// updates from the regular state root calculation.
580    pub const fn with_always_compare_trie_updates(
581        mut self,
582        always_compare_trie_updates: bool,
583    ) -> Self {
584        self.always_compare_trie_updates = always_compare_trie_updates;
585        self
586    }
587
588    /// Setter for cross block cache size.
589    pub const fn with_cross_block_cache_size(mut self, cross_block_cache_size: usize) -> Self {
590        self.cross_block_cache_size = cross_block_cache_size;
591        self
592    }
593
594    /// Setter for has enough parallelism.
595    pub const fn with_has_enough_parallelism(mut self, has_enough_parallelism: bool) -> Self {
596        self.has_enough_parallelism = has_enough_parallelism;
597        self
598    }
599
600    /// Returns whether the host has enough parallelism to run the state root task.
601    pub const fn has_enough_parallelism(&self) -> bool {
602        self.has_enough_parallelism
603    }
604
605    /// Returns whether engine validation should use the state root task.
606    ///
607    /// The state root task requires at least 5 parallel threads, see
608    /// [`has_enough_parallelism`].
609    pub const fn use_state_root_task(&self) -> bool {
610        !self.skip_state_root && !self.state_root_fallback && self.has_enough_parallelism
611    }
612
613    /// Setter for state provider metrics.
614    pub const fn with_state_provider_metrics(mut self, state_provider_metrics: bool) -> Self {
615        self.state_provider_metrics = state_provider_metrics;
616        self
617    }
618
619    /// Setter for multiproof task chunk size for proof targets.
620    pub const fn with_multiproof_chunk_size(mut self, multiproof_chunk_size: usize) -> Self {
621        self.multiproof_chunk_size = multiproof_chunk_size;
622        self
623    }
624
625    /// Setter for the number of reserved CPU cores for any non-reth processes
626    pub const fn with_reserved_cpu_cores(mut self, reserved_cpu_cores: usize) -> Self {
627        self.reserved_cpu_cores = reserved_cpu_cores;
628        self
629    }
630
631    /// Setter for whether to disable the precompile cache.
632    pub const fn without_precompile_cache(mut self, precompile_cache_disabled: bool) -> Self {
633        self.precompile_cache_disabled = precompile_cache_disabled;
634        self
635    }
636
637    /// Setter for whether to use state root fallback, useful for testing.
638    pub const fn with_state_root_fallback(mut self, state_root_fallback: bool) -> Self {
639        self.state_root_fallback = state_root_fallback;
640        self
641    }
642
643    /// Setter for whether to unwind canonical header to ancestor during forkchoice updates.
644    pub const fn with_unwind_canonical_header(mut self, unwind_canonical_header: bool) -> Self {
645        self.allow_unwind_canonical_header = unwind_canonical_header;
646        self
647    }
648
649    /// Returns whether cache metrics recording is disabled.
650    pub const fn disable_cache_metrics(&self) -> bool {
651        self.disable_cache_metrics
652    }
653
654    /// Setter for whether to disable cache metrics recording.
655    pub const fn without_cache_metrics(mut self, disable_cache_metrics: bool) -> Self {
656        self.disable_cache_metrics = disable_cache_metrics;
657        self
658    }
659
660    /// Returns the sparse trie prune depth.
661    pub const fn sparse_trie_prune_depth(&self) -> usize {
662        self.sparse_trie_prune_depth
663    }
664
665    /// Setter for sparse trie prune depth.
666    pub const fn with_sparse_trie_prune_depth(mut self, depth: usize) -> Self {
667        self.sparse_trie_prune_depth = depth;
668        self
669    }
670
671    /// Returns the slow block threshold, if configured.
672    ///
673    /// When `Some`, blocks whose total processing time exceeds this duration emit a structured
674    /// warning with timing, state-operation, and cache-hit-rate details. `Duration::ZERO` logs
675    /// every block.
676    pub const fn slow_block_threshold(&self) -> Option<Duration> {
677        self.slow_block_threshold
678    }
679
680    /// Setter for slow block threshold.
681    pub const fn with_slow_block_threshold(
682        mut self,
683        slow_block_threshold: Option<Duration>,
684    ) -> Self {
685        self.slow_block_threshold = slow_block_threshold;
686        self
687    }
688
689    /// Returns whether sparse trie cache pruning is disabled.
690    pub const fn disable_sparse_trie_cache_pruning(&self) -> bool {
691        self.disable_sparse_trie_cache_pruning
692    }
693
694    /// Setter for whether to disable sparse trie cache pruning.
695    pub const fn with_disable_sparse_trie_cache_pruning(mut self, value: bool) -> Self {
696        self.disable_sparse_trie_cache_pruning = value;
697        self
698    }
699
700    /// Returns the state root task timeout.
701    pub const fn state_root_task_timeout(&self) -> Option<Duration> {
702        self.state_root_task_timeout
703    }
704
705    /// Setter for state root task timeout.
706    pub const fn with_state_root_task_timeout(mut self, timeout: Option<Duration>) -> Self {
707        self.state_root_task_timeout = timeout;
708        self
709    }
710
711    /// Returns whether to share execution cache with the payload builder.
712    pub const fn share_execution_cache_with_payload_builder(&self) -> bool {
713        self.share_execution_cache_with_payload_builder
714    }
715
716    /// Returns whether to share sparse trie with the payload builder.
717    pub const fn share_sparse_trie_with_payload_builder(&self) -> bool {
718        self.share_sparse_trie_with_payload_builder
719    }
720
721    /// Setter for whether to share execution cache with the payload builder.
722    pub const fn with_share_execution_cache_with_payload_builder(
723        mut self,
724        share_execution_cache_with_payload_builder: bool,
725    ) -> Self {
726        self.share_execution_cache_with_payload_builder =
727            share_execution_cache_with_payload_builder;
728        self
729    }
730
731    /// Setter for whether to share sparse trie with the payload builder.
732    pub const fn with_share_sparse_trie_with_payload_builder(
733        mut self,
734        share_sparse_trie_with_payload_builder: bool,
735    ) -> Self {
736        self.share_sparse_trie_with_payload_builder = share_sparse_trie_with_payload_builder;
737        self
738    }
739
740    /// Returns whether persistence is suppressed during payload building.
741    pub const fn suppress_persistence_during_build(&self) -> bool {
742        self.suppress_persistence_during_build
743    }
744
745    /// Setter for whether to suppress persistence during payload building.
746    pub const fn with_suppress_persistence_during_build(mut self, value: bool) -> Self {
747        self.suppress_persistence_during_build = value;
748        self
749    }
750
751    /// Returns whether BAL-based parallel execution is disabled.
752    pub const fn disable_bal_parallel_execution(&self) -> bool {
753        self.disable_bal_parallel_execution
754    }
755
756    /// Setter for whether to disable BAL-based parallel execution.
757    pub const fn without_bal_parallel_execution(
758        mut self,
759        disable_bal_parallel_execution: bool,
760    ) -> Self {
761        self.disable_bal_parallel_execution = disable_bal_parallel_execution;
762        self
763    }
764
765    /// Returns whether BAL-driven parallel state root computation is disabled.
766    pub const fn disable_bal_parallel_state_root(&self) -> bool {
767        self.disable_bal_parallel_state_root
768    }
769
770    /// Setter for whether to disable BAL-driven parallel state root computation.
771    pub const fn without_bal_parallel_state_root(
772        mut self,
773        disable_bal_parallel_state_root: bool,
774    ) -> Self {
775        self.disable_bal_parallel_state_root = disable_bal_parallel_state_root;
776        self
777    }
778
779    /// Returns whether BAL state prefetching during prewarm is disabled.
780    pub const fn disable_bal_batch_io(&self) -> bool {
781        self.disable_bal_batch_io
782    }
783
784    /// Setter for whether to disable BAL state prefetching during prewarm.
785    pub const fn without_bal_batch_io(mut self, disable_bal_batch_io: bool) -> Self {
786        self.disable_bal_batch_io = disable_bal_batch_io;
787        self
788    }
789
790    /// Returns whether trie state-root computation is skipped during engine validation.
791    pub const fn skip_state_root(&self) -> bool {
792        self.skip_state_root
793    }
794
795    /// Setter for whether to skip trie state-root computation during engine validation.
796    pub const fn with_skip_state_root(mut self, skip_state_root: bool) -> Self {
797        self.skip_state_root = skip_state_root;
798        self
799    }
800
801    /// Returns the proof jitter duration, if configured (trie-debug only).
802    #[cfg(feature = "trie-debug")]
803    pub const fn proof_jitter(&self) -> Option<Duration> {
804        self.proof_jitter
805    }
806
807    /// Setter for proof jitter (trie-debug only).
808    #[cfg(feature = "trie-debug")]
809    pub const fn with_proof_jitter(mut self, proof_jitter: Option<Duration>) -> Self {
810        self.proof_jitter = proof_jitter;
811        self
812    }
813}
814
815#[cfg(test)]
816mod tests {
817    use super::{TreeConfig, DEFAULT_NUM_STATE_MASKING_BLOCKS};
818
819    #[test]
820    fn txpool_prewarming_is_disabled_by_default_and_can_be_enabled() {
821        assert!(!TreeConfig::default().txpool_prewarming());
822        assert!(TreeConfig::default().with_txpool_prewarming(true).txpool_prewarming());
823    }
824
825    #[test]
826    fn state_root_task_requires_parallelism_without_overrides() {
827        assert!(TreeConfig::default().with_has_enough_parallelism(true).use_state_root_task());
828        assert!(!TreeConfig::default().with_has_enough_parallelism(false).use_state_root_task());
829        assert!(!TreeConfig::default()
830            .with_has_enough_parallelism(true)
831            .with_state_root_fallback(true)
832            .use_state_root_task());
833        assert!(!TreeConfig::default()
834            .with_has_enough_parallelism(true)
835            .with_skip_state_root(true)
836            .use_state_root_task());
837    }
838
839    #[test]
840    #[should_panic(
841        expected = "persistence_backpressure_threshold must be greater than persistence_threshold"
842    )]
843    fn rejects_backpressure_threshold_at_or_below_persistence_threshold() {
844        let _ = TreeConfig::default()
845            .with_persistence_threshold(4)
846            .with_persistence_backpressure_threshold(4);
847    }
848
849    #[test]
850    fn state_masking_is_disabled_by_default() {
851        assert_eq!(
852            TreeConfig::default().num_state_masking_blocks(),
853            DEFAULT_NUM_STATE_MASKING_BLOCKS
854        );
855    }
856
857    #[test]
858    #[should_panic(
859        expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
860    )]
861    fn rejects_state_masking_window_at_or_above_persistence_threshold() {
862        let _ = TreeConfig::default()
863            .with_persistence_threshold(4)
864            .with_memory_block_buffer_target(2)
865            .with_num_state_masking_blocks(2);
866    }
867
868    #[test]
869    #[should_panic(
870        expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
871    )]
872    fn rejects_overflowing_state_masking_window() {
873        let _ = TreeConfig::default().with_num_state_masking_blocks(u64::MAX);
874    }
875}