1use serde::{Deserialize, Serialize};
8
9pub use blvm_primitives::config::{BlockValidationConfig, NetworkMessageLimits};
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub struct MempoolConfig {
18 #[serde(default = "default_max_mempool_mb")]
21 pub max_mempool_mb: u64,
22
23 #[serde(default = "default_max_mempool_txs")]
26 pub max_mempool_txs: usize,
27
28 #[serde(default = "default_mempool_expiry_hours")]
32 pub mempool_expiry_hours: u64,
33
34 #[serde(default = "default_min_relay_fee_rate")]
38 pub min_relay_fee_rate: u64,
39
40 #[serde(default = "default_min_tx_fee")]
43 pub min_tx_fee: i64,
44
45 #[serde(default = "default_rbf_fee_increment")]
49 pub rbf_fee_increment: i64,
50
51 #[serde(default = "default_max_op_return_size")]
54 pub max_op_return_size: u32,
55
56 #[serde(default = "default_max_op_return_outputs")]
59 pub max_op_return_outputs: u32,
60
61 #[serde(default = "default_reject_multiple_op_return")]
64 pub reject_multiple_op_return: bool,
65
66 #[serde(default = "default_max_standard_script_size")]
69 pub max_standard_script_size: u32,
70
71 #[serde(default = "default_reject_envelope_protocol")]
74 pub reject_envelope_protocol: bool,
75
76 #[serde(default = "default_reject_spam_in_mempool")]
82 pub reject_spam_in_mempool: bool,
83
84 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub spam_filter_config: Option<serde_json::Value>,
88
89 #[serde(default = "default_min_fee_rate_large_tx")]
93 pub min_fee_rate_large_tx: u64,
94
95 #[serde(default = "default_large_tx_threshold_bytes")]
99 pub large_tx_threshold_bytes: u64,
100}
101
102fn default_rbf_fee_increment() -> i64 {
103 1000
104}
105
106fn default_max_mempool_mb() -> u64 {
107 300
108}
109
110fn default_max_mempool_txs() -> usize {
111 100_000
112}
113
114fn default_mempool_expiry_hours() -> u64 {
115 336 }
117
118fn default_min_relay_fee_rate() -> u64 {
119 1 }
121
122fn default_min_tx_fee() -> i64 {
123 1000
124}
125
126fn default_max_op_return_size() -> u32 {
127 80
128}
129
130fn default_max_op_return_outputs() -> u32 {
131 1
132}
133
134fn default_reject_multiple_op_return() -> bool {
135 true
136}
137
138fn default_max_standard_script_size() -> u32 {
139 200
140}
141
142fn default_reject_envelope_protocol() -> bool {
143 true
144}
145
146fn default_reject_spam_in_mempool() -> bool {
147 false
148}
149
150fn default_min_fee_rate_large_tx() -> u64 {
151 2 }
153
154fn default_large_tx_threshold_bytes() -> u64 {
155 1000 }
157
158impl Default for MempoolConfig {
159 fn default() -> Self {
160 Self {
161 max_mempool_mb: 300,
162 max_mempool_txs: 100_000,
163 mempool_expiry_hours: 336,
164 min_relay_fee_rate: 1,
165 min_tx_fee: 1000,
166 rbf_fee_increment: 1000,
167 max_op_return_size: 80,
168 max_op_return_outputs: 1,
169 reject_multiple_op_return: true,
170 max_standard_script_size: 200,
171 reject_envelope_protocol: true,
172 reject_spam_in_mempool: false,
173 spam_filter_config: None,
174 min_fee_rate_large_tx: 2,
175 large_tx_threshold_bytes: 1000,
176 }
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
184pub struct UtxoCommitmentConfig {
185 #[serde(default = "default_max_utxo_commitment_set_mb")]
189 pub max_utxo_commitment_set_mb: u64,
190
191 #[serde(default = "default_max_utxo_count")]
194 pub max_utxo_count: u64,
195
196 #[serde(default = "default_max_historical_commitments")]
200 pub max_historical_commitments: usize,
201
202 #[serde(default = "default_true")]
205 pub enable_incremental_updates: bool,
206}
207
208fn default_max_utxo_commitment_set_mb() -> u64 {
209 512
210}
211
212fn default_max_utxo_count() -> u64 {
213 100_000_000
214}
215
216fn default_max_historical_commitments() -> usize {
217 1000
218}
219
220impl Default for UtxoCommitmentConfig {
221 fn default() -> Self {
222 Self {
223 max_utxo_commitment_set_mb: 512,
224 max_utxo_count: 100_000_000,
225 max_historical_commitments: 1000,
226 enable_incremental_updates: true,
227 }
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
239pub struct PerformanceConfig {
240 #[serde(default)]
243 pub script_verification_threads: usize,
244
245 #[serde(default = "default_parallel_batch_size")]
249 pub parallel_batch_size: usize,
250
251 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub ibd_chunk_threshold: Option<usize>,
255
256 #[serde(default, skip_serializing_if = "Option::is_none")]
259 pub ibd_min_chunk_size: Option<usize>,
260
261 #[serde(default = "default_true")]
264 pub enable_simd_optimizations: bool,
265
266 #[serde(default = "default_true")]
269 pub enable_cache_optimizations: bool,
270
271 #[serde(default = "default_true")]
274 pub enable_batch_utxo_lookups: bool,
275}
276
277fn default_parallel_batch_size() -> usize {
278 8
279}
280
281impl Default for PerformanceConfig {
282 fn default() -> Self {
283 Self {
284 script_verification_threads: 0, parallel_batch_size: 8,
286 ibd_chunk_threshold: None,
287 ibd_min_chunk_size: None,
288 enable_simd_optimizations: true,
289 enable_cache_optimizations: true,
290 enable_batch_utxo_lookups: true,
291 }
292 }
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
300pub struct DebugConfig {
301 #[serde(default = "default_false")]
304 pub enable_runtime_assertions: bool,
305
306 #[serde(default = "default_false")]
309 pub enable_runtime_invariants: bool,
310
311 #[serde(default = "default_false")]
314 pub enable_verbose_logging: bool,
315
316 #[serde(default = "default_false")]
319 pub enable_performance_profiling: bool,
320
321 #[serde(default = "default_false")]
324 pub log_rejections: bool,
325}
326
327fn default_false() -> bool {
328 false
329}
330
331#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
336pub struct FeatureFlagsConfig {
337 #[serde(default = "default_false")]
340 pub enable_experimental_optimizations: bool,
341
342 #[serde(default = "default_true")]
345 pub enable_bounds_check_optimizations: bool,
346
347 #[serde(default = "default_false")]
350 pub enable_reference_checks: bool,
351
352 #[serde(default = "default_true")]
355 pub enable_aggressive_caching: bool,
356
357 #[serde(default = "default_true")]
360 pub enable_batch_tx_id_computation: bool,
361
362 #[serde(default = "default_true")]
365 pub enable_simd_hash_operations: bool,
366}
367
368impl Default for FeatureFlagsConfig {
369 fn default() -> Self {
370 Self {
371 enable_experimental_optimizations: false,
372 enable_bounds_check_optimizations: true,
373 enable_reference_checks: false,
374 enable_aggressive_caching: true,
375 enable_batch_tx_id_computation: true,
376 enable_simd_hash_operations: true,
377 }
378 }
379}
380
381#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
386pub struct AdvancedConfig {
387 #[serde(default)]
392 pub custom_checkpoints: Vec<u64>,
393
394 #[serde(default = "default_max_reorg_depth")]
398 pub max_reorg_depth: u64,
399
400 #[serde(default = "default_false")]
403 pub strict_mode: bool,
404
405 #[serde(default)]
409 pub max_block_size_override: usize,
410
411 #[serde(default = "default_true")]
414 pub enable_rbf: bool,
415}
416
417fn default_max_reorg_depth() -> u64 {
418 100
419}
420
421impl Default for AdvancedConfig {
422 fn default() -> Self {
423 Self {
424 custom_checkpoints: Vec::new(),
425 max_reorg_depth: 100,
426 strict_mode: false,
427 max_block_size_override: 0,
428 enable_rbf: true,
429 }
430 }
431}
432
433fn default_true() -> bool {
434 true
435}
436
437#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
439pub struct ConsensusConfig {
440 #[serde(default)]
442 pub network_limits: NetworkMessageLimits,
443
444 #[serde(default)]
446 pub block_validation: BlockValidationConfig,
447
448 #[serde(default)]
450 pub mempool: MempoolConfig,
451
452 #[serde(default)]
454 pub utxo_commitment: UtxoCommitmentConfig,
455
456 #[serde(default)]
458 pub performance: PerformanceConfig,
459
460 #[serde(default)]
462 pub debug: DebugConfig,
463
464 #[serde(default)]
466 pub features: FeatureFlagsConfig,
467
468 #[serde(default)]
470 pub advanced: AdvancedConfig,
471}
472
473impl ConsensusConfig {
474 pub fn from_env() -> Self {
478 let mut config = Self::default();
479
480 if let Ok(val) = std::env::var("BLVM_ASSUME_VALID_HEIGHT") {
482 if let Ok(height) = val.parse::<u64>() {
483 config.block_validation.assume_valid_height = height;
484 }
485 }
486
487 if let Ok(val) = std::env::var("BLVM_MTP_HEADERS") {
488 if let Ok(count) = val.parse::<usize>() {
489 config.block_validation.median_time_past_headers = count;
490 }
491 }
492 if let Ok(val) = std::env::var("BLVM_PARALLEL_VALIDATION") {
493 if let Ok(enabled) = val.parse::<bool>() {
494 config.block_validation.enable_parallel_validation = enabled;
495 }
496 }
497 if let Ok(val) = std::env::var("BLVM_COINBASE_MATURITY") {
498 if let Ok(maturity) = val.parse::<u64>() {
499 config.block_validation.coinbase_maturity_override = maturity;
500 }
501 }
502 if let Ok(val) = std::env::var("BLVM_MAX_SIGOPS_COST") {
503 if let Ok(cost) = val.parse::<u64>() {
504 config.block_validation.max_block_sigops_cost_override = cost;
505 }
506 }
507
508 if let Ok(val) = std::env::var("BLVM_MAX_ADDR_ADDRESSES") {
509 if let Ok(limit) = val.parse::<usize>() {
510 config.network_limits.max_addr_addresses = limit;
511 }
512 }
513
514 if let Ok(val) = std::env::var("BLVM_MAX_INV_ITEMS") {
515 if let Ok(limit) = val.parse::<usize>() {
516 config.network_limits.max_inv_items = limit;
517 }
518 }
519
520 if let Ok(val) = std::env::var("BLVM_MAX_HEADERS") {
521 if let Ok(limit) = val.parse::<usize>() {
522 config.network_limits.max_headers = limit;
523 }
524 }
525
526 if let Ok(val) = std::env::var("BLVM_MAX_USER_AGENT_LENGTH") {
527 if let Ok(limit) = val.parse::<usize>() {
528 config.network_limits.max_user_agent_length = limit;
529 }
530 }
531
532 if let Ok(val) = std::env::var("BLVM_MEMPOOL_MB") {
534 if let Ok(mb) = val.parse::<u64>() {
535 config.mempool.max_mempool_mb = mb;
536 }
537 }
538 if let Ok(val) = std::env::var("BLVM_MEMPOOL_TXS") {
539 if let Ok(count) = val.parse::<usize>() {
540 config.mempool.max_mempool_txs = count;
541 }
542 }
543 if let Ok(val) = std::env::var("BLVM_MEMPOOL_EXPIRY_HOURS") {
544 if let Ok(hours) = val.parse::<u64>() {
545 config.mempool.mempool_expiry_hours = hours;
546 }
547 }
548 if let Ok(val) = std::env::var("BLVM_MEMPOOL_MIN_RELAY_FEE") {
549 if let Ok(rate) = val.parse::<u64>() {
550 config.mempool.min_relay_fee_rate = rate;
551 }
552 }
553 if let Ok(val) = std::env::var("BLVM_MEMPOOL_MIN_TX_FEE") {
554 if let Ok(fee) = val.parse::<i64>() {
555 config.mempool.min_tx_fee = fee;
556 }
557 }
558 if let Ok(val) = std::env::var("BLVM_MEMPOOL_RBF_FEE_INCREMENT") {
559 if let Ok(increment) = val.parse::<i64>() {
560 config.mempool.rbf_fee_increment = increment;
561 }
562 }
563
564 if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_SET_MB") {
566 if let Ok(mb) = val.parse::<u64>() {
567 config.utxo_commitment.max_utxo_commitment_set_mb = mb;
568 }
569 }
570 if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_UTXO_COUNT") {
571 if let Ok(count) = val.parse::<u64>() {
572 config.utxo_commitment.max_utxo_count = count;
573 }
574 }
575 if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_HISTORICAL") {
576 if let Ok(count) = val.parse::<usize>() {
577 config.utxo_commitment.max_historical_commitments = count;
578 }
579 }
580 if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_INCREMENTAL") {
581 if let Ok(enabled) = val.parse::<bool>() {
582 config.utxo_commitment.enable_incremental_updates = enabled;
583 }
584 }
585
586 if let Ok(val) = std::env::var("BLVM_SCRIPT_THREADS") {
588 if let Ok(threads) = val.parse::<usize>() {
589 config.performance.script_verification_threads = threads;
590 }
591 }
592 if let Ok(val) = std::env::var("BLVM_PARALLEL_BATCH_SIZE") {
593 if let Ok(size) = val.parse::<usize>() {
594 config.performance.parallel_batch_size = size;
595 }
596 }
597 if let Ok(val) = std::env::var("BLVM_SIMD") {
598 if let Ok(enabled) = val.parse::<bool>() {
599 config.performance.enable_simd_optimizations = enabled;
600 }
601 }
602 if let Ok(val) = std::env::var("BLVM_CACHE_OPTIMIZATIONS") {
603 if let Ok(enabled) = val.parse::<bool>() {
604 config.performance.enable_cache_optimizations = enabled;
605 }
606 }
607 if let Ok(val) = std::env::var("BLVM_BATCH_UTXO_LOOKUPS") {
608 if let Ok(enabled) = val.parse::<bool>() {
609 config.performance.enable_batch_utxo_lookups = enabled;
610 }
611 }
612 if let Ok(val) = std::env::var("BLVM_IBD_CHUNK_THRESHOLD") {
613 if let Ok(n) = val.parse::<usize>() {
614 config.performance.ibd_chunk_threshold = Some(n);
615 }
616 }
617 if let Ok(val) = std::env::var("BLVM_IBD_MIN_CHUNK_SIZE") {
618 if let Ok(n) = val.parse::<usize>() {
619 config.performance.ibd_min_chunk_size = Some(n);
620 }
621 }
622
623 if let Ok(val) = std::env::var("BLVM_CONSENSUS_DEBUG") {
625 let parts: Vec<&str> = val.split(',').map(|s| s.trim()).collect();
626 for p in &parts {
627 match *p {
628 "full" => {
629 config.debug.enable_runtime_assertions = true;
630 config.debug.enable_runtime_invariants = true;
631 config.debug.enable_verbose_logging = true;
632 config.debug.enable_performance_profiling = true;
633 config.debug.log_rejections = true;
634 }
635 "assertions" => config.debug.enable_runtime_assertions = true,
636 "invariants" => config.debug.enable_runtime_invariants = true,
637 "verbose" => config.debug.enable_verbose_logging = true,
638 "profile" => config.debug.enable_performance_profiling = true,
639 "rejections" => config.debug.log_rejections = true,
640 _ => {}
641 }
642 }
643 }
644
645 if let Ok(val) = std::env::var("BLVM_CONSENSUS_FEATURES") {
647 let parts: Vec<&str> = val.split(',').map(|s| s.trim()).collect();
648 for p in &parts {
649 match *p {
650 "full" => {
651 config.features.enable_experimental_optimizations = true;
652 config.features.enable_bounds_check_optimizations = true;
653 config.features.enable_reference_checks = true;
654 config.features.enable_aggressive_caching = true;
655 config.features.enable_batch_tx_id_computation = true;
656 config.features.enable_simd_hash_operations = true;
657 }
658 "experimental" => config.features.enable_experimental_optimizations = true,
659 "bounds_check" => config.features.enable_bounds_check_optimizations = true,
660 "reference_checks" => config.features.enable_reference_checks = true,
661 "aggressive_cache" => config.features.enable_aggressive_caching = true,
662 "batch_txid" => config.features.enable_batch_tx_id_computation = true,
663 "simd_hash" => config.features.enable_simd_hash_operations = true,
664 _ => {}
665 }
666 }
667 }
668
669 if let Ok(val) = std::env::var("BLVM_CUSTOM_CHECKPOINTS") {
671 config.advanced.custom_checkpoints = val
673 .split(',')
674 .filter_map(|s| s.trim().parse::<u64>().ok())
675 .collect();
676 }
677 if let Ok(val) = std::env::var("BLVM_MAX_REORG_DEPTH") {
678 if let Ok(depth) = val.parse::<u64>() {
679 config.advanced.max_reorg_depth = depth;
680 }
681 }
682 if let Ok(val) = std::env::var("BLVM_STRICT_MODE") {
683 if let Ok(enabled) = val.parse::<bool>() {
684 config.advanced.strict_mode = enabled;
685 }
686 }
687 if let Ok(val) = std::env::var("BLVM_MAX_BLOCK_SIZE") {
688 if let Ok(size) = val.parse::<usize>() {
689 config.advanced.max_block_size_override = size;
690 }
691 }
692 if let Ok(val) = std::env::var("BLVM_RBF") {
693 if let Ok(enabled) = val.parse::<bool>() {
694 config.advanced.enable_rbf = enabled;
695 }
696 }
697
698 config
699 }
700
701 #[cfg(feature = "production")]
703 pub fn get_assume_valid_height(&self) -> u64 {
704 #[cfg(feature = "benchmarking")]
706 {
707 use std::sync::atomic::{AtomicU64, Ordering};
708 static OVERRIDE: AtomicU64 = AtomicU64::new(u64::MAX);
709 let override_val = OVERRIDE.load(Ordering::Relaxed);
710 if override_val != u64::MAX {
711 return override_val;
712 }
713 }
714
715 self.block_validation.assume_valid_height
716 }
717
718 #[cfg(not(feature = "production"))]
720 pub fn get_assume_valid_height(&self) -> u64 {
721 self.block_validation.assume_valid_height
722 }
723}
724
725static GLOBAL_CONSENSUS_CONFIG: std::sync::OnceLock<ConsensusConfig> = std::sync::OnceLock::new();
731
732#[allow(dead_code)] pub fn init_consensus_config(config: ConsensusConfig) {
737 let _ = GLOBAL_CONSENSUS_CONFIG.set(config);
738}
739
740pub fn get_consensus_config_ref() -> &'static ConsensusConfig {
744 GLOBAL_CONSENSUS_CONFIG.get_or_init(ConsensusConfig::from_env)
745}
746
747pub fn get_consensus_config() -> ConsensusConfig {
749 get_consensus_config_ref().clone()
750}
751
752#[cfg(all(feature = "production", feature = "benchmarking"))]
756static ASSUME_VALID_OVERRIDE: std::sync::atomic::AtomicU64 =
757 std::sync::atomic::AtomicU64::new(u64::MAX);
758
759pub fn get_assume_valid_height() -> u64 {
761 #[cfg(all(feature = "production", feature = "benchmarking"))]
762 {
763 use std::sync::atomic::Ordering;
764 let v = ASSUME_VALID_OVERRIDE.load(Ordering::Relaxed);
765 if v != u64::MAX {
766 return v;
767 }
768 }
769 get_consensus_config_ref()
770 .block_validation
771 .assume_valid_height
772}
773
774pub fn get_assume_valid_hash() -> Option<[u8; 32]> {
776 get_consensus_config_ref()
777 .block_validation
778 .assume_valid_hash
779}
780
781pub fn get_n_minimum_chain_work() -> u128 {
783 get_consensus_config_ref()
784 .block_validation
785 .n_minimum_chain_work
786}
787
788#[cfg(all(feature = "production", feature = "benchmarking"))]
790pub fn set_assume_valid_height(height: u64) {
791 use std::sync::atomic::Ordering;
792 ASSUME_VALID_OVERRIDE.store(height, Ordering::Relaxed);
793}
794
795#[cfg(all(feature = "production", feature = "benchmarking"))]
797pub fn reset_assume_valid_height() {
798 set_assume_valid_height(u64::MAX);
799}
800
801pub fn use_overlay_delta() -> bool {
805 true
806}
807
808#[cfg(all(feature = "production", feature = "rayon"))]
817pub fn init_rayon_for_script_verification() {
818 use std::sync::Once;
819 static INIT: Once = Once::new();
820 INIT.call_once(|| {
821 let config = get_consensus_config_ref();
822 let n = config.performance.script_verification_threads;
823 if n > 0 {
824 if let Err(e) = rayon::ThreadPoolBuilder::new()
825 .num_threads(n)
826 .build_global()
827 {
828 eprintln!(
829 "Warning: Failed to set Rayon script verification pool to {n} threads: {e}. Using default."
830 );
831 }
832 }
833 });
835}