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