reth_engine_primitives/
config.rs

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