1use alloy_eips::merge::EPOCH_SLOTS;
4use core::time::Duration;
5
6pub const DEFAULT_PERSISTENCE_THRESHOLD: u64 = 7;
8
9pub const DEFAULT_NUM_STATE_MASKING_BLOCKS: u64 = 0;
11
12pub const DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD: u64 = 16;
15
16pub const DEFAULT_MEMORY_BLOCK_BUFFER_TARGET: u64 = 5;
18
19pub const DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE: usize = 5;
21
22pub const DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD: u8 = 128;
24
25pub const SMALL_BLOCK_GAS_THRESHOLD: u64 = 20_000_000;
27
28pub const DEFAULT_RESERVED_CPU_CORES: usize = 1;
32
33pub const DEFAULT_SPARSE_TRIE_PRUNE_DEPTH: usize = 4;
38
39pub const DEFAULT_STATE_ROOT_TASK_TIMEOUT: Duration = Duration::from_secs(1);
41
42const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = EPOCH_SLOTS as u32 * 2;
43const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
44const DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE: usize = 4;
45const DEFAULT_CROSS_BLOCK_CACHE_SIZE: usize = default_cross_block_cache_size();
46
47const fn assert_backpressure_threshold_invariant(
48 persistence_threshold: u64,
49 persistence_backpressure_threshold: u64,
50) {
51 debug_assert!(
52 persistence_backpressure_threshold > persistence_threshold,
53 "persistence_backpressure_threshold must be greater than persistence_threshold",
54 );
55}
56
57const fn assert_state_masking_invariant(
58 persistence_threshold: u64,
59 num_state_masking_blocks: u64,
60 memory_block_buffer_target: u64,
61) {
62 let valid_window = match num_state_masking_blocks.checked_add(memory_block_buffer_target) {
63 Some(window) => window < persistence_threshold,
64 None => false,
65 };
66 debug_assert!(
67 num_state_masking_blocks == 0 || valid_window,
68 "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold",
69 );
70}
71
72const fn default_cross_block_cache_size() -> usize {
73 if cfg!(test) {
74 1024 * 1024 } else if cfg!(target_pointer_width = "32") {
76 usize::MAX } else {
78 4 * 1024 * 1024 * 1024 }
80}
81
82pub fn has_enough_parallelism() -> bool {
91 #[cfg(feature = "std")]
92 {
93 std::thread::available_parallelism().is_ok_and(|num| num.get() >= 5)
94 }
95 #[cfg(not(feature = "std"))]
96 false
97}
98
99#[derive(Debug, Clone)]
101pub struct TreeConfig {
102 persistence_threshold: u64,
105 num_state_masking_blocks: u64,
108 memory_block_buffer_target: u64,
113 persistence_backpressure_threshold: u64,
116 block_buffer_limit: u32,
119 max_invalid_header_cache_length: u32,
121 invalid_header_hit_eviction_threshold: u8,
126 max_execute_block_batch_size: usize,
131 always_compare_trie_updates: bool,
134 disable_state_cache: bool,
136 disable_prewarming: bool,
138 txpool_prewarming: bool,
140 state_provider_metrics: bool,
142 cross_block_cache_size: usize,
144 has_enough_parallelism: bool,
154 multiproof_chunk_size: usize,
156 reserved_cpu_cores: usize,
158 precompile_cache_disabled: bool,
160 state_root_fallback: bool,
162 always_process_payload_attributes_on_canonical_head: bool,
176 allow_unwind_canonical_header: bool,
178 disable_cache_metrics: bool,
180 sparse_trie_prune_depth: usize,
182 slow_block_threshold: Option<Duration>,
186 disable_sparse_trie_cache_pruning: bool,
188 state_root_task_timeout: Option<Duration>,
193 share_execution_cache_with_payload_builder: bool,
195 share_sparse_trie_with_payload_builder: bool,
197 suppress_persistence_during_build: bool,
202 disable_bal_parallel_execution: bool,
205 disable_bal_parallel_state_root: bool,
208 disable_bal_batch_io: bool,
212 skip_state_root: bool,
217 #[cfg(feature = "trie-debug")]
221 proof_jitter: Option<Duration>,
222}
223
224impl Default for TreeConfig {
225 fn default() -> Self {
226 assert_backpressure_threshold_invariant(
227 DEFAULT_PERSISTENCE_THRESHOLD,
228 DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
229 );
230 assert_state_masking_invariant(
231 DEFAULT_PERSISTENCE_THRESHOLD,
232 DEFAULT_NUM_STATE_MASKING_BLOCKS,
233 DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
234 );
235 Self {
236 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
237 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
238 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
239 persistence_backpressure_threshold: DEFAULT_PERSISTENCE_BACKPRESSURE_THRESHOLD,
240 block_buffer_limit: DEFAULT_BLOCK_BUFFER_LIMIT,
241 max_invalid_header_cache_length: DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
242 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
243 max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE,
244 always_compare_trie_updates: false,
245 disable_state_cache: false,
246 disable_prewarming: false,
247 txpool_prewarming: false,
248 state_provider_metrics: false,
249 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE,
250 has_enough_parallelism: has_enough_parallelism(),
251 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
252 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
253 precompile_cache_disabled: false,
254 state_root_fallback: false,
255 always_process_payload_attributes_on_canonical_head: false,
256 allow_unwind_canonical_header: false,
257 disable_cache_metrics: false,
258 sparse_trie_prune_depth: DEFAULT_SPARSE_TRIE_PRUNE_DEPTH,
259 slow_block_threshold: None,
260 disable_sparse_trie_cache_pruning: false,
261 state_root_task_timeout: Some(DEFAULT_STATE_ROOT_TASK_TIMEOUT),
262 share_execution_cache_with_payload_builder: false,
263 share_sparse_trie_with_payload_builder: false,
264 suppress_persistence_during_build: false,
265 disable_bal_parallel_execution: false,
266 disable_bal_parallel_state_root: false,
267 disable_bal_batch_io: false,
268 skip_state_root: false,
269 #[cfg(feature = "trie-debug")]
270 proof_jitter: None,
271 }
272 }
273}
274
275impl TreeConfig {
276 #[expect(clippy::too_many_arguments)]
278 pub const fn new(
279 persistence_threshold: u64,
280 num_state_masking_blocks: u64,
281 memory_block_buffer_target: u64,
282 persistence_backpressure_threshold: u64,
283 block_buffer_limit: u32,
284 max_invalid_header_cache_length: u32,
285 invalid_header_hit_eviction_threshold: u8,
286 max_execute_block_batch_size: usize,
287 always_compare_trie_updates: bool,
288 disable_state_cache: bool,
289 disable_prewarming: bool,
290 state_provider_metrics: bool,
291 cross_block_cache_size: usize,
292 has_enough_parallelism: bool,
293 multiproof_chunk_size: usize,
294 reserved_cpu_cores: usize,
295 precompile_cache_disabled: bool,
296 state_root_fallback: bool,
297 always_process_payload_attributes_on_canonical_head: bool,
298 allow_unwind_canonical_header: bool,
299 disable_cache_metrics: bool,
300 sparse_trie_prune_depth: usize,
301 slow_block_threshold: Option<Duration>,
302 state_root_task_timeout: Option<Duration>,
303 share_execution_cache_with_payload_builder: bool,
304 share_sparse_trie_with_payload_builder: bool,
305 ) -> Self {
306 assert_backpressure_threshold_invariant(
307 persistence_threshold,
308 persistence_backpressure_threshold,
309 );
310 assert_state_masking_invariant(
311 persistence_threshold,
312 num_state_masking_blocks,
313 memory_block_buffer_target,
314 );
315 Self {
316 persistence_threshold,
317 num_state_masking_blocks,
318 memory_block_buffer_target,
319 persistence_backpressure_threshold,
320 block_buffer_limit,
321 max_invalid_header_cache_length,
322 invalid_header_hit_eviction_threshold,
323 max_execute_block_batch_size,
324 always_compare_trie_updates,
325 disable_state_cache,
326 disable_prewarming,
327 txpool_prewarming: false,
328 state_provider_metrics,
329 cross_block_cache_size,
330 has_enough_parallelism,
331 multiproof_chunk_size,
332 reserved_cpu_cores,
333 precompile_cache_disabled,
334 state_root_fallback,
335 always_process_payload_attributes_on_canonical_head,
336 allow_unwind_canonical_header,
337 disable_cache_metrics,
338 sparse_trie_prune_depth,
339 slow_block_threshold,
340 disable_sparse_trie_cache_pruning: false,
341 state_root_task_timeout,
342 share_execution_cache_with_payload_builder,
343 share_sparse_trie_with_payload_builder,
344 suppress_persistence_during_build: false,
345 disable_bal_parallel_execution: false,
346 disable_bal_parallel_state_root: false,
347 disable_bal_batch_io: false,
348 skip_state_root: false,
349 #[cfg(feature = "trie-debug")]
350 proof_jitter: None,
351 }
352 }
353
354 pub const fn persistence_threshold(&self) -> u64 {
356 self.persistence_threshold
357 }
358
359 pub const fn num_state_masking_blocks(&self) -> u64 {
361 self.num_state_masking_blocks
362 }
363
364 pub const fn memory_block_buffer_target(&self) -> u64 {
366 self.memory_block_buffer_target
367 }
368
369 pub const fn persistence_backpressure_threshold(&self) -> u64 {
371 self.persistence_backpressure_threshold
372 }
373
374 pub const fn block_buffer_limit(&self) -> u32 {
376 self.block_buffer_limit
377 }
378
379 pub const fn max_invalid_header_cache_length(&self) -> u32 {
381 self.max_invalid_header_cache_length
382 }
383
384 pub const fn invalid_header_hit_eviction_threshold(&self) -> u8 {
389 self.invalid_header_hit_eviction_threshold
390 }
391
392 pub const fn max_execute_block_batch_size(&self) -> usize {
394 self.max_execute_block_batch_size
395 }
396
397 pub const fn multiproof_chunk_size(&self) -> usize {
399 self.multiproof_chunk_size
400 }
401
402 pub const fn effective_multiproof_chunk_size(&self) -> usize {
404 self.multiproof_chunk_size
405 }
406
407 pub const fn reserved_cpu_cores(&self) -> usize {
409 self.reserved_cpu_cores
410 }
411
412 pub const fn state_provider_metrics(&self) -> bool {
414 self.state_provider_metrics
415 }
416
417 pub const fn disable_state_cache(&self) -> bool {
419 self.disable_state_cache
420 }
421
422 pub const fn disable_prewarming(&self) -> bool {
424 self.disable_prewarming
425 }
426
427 pub const fn txpool_prewarming(&self) -> bool {
429 self.txpool_prewarming
430 }
431
432 pub const fn always_compare_trie_updates(&self) -> bool {
435 self.always_compare_trie_updates
436 }
437
438 pub const fn cross_block_cache_size(&self) -> usize {
440 self.cross_block_cache_size
441 }
442
443 pub const fn precompile_cache_disabled(&self) -> bool {
445 self.precompile_cache_disabled
446 }
447
448 pub const fn state_root_fallback(&self) -> bool {
450 self.state_root_fallback
451 }
452
453 pub const fn with_always_process_payload_attributes_on_canonical_head(
455 mut self,
456 always_process_payload_attributes_on_canonical_head: bool,
457 ) -> Self {
458 self.always_process_payload_attributes_on_canonical_head =
459 always_process_payload_attributes_on_canonical_head;
460 self
461 }
462
463 pub const fn always_process_payload_attributes_on_canonical_head(&self) -> bool {
466 self.always_process_payload_attributes_on_canonical_head
467 }
468
469 pub const fn unwind_canonical_header(&self) -> bool {
471 self.allow_unwind_canonical_header
472 }
473
474 pub const fn with_persistence_threshold(mut self, persistence_threshold: u64) -> Self {
476 self.persistence_threshold = persistence_threshold;
477 assert_backpressure_threshold_invariant(
478 self.persistence_threshold,
479 self.persistence_backpressure_threshold,
480 );
481 assert_state_masking_invariant(
482 self.persistence_threshold,
483 self.num_state_masking_blocks,
484 self.memory_block_buffer_target,
485 );
486 self
487 }
488
489 pub const fn with_num_state_masking_blocks(mut self, num_state_masking_blocks: u64) -> Self {
491 self.num_state_masking_blocks = num_state_masking_blocks;
492 assert_state_masking_invariant(
493 self.persistence_threshold,
494 self.num_state_masking_blocks,
495 self.memory_block_buffer_target,
496 );
497 self
498 }
499
500 pub const fn with_memory_block_buffer_target(
502 mut self,
503 memory_block_buffer_target: u64,
504 ) -> Self {
505 self.memory_block_buffer_target = memory_block_buffer_target;
506 assert_state_masking_invariant(
507 self.persistence_threshold,
508 self.num_state_masking_blocks,
509 self.memory_block_buffer_target,
510 );
511 self
512 }
513
514 pub const fn with_persistence_backpressure_threshold(
516 mut self,
517 persistence_backpressure_threshold: u64,
518 ) -> Self {
519 self.persistence_backpressure_threshold = persistence_backpressure_threshold;
520 assert_backpressure_threshold_invariant(
521 self.persistence_threshold,
522 self.persistence_backpressure_threshold,
523 );
524 self
525 }
526
527 pub const fn with_block_buffer_limit(mut self, block_buffer_limit: u32) -> Self {
529 self.block_buffer_limit = block_buffer_limit;
530 self
531 }
532
533 pub const fn with_max_invalid_header_cache_length(
535 mut self,
536 max_invalid_header_cache_length: u32,
537 ) -> Self {
538 self.max_invalid_header_cache_length = max_invalid_header_cache_length;
539 self
540 }
541
542 pub const fn with_invalid_header_hit_eviction_threshold(
544 mut self,
545 invalid_header_hit_eviction_threshold: u8,
546 ) -> Self {
547 self.invalid_header_hit_eviction_threshold = invalid_header_hit_eviction_threshold;
548 self
549 }
550
551 pub const fn with_max_execute_block_batch_size(
553 mut self,
554 max_execute_block_batch_size: usize,
555 ) -> Self {
556 self.max_execute_block_batch_size = max_execute_block_batch_size;
557 self
558 }
559
560 pub const fn without_state_cache(mut self, disable_state_cache: bool) -> Self {
562 self.disable_state_cache = disable_state_cache;
563 self
564 }
565
566 pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self {
568 self.disable_prewarming = disable_prewarming;
569 self
570 }
571
572 pub const fn with_txpool_prewarming(mut self, enabled: bool) -> Self {
574 self.txpool_prewarming = enabled;
575 self
576 }
577
578 pub const fn with_always_compare_trie_updates(
581 mut self,
582 always_compare_trie_updates: bool,
583 ) -> Self {
584 self.always_compare_trie_updates = always_compare_trie_updates;
585 self
586 }
587
588 pub const fn with_cross_block_cache_size(mut self, cross_block_cache_size: usize) -> Self {
590 self.cross_block_cache_size = cross_block_cache_size;
591 self
592 }
593
594 pub const fn with_has_enough_parallelism(mut self, has_enough_parallelism: bool) -> Self {
596 self.has_enough_parallelism = has_enough_parallelism;
597 self
598 }
599
600 pub const fn has_enough_parallelism(&self) -> bool {
602 self.has_enough_parallelism
603 }
604
605 pub const fn use_state_root_task(&self) -> bool {
610 !self.skip_state_root && !self.state_root_fallback && self.has_enough_parallelism
611 }
612
613 pub const fn with_state_provider_metrics(mut self, state_provider_metrics: bool) -> Self {
615 self.state_provider_metrics = state_provider_metrics;
616 self
617 }
618
619 pub const fn with_multiproof_chunk_size(mut self, multiproof_chunk_size: usize) -> Self {
621 self.multiproof_chunk_size = multiproof_chunk_size;
622 self
623 }
624
625 pub const fn with_reserved_cpu_cores(mut self, reserved_cpu_cores: usize) -> Self {
627 self.reserved_cpu_cores = reserved_cpu_cores;
628 self
629 }
630
631 pub const fn without_precompile_cache(mut self, precompile_cache_disabled: bool) -> Self {
633 self.precompile_cache_disabled = precompile_cache_disabled;
634 self
635 }
636
637 pub const fn with_state_root_fallback(mut self, state_root_fallback: bool) -> Self {
639 self.state_root_fallback = state_root_fallback;
640 self
641 }
642
643 pub const fn with_unwind_canonical_header(mut self, unwind_canonical_header: bool) -> Self {
645 self.allow_unwind_canonical_header = unwind_canonical_header;
646 self
647 }
648
649 pub const fn disable_cache_metrics(&self) -> bool {
651 self.disable_cache_metrics
652 }
653
654 pub const fn without_cache_metrics(mut self, disable_cache_metrics: bool) -> Self {
656 self.disable_cache_metrics = disable_cache_metrics;
657 self
658 }
659
660 pub const fn sparse_trie_prune_depth(&self) -> usize {
662 self.sparse_trie_prune_depth
663 }
664
665 pub const fn with_sparse_trie_prune_depth(mut self, depth: usize) -> Self {
667 self.sparse_trie_prune_depth = depth;
668 self
669 }
670
671 pub const fn slow_block_threshold(&self) -> Option<Duration> {
677 self.slow_block_threshold
678 }
679
680 pub const fn with_slow_block_threshold(
682 mut self,
683 slow_block_threshold: Option<Duration>,
684 ) -> Self {
685 self.slow_block_threshold = slow_block_threshold;
686 self
687 }
688
689 pub const fn disable_sparse_trie_cache_pruning(&self) -> bool {
691 self.disable_sparse_trie_cache_pruning
692 }
693
694 pub const fn with_disable_sparse_trie_cache_pruning(mut self, value: bool) -> Self {
696 self.disable_sparse_trie_cache_pruning = value;
697 self
698 }
699
700 pub const fn state_root_task_timeout(&self) -> Option<Duration> {
702 self.state_root_task_timeout
703 }
704
705 pub const fn with_state_root_task_timeout(mut self, timeout: Option<Duration>) -> Self {
707 self.state_root_task_timeout = timeout;
708 self
709 }
710
711 pub const fn share_execution_cache_with_payload_builder(&self) -> bool {
713 self.share_execution_cache_with_payload_builder
714 }
715
716 pub const fn share_sparse_trie_with_payload_builder(&self) -> bool {
718 self.share_sparse_trie_with_payload_builder
719 }
720
721 pub const fn with_share_execution_cache_with_payload_builder(
723 mut self,
724 share_execution_cache_with_payload_builder: bool,
725 ) -> Self {
726 self.share_execution_cache_with_payload_builder =
727 share_execution_cache_with_payload_builder;
728 self
729 }
730
731 pub const fn with_share_sparse_trie_with_payload_builder(
733 mut self,
734 share_sparse_trie_with_payload_builder: bool,
735 ) -> Self {
736 self.share_sparse_trie_with_payload_builder = share_sparse_trie_with_payload_builder;
737 self
738 }
739
740 pub const fn suppress_persistence_during_build(&self) -> bool {
742 self.suppress_persistence_during_build
743 }
744
745 pub const fn with_suppress_persistence_during_build(mut self, value: bool) -> Self {
747 self.suppress_persistence_during_build = value;
748 self
749 }
750
751 pub const fn disable_bal_parallel_execution(&self) -> bool {
753 self.disable_bal_parallel_execution
754 }
755
756 pub const fn without_bal_parallel_execution(
758 mut self,
759 disable_bal_parallel_execution: bool,
760 ) -> Self {
761 self.disable_bal_parallel_execution = disable_bal_parallel_execution;
762 self
763 }
764
765 pub const fn disable_bal_parallel_state_root(&self) -> bool {
767 self.disable_bal_parallel_state_root
768 }
769
770 pub const fn without_bal_parallel_state_root(
772 mut self,
773 disable_bal_parallel_state_root: bool,
774 ) -> Self {
775 self.disable_bal_parallel_state_root = disable_bal_parallel_state_root;
776 self
777 }
778
779 pub const fn disable_bal_batch_io(&self) -> bool {
781 self.disable_bal_batch_io
782 }
783
784 pub const fn without_bal_batch_io(mut self, disable_bal_batch_io: bool) -> Self {
786 self.disable_bal_batch_io = disable_bal_batch_io;
787 self
788 }
789
790 pub const fn skip_state_root(&self) -> bool {
792 self.skip_state_root
793 }
794
795 pub const fn with_skip_state_root(mut self, skip_state_root: bool) -> Self {
797 self.skip_state_root = skip_state_root;
798 self
799 }
800
801 #[cfg(feature = "trie-debug")]
803 pub const fn proof_jitter(&self) -> Option<Duration> {
804 self.proof_jitter
805 }
806
807 #[cfg(feature = "trie-debug")]
809 pub const fn with_proof_jitter(mut self, proof_jitter: Option<Duration>) -> Self {
810 self.proof_jitter = proof_jitter;
811 self
812 }
813}
814
815#[cfg(test)]
816mod tests {
817 use super::{TreeConfig, DEFAULT_NUM_STATE_MASKING_BLOCKS};
818
819 #[test]
820 fn txpool_prewarming_is_disabled_by_default_and_can_be_enabled() {
821 assert!(!TreeConfig::default().txpool_prewarming());
822 assert!(TreeConfig::default().with_txpool_prewarming(true).txpool_prewarming());
823 }
824
825 #[test]
826 fn state_root_task_requires_parallelism_without_overrides() {
827 assert!(TreeConfig::default().with_has_enough_parallelism(true).use_state_root_task());
828 assert!(!TreeConfig::default().with_has_enough_parallelism(false).use_state_root_task());
829 assert!(!TreeConfig::default()
830 .with_has_enough_parallelism(true)
831 .with_state_root_fallback(true)
832 .use_state_root_task());
833 assert!(!TreeConfig::default()
834 .with_has_enough_parallelism(true)
835 .with_skip_state_root(true)
836 .use_state_root_task());
837 }
838
839 #[test]
840 #[should_panic(
841 expected = "persistence_backpressure_threshold must be greater than persistence_threshold"
842 )]
843 fn rejects_backpressure_threshold_at_or_below_persistence_threshold() {
844 let _ = TreeConfig::default()
845 .with_persistence_threshold(4)
846 .with_persistence_backpressure_threshold(4);
847 }
848
849 #[test]
850 fn state_masking_is_disabled_by_default() {
851 assert_eq!(
852 TreeConfig::default().num_state_masking_blocks(),
853 DEFAULT_NUM_STATE_MASKING_BLOCKS
854 );
855 }
856
857 #[test]
858 #[should_panic(
859 expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
860 )]
861 fn rejects_state_masking_window_at_or_above_persistence_threshold() {
862 let _ = TreeConfig::default()
863 .with_persistence_threshold(4)
864 .with_memory_block_buffer_target(2)
865 .with_num_state_masking_blocks(2);
866 }
867
868 #[test]
869 #[should_panic(
870 expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
871 )]
872 fn rejects_overflowing_state_masking_window() {
873 let _ = TreeConfig::default().with_num_state_masking_blocks(u64::MAX);
874 }
875}