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