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