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