1use alloy_eips::merge::EPOCH_SLOTS;
4use core::time::Duration;
5
6pub const DEFAULT_PERSISTENCE_THRESHOLD: u64 = 50;
8
9pub const DEFAULT_NUM_STATE_MASKING_BLOCKS: u64 = 30;
11
12pub const MIN_PERSISTENCE_BACKPRESSURE_THRESHOLD: u64 = 16;
15
16pub const DEFAULT_MEMORY_BLOCK_BUFFER_TARGET: u64 = 5;
18
19pub const DEFAULT_BACKFILL_RUN_THRESHOLD: u64 = EPOCH_SLOTS;
29
30pub const MAX_BACKFILL_RUN_THRESHOLD: u64 = 1024;
34
35pub const DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE: usize = 5;
37
38pub const DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD: u8 = 128;
40
41pub const SMALL_BLOCK_GAS_THRESHOLD: u64 = 20_000_000;
43
44pub const DEFAULT_RESERVED_CPU_CORES: usize = 1;
48
49pub const DEFAULT_SPARSE_TRIE_PRUNE_DEPTH: usize = 4;
54
55pub const DEFAULT_STATE_ROOT_TASK_TIMEOUT: Duration = Duration::from_secs(1);
57
58const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = EPOCH_SLOTS as u32 * 2;
59const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
60const DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE: usize = 4;
61const DEFAULT_CROSS_BLOCK_CACHE_SIZE: usize = default_cross_block_cache_size();
62
63const fn assert_backpressure_threshold_invariant(
64 persistence_threshold: u64,
65 persistence_backpressure_threshold: u64,
66) {
67 debug_assert!(
68 persistence_backpressure_threshold > persistence_threshold,
69 "persistence_backpressure_threshold must be greater than persistence_threshold",
70 );
71}
72
73const fn assert_state_masking_invariant(
74 persistence_threshold: u64,
75 num_state_masking_blocks: u64,
76 memory_block_buffer_target: u64,
77) {
78 let valid_window = match num_state_masking_blocks.checked_add(memory_block_buffer_target) {
79 Some(window) => window < persistence_threshold,
80 None => false,
81 };
82 debug_assert!(
83 num_state_masking_blocks == 0 || valid_window,
84 "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold",
85 );
86}
87
88const fn default_cross_block_cache_size() -> usize {
89 if cfg!(test) {
90 1024 * 1024 } else if cfg!(target_pointer_width = "32") {
92 usize::MAX } else {
94 4 * 1024 * 1024 * 1024 }
96}
97
98pub fn has_enough_parallelism() -> bool {
107 #[cfg(feature = "std")]
108 {
109 std::thread::available_parallelism().is_ok_and(|num| num.get() >= 5)
110 }
111 #[cfg(not(feature = "std"))]
112 false
113}
114
115#[derive(Debug, Clone)]
117pub struct TreeConfig {
118 persistence_threshold: u64,
121 num_state_masking_blocks: u64,
124 memory_block_buffer_target: u64,
129 persistence_backpressure_threshold: u64,
132 block_buffer_limit: u32,
135 backfill_run_threshold: u64,
138 max_invalid_header_cache_length: u32,
140 invalid_header_hit_eviction_threshold: u8,
145 max_execute_block_batch_size: usize,
150 disable_state_cache: bool,
152 disable_prewarming: bool,
154 txpool_prewarming: bool,
156 state_provider_metrics: bool,
158 cross_block_cache_size: usize,
160 has_enough_parallelism: bool,
170 multiproof_chunk_size: usize,
172 reserved_cpu_cores: usize,
174 precompile_cache_disabled: bool,
176 state_root_fallback: bool,
178 always_process_payload_attributes_on_canonical_head: bool,
192 allow_unwind_canonical_header: bool,
194 disable_cache_metrics: bool,
196 sparse_trie_prune_depth: usize,
198 slow_block_threshold: Option<Duration>,
202 disable_sparse_trie_cache_pruning: bool,
204 state_root_task_timeout: Option<Duration>,
209 share_execution_cache_with_payload_builder: bool,
211 share_sparse_trie_with_payload_builder: bool,
213 suppress_persistence_during_build: bool,
218 disable_bal_parallel_execution: bool,
221 disable_bal_parallel_state_root: bool,
224 disable_bal_batch_io: bool,
228 prewarm_reuse: bool,
230 skip_state_root: bool,
235 #[cfg(feature = "trie-debug")]
239 proof_jitter: Option<Duration>,
240}
241
242impl Default for TreeConfig {
243 fn default() -> Self {
244 assert_backpressure_threshold_invariant(
245 DEFAULT_PERSISTENCE_THRESHOLD,
246 DEFAULT_PERSISTENCE_THRESHOLD * 2,
247 );
248 assert_state_masking_invariant(
249 DEFAULT_PERSISTENCE_THRESHOLD,
250 DEFAULT_NUM_STATE_MASKING_BLOCKS,
251 DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
252 );
253 Self {
254 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
255 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
256 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
257 persistence_backpressure_threshold: DEFAULT_PERSISTENCE_THRESHOLD * 2,
258 block_buffer_limit: DEFAULT_BLOCK_BUFFER_LIMIT,
259 backfill_run_threshold: DEFAULT_BACKFILL_RUN_THRESHOLD,
260 max_invalid_header_cache_length: DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
261 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
262 max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE,
263 disable_state_cache: false,
264 disable_prewarming: false,
265 txpool_prewarming: false,
266 state_provider_metrics: false,
267 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE,
268 has_enough_parallelism: has_enough_parallelism(),
269 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
270 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
271 precompile_cache_disabled: false,
272 state_root_fallback: false,
273 always_process_payload_attributes_on_canonical_head: false,
274 allow_unwind_canonical_header: false,
275 disable_cache_metrics: false,
276 sparse_trie_prune_depth: DEFAULT_SPARSE_TRIE_PRUNE_DEPTH,
277 slow_block_threshold: None,
278 disable_sparse_trie_cache_pruning: false,
279 state_root_task_timeout: Some(DEFAULT_STATE_ROOT_TASK_TIMEOUT),
280 share_execution_cache_with_payload_builder: false,
281 share_sparse_trie_with_payload_builder: false,
282 suppress_persistence_during_build: false,
283 disable_bal_parallel_execution: false,
284 disable_bal_parallel_state_root: false,
285 disable_bal_batch_io: false,
286 prewarm_reuse: false,
287 skip_state_root: false,
288 #[cfg(feature = "trie-debug")]
289 proof_jitter: None,
290 }
291 }
292}
293
294impl TreeConfig {
295 #[expect(clippy::too_many_arguments)]
303 pub const fn new(
304 persistence_threshold: u64,
305 num_state_masking_blocks: u64,
306 memory_block_buffer_target: u64,
307 persistence_backpressure_threshold: u64,
308 block_buffer_limit: u32,
309 backfill_run_threshold: u64,
310 max_invalid_header_cache_length: u32,
311 invalid_header_hit_eviction_threshold: u8,
312 max_execute_block_batch_size: usize,
313 disable_state_cache: bool,
314 disable_prewarming: bool,
315 state_provider_metrics: bool,
316 cross_block_cache_size: usize,
317 has_enough_parallelism: bool,
318 multiproof_chunk_size: usize,
319 reserved_cpu_cores: usize,
320 precompile_cache_disabled: bool,
321 state_root_fallback: bool,
322 always_process_payload_attributes_on_canonical_head: bool,
323 allow_unwind_canonical_header: bool,
324 disable_cache_metrics: bool,
325 sparse_trie_prune_depth: usize,
326 slow_block_threshold: Option<Duration>,
327 state_root_task_timeout: Option<Duration>,
328 share_execution_cache_with_payload_builder: bool,
329 share_sparse_trie_with_payload_builder: bool,
330 ) -> Self {
331 assert!(multiproof_chunk_size > 0, "multiproof chunk size must be non-zero");
332 let num_state_masking_blocks =
333 if persistence_threshold == 0 { 0 } else { num_state_masking_blocks };
334 assert_backpressure_threshold_invariant(
335 persistence_threshold,
336 persistence_backpressure_threshold,
337 );
338 assert_state_masking_invariant(
339 persistence_threshold,
340 num_state_masking_blocks,
341 memory_block_buffer_target,
342 );
343 Self {
344 persistence_threshold,
345 num_state_masking_blocks,
346 memory_block_buffer_target,
347 persistence_backpressure_threshold,
348 block_buffer_limit,
349 backfill_run_threshold: clamp_backfill_run_threshold(backfill_run_threshold),
350 max_invalid_header_cache_length,
351 invalid_header_hit_eviction_threshold,
352 max_execute_block_batch_size,
353 disable_state_cache,
354 disable_prewarming,
355 txpool_prewarming: false,
356 state_provider_metrics,
357 cross_block_cache_size,
358 has_enough_parallelism,
359 multiproof_chunk_size,
360 reserved_cpu_cores,
361 precompile_cache_disabled,
362 state_root_fallback,
363 always_process_payload_attributes_on_canonical_head,
364 allow_unwind_canonical_header,
365 disable_cache_metrics,
366 sparse_trie_prune_depth,
367 slow_block_threshold,
368 disable_sparse_trie_cache_pruning: false,
369 state_root_task_timeout,
370 share_execution_cache_with_payload_builder,
371 share_sparse_trie_with_payload_builder,
372 suppress_persistence_during_build: false,
373 disable_bal_parallel_execution: false,
374 disable_bal_parallel_state_root: false,
375 disable_bal_batch_io: false,
376 prewarm_reuse: false,
377 skip_state_root: false,
378 #[cfg(feature = "trie-debug")]
379 proof_jitter: None,
380 }
381 }
382
383 pub const fn persistence_threshold(&self) -> u64 {
385 self.persistence_threshold
386 }
387
388 pub const fn num_state_masking_blocks(&self) -> u64 {
390 self.num_state_masking_blocks
391 }
392
393 pub const fn memory_block_buffer_target(&self) -> u64 {
395 self.memory_block_buffer_target
396 }
397
398 pub const fn persistence_backpressure_threshold(&self) -> u64 {
400 self.persistence_backpressure_threshold
401 }
402
403 pub const fn block_buffer_limit(&self) -> u32 {
405 self.block_buffer_limit
406 }
407
408 pub const fn backfill_run_threshold(&self) -> u64 {
410 self.backfill_run_threshold
411 }
412
413 pub const fn max_invalid_header_cache_length(&self) -> u32 {
415 self.max_invalid_header_cache_length
416 }
417
418 pub const fn invalid_header_hit_eviction_threshold(&self) -> u8 {
423 self.invalid_header_hit_eviction_threshold
424 }
425
426 pub const fn max_execute_block_batch_size(&self) -> usize {
428 self.max_execute_block_batch_size
429 }
430
431 pub const fn multiproof_chunk_size(&self) -> usize {
433 self.multiproof_chunk_size
434 }
435
436 pub const fn effective_multiproof_chunk_size(&self) -> usize {
438 self.multiproof_chunk_size
439 }
440
441 pub const fn reserved_cpu_cores(&self) -> usize {
443 self.reserved_cpu_cores
444 }
445
446 pub const fn state_provider_metrics(&self) -> bool {
448 self.state_provider_metrics
449 }
450
451 pub const fn disable_state_cache(&self) -> bool {
453 self.disable_state_cache
454 }
455
456 pub const fn disable_prewarming(&self) -> bool {
458 self.disable_prewarming
459 }
460
461 pub const fn txpool_prewarming(&self) -> bool {
463 self.txpool_prewarming
464 }
465
466 pub const fn cross_block_cache_size(&self) -> usize {
468 self.cross_block_cache_size
469 }
470
471 pub const fn precompile_cache_disabled(&self) -> bool {
473 self.precompile_cache_disabled
474 }
475
476 pub const fn state_root_fallback(&self) -> bool {
478 self.state_root_fallback
479 }
480
481 pub const fn with_always_process_payload_attributes_on_canonical_head(
483 mut self,
484 always_process_payload_attributes_on_canonical_head: bool,
485 ) -> Self {
486 self.always_process_payload_attributes_on_canonical_head =
487 always_process_payload_attributes_on_canonical_head;
488 self
489 }
490
491 pub const fn always_process_payload_attributes_on_canonical_head(&self) -> bool {
494 self.always_process_payload_attributes_on_canonical_head
495 }
496
497 pub const fn unwind_canonical_header(&self) -> bool {
499 self.allow_unwind_canonical_header
500 }
501
502 pub const fn with_persistence_threshold(mut self, persistence_threshold: u64) -> Self {
504 self.persistence_threshold = persistence_threshold;
505 if persistence_threshold == 0 {
506 self.num_state_masking_blocks = 0;
507 }
508 assert_backpressure_threshold_invariant(
509 self.persistence_threshold,
510 self.persistence_backpressure_threshold,
511 );
512 assert_state_masking_invariant(
513 self.persistence_threshold,
514 self.num_state_masking_blocks,
515 self.memory_block_buffer_target,
516 );
517 self
518 }
519
520 pub const fn with_num_state_masking_blocks(mut self, num_state_masking_blocks: u64) -> Self {
523 self.num_state_masking_blocks =
524 if self.persistence_threshold == 0 { 0 } else { num_state_masking_blocks };
525 assert_state_masking_invariant(
526 self.persistence_threshold,
527 self.num_state_masking_blocks,
528 self.memory_block_buffer_target,
529 );
530 self
531 }
532
533 pub const fn with_memory_block_buffer_target(
535 mut self,
536 memory_block_buffer_target: u64,
537 ) -> Self {
538 self.memory_block_buffer_target = memory_block_buffer_target;
539 assert_state_masking_invariant(
540 self.persistence_threshold,
541 self.num_state_masking_blocks,
542 self.memory_block_buffer_target,
543 );
544 self
545 }
546
547 pub const fn with_persistence_backpressure_threshold(
549 mut self,
550 persistence_backpressure_threshold: u64,
551 ) -> Self {
552 self.persistence_backpressure_threshold = persistence_backpressure_threshold;
553 assert_backpressure_threshold_invariant(
554 self.persistence_threshold,
555 self.persistence_backpressure_threshold,
556 );
557 self
558 }
559
560 pub const fn with_block_buffer_limit(mut self, block_buffer_limit: u32) -> Self {
562 self.block_buffer_limit = block_buffer_limit;
563 self
564 }
565
566 pub const fn with_backfill_run_threshold(mut self, backfill_run_threshold: u64) -> Self {
572 self.backfill_run_threshold = clamp_backfill_run_threshold(backfill_run_threshold);
573 let buffer_limit = self.backfill_run_threshold as u32 * 2;
574 if buffer_limit > self.block_buffer_limit {
575 self.block_buffer_limit = buffer_limit;
576 }
577 self
578 }
579
580 pub const fn with_max_invalid_header_cache_length(
582 mut self,
583 max_invalid_header_cache_length: u32,
584 ) -> Self {
585 self.max_invalid_header_cache_length = max_invalid_header_cache_length;
586 self
587 }
588
589 pub const fn with_invalid_header_hit_eviction_threshold(
591 mut self,
592 invalid_header_hit_eviction_threshold: u8,
593 ) -> Self {
594 self.invalid_header_hit_eviction_threshold = invalid_header_hit_eviction_threshold;
595 self
596 }
597
598 pub const fn with_max_execute_block_batch_size(
600 mut self,
601 max_execute_block_batch_size: usize,
602 ) -> Self {
603 self.max_execute_block_batch_size = max_execute_block_batch_size;
604 self
605 }
606
607 pub const fn without_state_cache(mut self, disable_state_cache: bool) -> Self {
609 self.disable_state_cache = disable_state_cache;
610 self
611 }
612
613 pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self {
615 self.disable_prewarming = disable_prewarming;
616 self
617 }
618
619 pub const fn with_txpool_prewarming(mut self, enabled: bool) -> Self {
621 self.txpool_prewarming = enabled;
622 self
623 }
624
625 pub const fn with_cross_block_cache_size(mut self, cross_block_cache_size: usize) -> Self {
627 self.cross_block_cache_size = cross_block_cache_size;
628 self
629 }
630
631 pub const fn with_has_enough_parallelism(mut self, has_enough_parallelism: bool) -> Self {
633 self.has_enough_parallelism = has_enough_parallelism;
634 self
635 }
636
637 pub const fn has_enough_parallelism(&self) -> bool {
639 self.has_enough_parallelism
640 }
641
642 pub const fn use_state_root_task(&self) -> bool {
647 !self.skip_state_root && !self.state_root_fallback && self.has_enough_parallelism
648 }
649
650 pub const fn with_state_provider_metrics(mut self, state_provider_metrics: bool) -> Self {
652 self.state_provider_metrics = state_provider_metrics;
653 self
654 }
655
656 pub const fn with_multiproof_chunk_size(mut self, multiproof_chunk_size: usize) -> Self {
662 assert!(multiproof_chunk_size > 0, "multiproof chunk size must be non-zero");
663 self.multiproof_chunk_size = multiproof_chunk_size;
664 self
665 }
666
667 pub const fn with_reserved_cpu_cores(mut self, reserved_cpu_cores: usize) -> Self {
669 self.reserved_cpu_cores = reserved_cpu_cores;
670 self
671 }
672
673 pub const fn without_precompile_cache(mut self, precompile_cache_disabled: bool) -> Self {
675 self.precompile_cache_disabled = precompile_cache_disabled;
676 self
677 }
678
679 pub const fn with_state_root_fallback(mut self, state_root_fallback: bool) -> Self {
681 self.state_root_fallback = state_root_fallback;
682 self
683 }
684
685 pub const fn with_unwind_canonical_header(mut self, unwind_canonical_header: bool) -> Self {
687 self.allow_unwind_canonical_header = unwind_canonical_header;
688 self
689 }
690
691 pub const fn disable_cache_metrics(&self) -> bool {
693 self.disable_cache_metrics
694 }
695
696 pub const fn without_cache_metrics(mut self, disable_cache_metrics: bool) -> Self {
698 self.disable_cache_metrics = disable_cache_metrics;
699 self
700 }
701
702 pub const fn sparse_trie_prune_depth(&self) -> usize {
704 self.sparse_trie_prune_depth
705 }
706
707 pub const fn with_sparse_trie_prune_depth(mut self, depth: usize) -> Self {
709 self.sparse_trie_prune_depth = depth;
710 self
711 }
712
713 pub const fn slow_block_threshold(&self) -> Option<Duration> {
719 self.slow_block_threshold
720 }
721
722 pub const fn with_slow_block_threshold(
724 mut self,
725 slow_block_threshold: Option<Duration>,
726 ) -> Self {
727 self.slow_block_threshold = slow_block_threshold;
728 self
729 }
730
731 pub const fn disable_sparse_trie_cache_pruning(&self) -> bool {
733 self.disable_sparse_trie_cache_pruning
734 }
735
736 pub const fn with_disable_sparse_trie_cache_pruning(mut self, value: bool) -> Self {
738 self.disable_sparse_trie_cache_pruning = value;
739 self
740 }
741
742 pub const fn state_root_task_timeout(&self) -> Option<Duration> {
744 self.state_root_task_timeout
745 }
746
747 pub const fn with_state_root_task_timeout(mut self, timeout: Option<Duration>) -> Self {
749 self.state_root_task_timeout = timeout;
750 self
751 }
752
753 pub const fn share_execution_cache_with_payload_builder(&self) -> bool {
755 self.share_execution_cache_with_payload_builder
756 }
757
758 pub const fn share_sparse_trie_with_payload_builder(&self) -> bool {
760 self.share_sparse_trie_with_payload_builder
761 }
762
763 pub const fn with_share_execution_cache_with_payload_builder(
765 mut self,
766 share_execution_cache_with_payload_builder: bool,
767 ) -> Self {
768 self.share_execution_cache_with_payload_builder =
769 share_execution_cache_with_payload_builder;
770 self
771 }
772
773 pub const fn with_share_sparse_trie_with_payload_builder(
775 mut self,
776 share_sparse_trie_with_payload_builder: bool,
777 ) -> Self {
778 self.share_sparse_trie_with_payload_builder = share_sparse_trie_with_payload_builder;
779 self
780 }
781
782 pub const fn suppress_persistence_during_build(&self) -> bool {
784 self.suppress_persistence_during_build
785 }
786
787 pub const fn with_suppress_persistence_during_build(mut self, value: bool) -> Self {
789 self.suppress_persistence_during_build = value;
790 self
791 }
792
793 pub const fn disable_bal_parallel_execution(&self) -> bool {
795 self.disable_bal_parallel_execution
796 }
797
798 pub const fn without_bal_parallel_execution(
800 mut self,
801 disable_bal_parallel_execution: bool,
802 ) -> Self {
803 self.disable_bal_parallel_execution = disable_bal_parallel_execution;
804 self
805 }
806
807 pub const fn disable_bal_parallel_state_root(&self) -> bool {
809 self.disable_bal_parallel_state_root
810 }
811
812 pub const fn without_bal_parallel_state_root(
814 mut self,
815 disable_bal_parallel_state_root: bool,
816 ) -> Self {
817 self.disable_bal_parallel_state_root = disable_bal_parallel_state_root;
818 self
819 }
820
821 pub const fn disable_bal_batch_io(&self) -> bool {
823 self.disable_bal_batch_io
824 }
825
826 pub const fn without_bal_batch_io(mut self, disable_bal_batch_io: bool) -> Self {
828 self.disable_bal_batch_io = disable_bal_batch_io;
829 self
830 }
831
832 pub const fn prewarm_reuse(&self) -> bool {
834 self.prewarm_reuse
835 }
836
837 pub const fn with_prewarm_reuse(mut self, prewarm_reuse: bool) -> Self {
839 self.prewarm_reuse = prewarm_reuse;
840 self
841 }
842
843 pub const fn skip_state_root(&self) -> bool {
845 self.skip_state_root
846 }
847
848 pub const fn with_skip_state_root(mut self, skip_state_root: bool) -> Self {
850 self.skip_state_root = skip_state_root;
851 self
852 }
853
854 #[cfg(feature = "trie-debug")]
856 pub const fn proof_jitter(&self) -> Option<Duration> {
857 self.proof_jitter
858 }
859
860 #[cfg(feature = "trie-debug")]
862 pub const fn with_proof_jitter(mut self, proof_jitter: Option<Duration>) -> Self {
863 self.proof_jitter = proof_jitter;
864 self
865 }
866}
867
868const fn clamp_backfill_run_threshold(threshold: u64) -> u64 {
870 if threshold > MAX_BACKFILL_RUN_THRESHOLD {
871 MAX_BACKFILL_RUN_THRESHOLD
872 } else {
873 threshold
874 }
875}
876
877#[cfg(test)]
878mod tests {
879 use super::*;
880
881 #[test]
882 fn backfill_threshold_grows_block_buffer() {
883 let default = TreeConfig::default();
884 for (threshold, expected, buffer_limit) in
885 [(0, 0, 64), (500, 500, 1000), (1024, 1024, 2048), (u64::MAX, 1024, 2048)]
886 {
887 let config = default.clone().with_backfill_run_threshold(threshold);
888 assert_eq!(config.backfill_run_threshold(), expected);
889 assert_eq!(config.block_buffer_limit(), buffer_limit);
890 }
891 let config = default.with_block_buffer_limit(2000).with_backfill_run_threshold(500);
892 assert_eq!(config.block_buffer_limit(), 2000);
893 }
894
895 #[test]
896 #[should_panic(
897 expected = "persistence_backpressure_threshold must be greater than persistence_threshold"
898 )]
899 fn rejects_backpressure_threshold_at_or_below_persistence_threshold() {
900 let _ = TreeConfig::default()
901 .with_num_state_masking_blocks(0)
902 .with_persistence_threshold(4)
903 .with_persistence_backpressure_threshold(4);
904 }
905
906 #[test]
907 fn zero_persistence_threshold_disables_state_masking() {
908 let config = TreeConfig::default().with_persistence_threshold(0);
909 assert_eq!(config.num_state_masking_blocks(), 0);
910 let config = config.with_num_state_masking_blocks(u64::MAX);
911 assert_eq!(config.num_state_masking_blocks(), 0);
912 let config = config.with_memory_block_buffer_target(0);
913 assert_eq!(config.num_state_masking_blocks(), 0);
914 }
915
916 #[test]
917 #[should_panic(
918 expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
919 )]
920 fn rejects_state_masking_window_at_or_above_persistence_threshold() {
921 let _ = TreeConfig::default()
922 .with_num_state_masking_blocks(0)
923 .with_persistence_threshold(4)
924 .with_memory_block_buffer_target(2)
925 .with_num_state_masking_blocks(2);
926 }
927
928 #[test]
929 #[should_panic(
930 expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
931 )]
932 fn rejects_overflowing_state_masking_window() {
933 let _ = TreeConfig::default().with_num_state_masking_blocks(u64::MAX);
934 }
935}