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 always_compare_trie_updates: bool,
153 disable_state_cache: bool,
155 disable_prewarming: bool,
157 txpool_prewarming: bool,
159 state_provider_metrics: bool,
161 cross_block_cache_size: usize,
163 has_enough_parallelism: bool,
173 multiproof_chunk_size: usize,
175 reserved_cpu_cores: usize,
177 precompile_cache_disabled: bool,
179 state_root_fallback: bool,
181 always_process_payload_attributes_on_canonical_head: bool,
195 allow_unwind_canonical_header: bool,
197 disable_cache_metrics: bool,
199 sparse_trie_prune_depth: usize,
201 slow_block_threshold: Option<Duration>,
205 disable_sparse_trie_cache_pruning: bool,
207 state_root_task_timeout: Option<Duration>,
212 share_execution_cache_with_payload_builder: bool,
214 share_sparse_trie_with_payload_builder: bool,
216 suppress_persistence_during_build: bool,
221 disable_bal_parallel_execution: bool,
224 disable_bal_parallel_state_root: bool,
227 disable_bal_batch_io: bool,
231 prewarm_reuse: bool,
233 skip_state_root: bool,
238 #[cfg(feature = "trie-debug")]
242 proof_jitter: Option<Duration>,
243}
244
245impl Default for TreeConfig {
246 fn default() -> Self {
247 assert_backpressure_threshold_invariant(
248 DEFAULT_PERSISTENCE_THRESHOLD,
249 DEFAULT_PERSISTENCE_THRESHOLD * 2,
250 );
251 assert_state_masking_invariant(
252 DEFAULT_PERSISTENCE_THRESHOLD,
253 DEFAULT_NUM_STATE_MASKING_BLOCKS,
254 DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
255 );
256 Self {
257 persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
258 num_state_masking_blocks: DEFAULT_NUM_STATE_MASKING_BLOCKS,
259 memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
260 persistence_backpressure_threshold: DEFAULT_PERSISTENCE_THRESHOLD * 2,
261 block_buffer_limit: DEFAULT_BLOCK_BUFFER_LIMIT,
262 backfill_run_threshold: DEFAULT_BACKFILL_RUN_THRESHOLD,
263 max_invalid_header_cache_length: DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH,
264 invalid_header_hit_eviction_threshold: DEFAULT_INVALID_HEADER_HIT_EVICTION_THRESHOLD,
265 max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE,
266 always_compare_trie_updates: false,
267 disable_state_cache: false,
268 disable_prewarming: false,
269 txpool_prewarming: false,
270 state_provider_metrics: false,
271 cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE,
272 has_enough_parallelism: has_enough_parallelism(),
273 multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
274 reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
275 precompile_cache_disabled: false,
276 state_root_fallback: false,
277 always_process_payload_attributes_on_canonical_head: false,
278 allow_unwind_canonical_header: false,
279 disable_cache_metrics: false,
280 sparse_trie_prune_depth: DEFAULT_SPARSE_TRIE_PRUNE_DEPTH,
281 slow_block_threshold: None,
282 disable_sparse_trie_cache_pruning: false,
283 state_root_task_timeout: Some(DEFAULT_STATE_ROOT_TASK_TIMEOUT),
284 share_execution_cache_with_payload_builder: false,
285 share_sparse_trie_with_payload_builder: false,
286 suppress_persistence_during_build: false,
287 disable_bal_parallel_execution: false,
288 disable_bal_parallel_state_root: false,
289 disable_bal_batch_io: false,
290 prewarm_reuse: false,
291 skip_state_root: false,
292 #[cfg(feature = "trie-debug")]
293 proof_jitter: None,
294 }
295 }
296}
297
298impl TreeConfig {
299 #[expect(clippy::too_many_arguments)]
307 pub const fn new(
308 persistence_threshold: u64,
309 num_state_masking_blocks: u64,
310 memory_block_buffer_target: u64,
311 persistence_backpressure_threshold: u64,
312 block_buffer_limit: u32,
313 backfill_run_threshold: u64,
314 max_invalid_header_cache_length: u32,
315 invalid_header_hit_eviction_threshold: u8,
316 max_execute_block_batch_size: usize,
317 always_compare_trie_updates: bool,
318 disable_state_cache: bool,
319 disable_prewarming: bool,
320 state_provider_metrics: bool,
321 cross_block_cache_size: usize,
322 has_enough_parallelism: bool,
323 multiproof_chunk_size: usize,
324 reserved_cpu_cores: usize,
325 precompile_cache_disabled: bool,
326 state_root_fallback: bool,
327 always_process_payload_attributes_on_canonical_head: bool,
328 allow_unwind_canonical_header: bool,
329 disable_cache_metrics: bool,
330 sparse_trie_prune_depth: usize,
331 slow_block_threshold: Option<Duration>,
332 state_root_task_timeout: Option<Duration>,
333 share_execution_cache_with_payload_builder: bool,
334 share_sparse_trie_with_payload_builder: bool,
335 ) -> Self {
336 assert!(multiproof_chunk_size > 0, "multiproof chunk size must be non-zero");
337 let num_state_masking_blocks =
338 if persistence_threshold == 0 { 0 } else { num_state_masking_blocks };
339 assert_backpressure_threshold_invariant(
340 persistence_threshold,
341 persistence_backpressure_threshold,
342 );
343 assert_state_masking_invariant(
344 persistence_threshold,
345 num_state_masking_blocks,
346 memory_block_buffer_target,
347 );
348 Self {
349 persistence_threshold,
350 num_state_masking_blocks,
351 memory_block_buffer_target,
352 persistence_backpressure_threshold,
353 block_buffer_limit,
354 backfill_run_threshold: clamp_backfill_run_threshold(backfill_run_threshold),
355 max_invalid_header_cache_length,
356 invalid_header_hit_eviction_threshold,
357 max_execute_block_batch_size,
358 always_compare_trie_updates,
359 disable_state_cache,
360 disable_prewarming,
361 txpool_prewarming: false,
362 state_provider_metrics,
363 cross_block_cache_size,
364 has_enough_parallelism,
365 multiproof_chunk_size,
366 reserved_cpu_cores,
367 precompile_cache_disabled,
368 state_root_fallback,
369 always_process_payload_attributes_on_canonical_head,
370 allow_unwind_canonical_header,
371 disable_cache_metrics,
372 sparse_trie_prune_depth,
373 slow_block_threshold,
374 disable_sparse_trie_cache_pruning: false,
375 state_root_task_timeout,
376 share_execution_cache_with_payload_builder,
377 share_sparse_trie_with_payload_builder,
378 suppress_persistence_during_build: false,
379 disable_bal_parallel_execution: false,
380 disable_bal_parallel_state_root: false,
381 disable_bal_batch_io: false,
382 prewarm_reuse: false,
383 skip_state_root: false,
384 #[cfg(feature = "trie-debug")]
385 proof_jitter: None,
386 }
387 }
388
389 pub const fn persistence_threshold(&self) -> u64 {
391 self.persistence_threshold
392 }
393
394 pub const fn num_state_masking_blocks(&self) -> u64 {
396 self.num_state_masking_blocks
397 }
398
399 pub const fn memory_block_buffer_target(&self) -> u64 {
401 self.memory_block_buffer_target
402 }
403
404 pub const fn persistence_backpressure_threshold(&self) -> u64 {
406 self.persistence_backpressure_threshold
407 }
408
409 pub const fn block_buffer_limit(&self) -> u32 {
411 self.block_buffer_limit
412 }
413
414 pub const fn backfill_run_threshold(&self) -> u64 {
416 self.backfill_run_threshold
417 }
418
419 pub const fn max_invalid_header_cache_length(&self) -> u32 {
421 self.max_invalid_header_cache_length
422 }
423
424 pub const fn invalid_header_hit_eviction_threshold(&self) -> u8 {
429 self.invalid_header_hit_eviction_threshold
430 }
431
432 pub const fn max_execute_block_batch_size(&self) -> usize {
434 self.max_execute_block_batch_size
435 }
436
437 pub const fn multiproof_chunk_size(&self) -> usize {
439 self.multiproof_chunk_size
440 }
441
442 pub const fn effective_multiproof_chunk_size(&self) -> usize {
444 self.multiproof_chunk_size
445 }
446
447 pub const fn reserved_cpu_cores(&self) -> usize {
449 self.reserved_cpu_cores
450 }
451
452 pub const fn state_provider_metrics(&self) -> bool {
454 self.state_provider_metrics
455 }
456
457 pub const fn disable_state_cache(&self) -> bool {
459 self.disable_state_cache
460 }
461
462 pub const fn disable_prewarming(&self) -> bool {
464 self.disable_prewarming
465 }
466
467 pub const fn txpool_prewarming(&self) -> bool {
469 self.txpool_prewarming
470 }
471
472 pub const fn always_compare_trie_updates(&self) -> bool {
475 self.always_compare_trie_updates
476 }
477
478 pub const fn cross_block_cache_size(&self) -> usize {
480 self.cross_block_cache_size
481 }
482
483 pub const fn precompile_cache_disabled(&self) -> bool {
485 self.precompile_cache_disabled
486 }
487
488 pub const fn state_root_fallback(&self) -> bool {
490 self.state_root_fallback
491 }
492
493 pub const fn with_always_process_payload_attributes_on_canonical_head(
495 mut self,
496 always_process_payload_attributes_on_canonical_head: bool,
497 ) -> Self {
498 self.always_process_payload_attributes_on_canonical_head =
499 always_process_payload_attributes_on_canonical_head;
500 self
501 }
502
503 pub const fn always_process_payload_attributes_on_canonical_head(&self) -> bool {
506 self.always_process_payload_attributes_on_canonical_head
507 }
508
509 pub const fn unwind_canonical_header(&self) -> bool {
511 self.allow_unwind_canonical_header
512 }
513
514 pub const fn with_persistence_threshold(mut self, persistence_threshold: u64) -> Self {
516 self.persistence_threshold = persistence_threshold;
517 if persistence_threshold == 0 {
518 self.num_state_masking_blocks = 0;
519 }
520 assert_backpressure_threshold_invariant(
521 self.persistence_threshold,
522 self.persistence_backpressure_threshold,
523 );
524 assert_state_masking_invariant(
525 self.persistence_threshold,
526 self.num_state_masking_blocks,
527 self.memory_block_buffer_target,
528 );
529 self
530 }
531
532 pub const fn with_num_state_masking_blocks(mut self, num_state_masking_blocks: u64) -> Self {
535 self.num_state_masking_blocks =
536 if self.persistence_threshold == 0 { 0 } else { num_state_masking_blocks };
537 assert_state_masking_invariant(
538 self.persistence_threshold,
539 self.num_state_masking_blocks,
540 self.memory_block_buffer_target,
541 );
542 self
543 }
544
545 pub const fn with_memory_block_buffer_target(
547 mut self,
548 memory_block_buffer_target: u64,
549 ) -> Self {
550 self.memory_block_buffer_target = memory_block_buffer_target;
551 assert_state_masking_invariant(
552 self.persistence_threshold,
553 self.num_state_masking_blocks,
554 self.memory_block_buffer_target,
555 );
556 self
557 }
558
559 pub const fn with_persistence_backpressure_threshold(
561 mut self,
562 persistence_backpressure_threshold: u64,
563 ) -> Self {
564 self.persistence_backpressure_threshold = persistence_backpressure_threshold;
565 assert_backpressure_threshold_invariant(
566 self.persistence_threshold,
567 self.persistence_backpressure_threshold,
568 );
569 self
570 }
571
572 pub const fn with_block_buffer_limit(mut self, block_buffer_limit: u32) -> Self {
574 self.block_buffer_limit = block_buffer_limit;
575 self
576 }
577
578 pub const fn with_backfill_run_threshold(mut self, backfill_run_threshold: u64) -> Self {
584 self.backfill_run_threshold = clamp_backfill_run_threshold(backfill_run_threshold);
585 let buffer_limit = self.backfill_run_threshold as u32 * 2;
586 if buffer_limit > self.block_buffer_limit {
587 self.block_buffer_limit = buffer_limit;
588 }
589 self
590 }
591
592 pub const fn with_max_invalid_header_cache_length(
594 mut self,
595 max_invalid_header_cache_length: u32,
596 ) -> Self {
597 self.max_invalid_header_cache_length = max_invalid_header_cache_length;
598 self
599 }
600
601 pub const fn with_invalid_header_hit_eviction_threshold(
603 mut self,
604 invalid_header_hit_eviction_threshold: u8,
605 ) -> Self {
606 self.invalid_header_hit_eviction_threshold = invalid_header_hit_eviction_threshold;
607 self
608 }
609
610 pub const fn with_max_execute_block_batch_size(
612 mut self,
613 max_execute_block_batch_size: usize,
614 ) -> Self {
615 self.max_execute_block_batch_size = max_execute_block_batch_size;
616 self
617 }
618
619 pub const fn without_state_cache(mut self, disable_state_cache: bool) -> Self {
621 self.disable_state_cache = disable_state_cache;
622 self
623 }
624
625 pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self {
627 self.disable_prewarming = disable_prewarming;
628 self
629 }
630
631 pub const fn with_txpool_prewarming(mut self, enabled: bool) -> Self {
633 self.txpool_prewarming = enabled;
634 self
635 }
636
637 pub const fn with_always_compare_trie_updates(
640 mut self,
641 always_compare_trie_updates: bool,
642 ) -> Self {
643 self.always_compare_trie_updates = always_compare_trie_updates;
644 self
645 }
646
647 pub const fn with_cross_block_cache_size(mut self, cross_block_cache_size: usize) -> Self {
649 self.cross_block_cache_size = cross_block_cache_size;
650 self
651 }
652
653 pub const fn with_has_enough_parallelism(mut self, has_enough_parallelism: bool) -> Self {
655 self.has_enough_parallelism = has_enough_parallelism;
656 self
657 }
658
659 pub const fn has_enough_parallelism(&self) -> bool {
661 self.has_enough_parallelism
662 }
663
664 pub const fn use_state_root_task(&self) -> bool {
669 !self.skip_state_root && !self.state_root_fallback && self.has_enough_parallelism
670 }
671
672 pub const fn with_state_provider_metrics(mut self, state_provider_metrics: bool) -> Self {
674 self.state_provider_metrics = state_provider_metrics;
675 self
676 }
677
678 pub const fn with_multiproof_chunk_size(mut self, multiproof_chunk_size: usize) -> Self {
684 assert!(multiproof_chunk_size > 0, "multiproof chunk size must be non-zero");
685 self.multiproof_chunk_size = multiproof_chunk_size;
686 self
687 }
688
689 pub const fn with_reserved_cpu_cores(mut self, reserved_cpu_cores: usize) -> Self {
691 self.reserved_cpu_cores = reserved_cpu_cores;
692 self
693 }
694
695 pub const fn without_precompile_cache(mut self, precompile_cache_disabled: bool) -> Self {
697 self.precompile_cache_disabled = precompile_cache_disabled;
698 self
699 }
700
701 pub const fn with_state_root_fallback(mut self, state_root_fallback: bool) -> Self {
703 self.state_root_fallback = state_root_fallback;
704 self
705 }
706
707 pub const fn with_unwind_canonical_header(mut self, unwind_canonical_header: bool) -> Self {
709 self.allow_unwind_canonical_header = unwind_canonical_header;
710 self
711 }
712
713 pub const fn disable_cache_metrics(&self) -> bool {
715 self.disable_cache_metrics
716 }
717
718 pub const fn without_cache_metrics(mut self, disable_cache_metrics: bool) -> Self {
720 self.disable_cache_metrics = disable_cache_metrics;
721 self
722 }
723
724 pub const fn sparse_trie_prune_depth(&self) -> usize {
726 self.sparse_trie_prune_depth
727 }
728
729 pub const fn with_sparse_trie_prune_depth(mut self, depth: usize) -> Self {
731 self.sparse_trie_prune_depth = depth;
732 self
733 }
734
735 pub const fn slow_block_threshold(&self) -> Option<Duration> {
741 self.slow_block_threshold
742 }
743
744 pub const fn with_slow_block_threshold(
746 mut self,
747 slow_block_threshold: Option<Duration>,
748 ) -> Self {
749 self.slow_block_threshold = slow_block_threshold;
750 self
751 }
752
753 pub const fn disable_sparse_trie_cache_pruning(&self) -> bool {
755 self.disable_sparse_trie_cache_pruning
756 }
757
758 pub const fn with_disable_sparse_trie_cache_pruning(mut self, value: bool) -> Self {
760 self.disable_sparse_trie_cache_pruning = value;
761 self
762 }
763
764 pub const fn state_root_task_timeout(&self) -> Option<Duration> {
766 self.state_root_task_timeout
767 }
768
769 pub const fn with_state_root_task_timeout(mut self, timeout: Option<Duration>) -> Self {
771 self.state_root_task_timeout = timeout;
772 self
773 }
774
775 pub const fn share_execution_cache_with_payload_builder(&self) -> bool {
777 self.share_execution_cache_with_payload_builder
778 }
779
780 pub const fn share_sparse_trie_with_payload_builder(&self) -> bool {
782 self.share_sparse_trie_with_payload_builder
783 }
784
785 pub const fn with_share_execution_cache_with_payload_builder(
787 mut self,
788 share_execution_cache_with_payload_builder: bool,
789 ) -> Self {
790 self.share_execution_cache_with_payload_builder =
791 share_execution_cache_with_payload_builder;
792 self
793 }
794
795 pub const fn with_share_sparse_trie_with_payload_builder(
797 mut self,
798 share_sparse_trie_with_payload_builder: bool,
799 ) -> Self {
800 self.share_sparse_trie_with_payload_builder = share_sparse_trie_with_payload_builder;
801 self
802 }
803
804 pub const fn suppress_persistence_during_build(&self) -> bool {
806 self.suppress_persistence_during_build
807 }
808
809 pub const fn with_suppress_persistence_during_build(mut self, value: bool) -> Self {
811 self.suppress_persistence_during_build = value;
812 self
813 }
814
815 pub const fn disable_bal_parallel_execution(&self) -> bool {
817 self.disable_bal_parallel_execution
818 }
819
820 pub const fn without_bal_parallel_execution(
822 mut self,
823 disable_bal_parallel_execution: bool,
824 ) -> Self {
825 self.disable_bal_parallel_execution = disable_bal_parallel_execution;
826 self
827 }
828
829 pub const fn disable_bal_parallel_state_root(&self) -> bool {
831 self.disable_bal_parallel_state_root
832 }
833
834 pub const fn without_bal_parallel_state_root(
836 mut self,
837 disable_bal_parallel_state_root: bool,
838 ) -> Self {
839 self.disable_bal_parallel_state_root = disable_bal_parallel_state_root;
840 self
841 }
842
843 pub const fn disable_bal_batch_io(&self) -> bool {
845 self.disable_bal_batch_io
846 }
847
848 pub const fn without_bal_batch_io(mut self, disable_bal_batch_io: bool) -> Self {
850 self.disable_bal_batch_io = disable_bal_batch_io;
851 self
852 }
853
854 pub const fn prewarm_reuse(&self) -> bool {
856 self.prewarm_reuse
857 }
858
859 pub const fn with_prewarm_reuse(mut self, prewarm_reuse: bool) -> Self {
861 self.prewarm_reuse = prewarm_reuse;
862 self
863 }
864
865 pub const fn skip_state_root(&self) -> bool {
867 self.skip_state_root
868 }
869
870 pub const fn with_skip_state_root(mut self, skip_state_root: bool) -> Self {
872 self.skip_state_root = skip_state_root;
873 self
874 }
875
876 #[cfg(feature = "trie-debug")]
878 pub const fn proof_jitter(&self) -> Option<Duration> {
879 self.proof_jitter
880 }
881
882 #[cfg(feature = "trie-debug")]
884 pub const fn with_proof_jitter(mut self, proof_jitter: Option<Duration>) -> Self {
885 self.proof_jitter = proof_jitter;
886 self
887 }
888}
889
890const fn clamp_backfill_run_threshold(threshold: u64) -> u64 {
892 if threshold > MAX_BACKFILL_RUN_THRESHOLD {
893 MAX_BACKFILL_RUN_THRESHOLD
894 } else {
895 threshold
896 }
897}
898
899#[cfg(test)]
900mod tests {
901 use super::*;
902
903 #[test]
904 fn backfill_threshold_grows_block_buffer() {
905 let default = TreeConfig::default();
906 for (threshold, expected, buffer_limit) in
907 [(0, 0, 64), (500, 500, 1000), (1024, 1024, 2048), (u64::MAX, 1024, 2048)]
908 {
909 let config = default.clone().with_backfill_run_threshold(threshold);
910 assert_eq!(config.backfill_run_threshold(), expected);
911 assert_eq!(config.block_buffer_limit(), buffer_limit);
912 }
913 let config = default.with_block_buffer_limit(2000).with_backfill_run_threshold(500);
914 assert_eq!(config.block_buffer_limit(), 2000);
915 }
916
917 #[test]
918 #[should_panic(
919 expected = "persistence_backpressure_threshold must be greater than persistence_threshold"
920 )]
921 fn rejects_backpressure_threshold_at_or_below_persistence_threshold() {
922 let _ = TreeConfig::default()
923 .with_num_state_masking_blocks(0)
924 .with_persistence_threshold(4)
925 .with_persistence_backpressure_threshold(4);
926 }
927
928 #[test]
929 fn zero_persistence_threshold_disables_state_masking() {
930 let config = TreeConfig::default().with_persistence_threshold(0);
931 assert_eq!(config.num_state_masking_blocks(), 0);
932 let config = config.with_num_state_masking_blocks(u64::MAX);
933 assert_eq!(config.num_state_masking_blocks(), 0);
934 let config = config.with_memory_block_buffer_target(0);
935 assert_eq!(config.num_state_masking_blocks(), 0);
936 }
937
938 #[test]
939 #[should_panic(
940 expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
941 )]
942 fn rejects_state_masking_window_at_or_above_persistence_threshold() {
943 let _ = TreeConfig::default()
944 .with_num_state_masking_blocks(0)
945 .with_persistence_threshold(4)
946 .with_memory_block_buffer_target(2)
947 .with_num_state_masking_blocks(2);
948 }
949
950 #[test]
951 #[should_panic(
952 expected = "num_state_masking_blocks + memory_block_buffer_target must be less than persistence_threshold"
953 )]
954 fn rejects_overflowing_state_masking_window() {
955 let _ = TreeConfig::default().with_num_state_masking_blocks(u64::MAX);
956 }
957}