Skip to main content

blvm_consensus/
config.rs

1//! Configuration for blvm-consensus
2//!
3//! Provides configurable parameters for consensus validation, network message limits,
4//! and performance optimizations. These settings can be loaded from config files,
5//! environment variables, or passed programmatically.
6
7use serde::{Deserialize, Serialize};
8
9// Re-export foundational config types from blvm-primitives
10pub use blvm_primitives::config::{
11    AdvancedConfig, BlockValidationConfig, DebugConfig, FeatureFlagsConfig, MempoolConfig,
12    NetworkMessageLimits, PerformanceConfig, UtxoCommitmentConfig,
13};
14
15/// Complete consensus configuration
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
17pub struct ConsensusConfig {
18    /// Network message size limits
19    #[serde(default)]
20    pub network_limits: NetworkMessageLimits,
21
22    /// Block validation configuration
23    #[serde(default)]
24    pub block_validation: BlockValidationConfig,
25
26    /// Mempool configuration
27    #[serde(default)]
28    pub mempool: MempoolConfig,
29
30    /// UTXO commitment set configuration
31    #[serde(default)]
32    pub utxo_commitment: UtxoCommitmentConfig,
33
34    /// Performance and optimization configuration
35    #[serde(default)]
36    pub performance: PerformanceConfig,
37
38    /// Debug and development configuration
39    #[serde(default)]
40    pub debug: DebugConfig,
41
42    /// Feature flags configuration
43    #[serde(default)]
44    pub features: FeatureFlagsConfig,
45
46    /// Advanced configuration options
47    #[serde(default)]
48    pub advanced: AdvancedConfig,
49}
50
51impl ConsensusConfig {
52    /// Load configuration from environment variables
53    ///
54    /// Environment variables. Short names (e.g. BLVM_ASSUME_VALID_HEIGHT) preferred.
55    pub fn from_env() -> Self {
56        let mut config = Self::default();
57
58        // Block validation
59        if let Ok(val) = std::env::var("BLVM_ASSUME_VALID_HEIGHT") {
60            if let Ok(height) = val.parse::<u64>() {
61                config.block_validation.assume_valid_height = height;
62            }
63        }
64
65        if let Ok(val) = std::env::var("BLVM_MAX_ADDR_ADDRESSES") {
66            if let Ok(limit) = val.parse::<usize>() {
67                config.network_limits.max_addr_addresses = limit;
68            }
69        }
70
71        if let Ok(val) = std::env::var("BLVM_MAX_INV_ITEMS") {
72            if let Ok(limit) = val.parse::<usize>() {
73                config.network_limits.max_inv_items = limit;
74            }
75        }
76
77        if let Ok(val) = std::env::var("BLVM_MAX_HEADERS") {
78            if let Ok(limit) = val.parse::<usize>() {
79                config.network_limits.max_headers = limit;
80            }
81        }
82
83        if let Ok(val) = std::env::var("BLVM_MAX_USER_AGENT_LENGTH") {
84            if let Ok(limit) = val.parse::<usize>() {
85                config.network_limits.max_user_agent_length = limit;
86            }
87        }
88
89        // Load mempool configuration
90        if let Ok(val) = std::env::var("BLVM_MEMPOOL_MB") {
91            if let Ok(mb) = val.parse::<u64>() {
92                config.mempool.max_mempool_mb = mb;
93            }
94        }
95        if let Ok(val) = std::env::var("BLVM_MEMPOOL_TXS") {
96            if let Ok(count) = val.parse::<usize>() {
97                config.mempool.max_mempool_txs = count;
98            }
99        }
100        if let Ok(val) = std::env::var("BLVM_MEMPOOL_EXPIRY_HOURS") {
101            if let Ok(hours) = val.parse::<u64>() {
102                config.mempool.mempool_expiry_hours = hours;
103            }
104        }
105        if let Ok(val) = std::env::var("BLVM_MEMPOOL_MIN_RELAY_FEE") {
106            if let Ok(rate) = val.parse::<u64>() {
107                config.mempool.min_relay_fee_rate = rate;
108            }
109        }
110        if let Ok(val) = std::env::var("BLVM_MEMPOOL_MIN_TX_FEE") {
111            if let Ok(fee) = val.parse::<i64>() {
112                config.mempool.min_tx_fee = fee;
113            }
114        }
115        if let Ok(val) = std::env::var("BLVM_MEMPOOL_RBF_FEE_INCREMENT") {
116            if let Ok(increment) = val.parse::<i64>() {
117                config.mempool.rbf_fee_increment = increment;
118            }
119        }
120
121        // Load UTXO commitment configuration
122        if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_SET_MB") {
123            if let Ok(mb) = val.parse::<u64>() {
124                config.utxo_commitment.max_utxo_commitment_set_mb = mb;
125            }
126        }
127        if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_UTXO_COUNT") {
128            if let Ok(count) = val.parse::<u64>() {
129                config.utxo_commitment.max_utxo_count = count;
130            }
131        }
132        if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_HISTORICAL") {
133            if let Ok(count) = val.parse::<usize>() {
134                config.utxo_commitment.max_historical_commitments = count;
135            }
136        }
137        if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_INCREMENTAL") {
138            if let Ok(enabled) = val.parse::<bool>() {
139                config.utxo_commitment.enable_incremental_updates = enabled;
140            }
141        }
142
143        // Load performance configuration
144        if let Ok(val) = std::env::var("BLVM_SCRIPT_THREADS") {
145            if let Ok(threads) = val.parse::<usize>() {
146                config.performance.script_verification_threads = threads;
147            }
148        }
149        if let Ok(val) = std::env::var("BLVM_PARALLEL_BATCH_SIZE") {
150            if let Ok(size) = val.parse::<usize>() {
151                config.performance.parallel_batch_size = size;
152            }
153        }
154        if let Ok(val) = std::env::var("BLVM_SIMD") {
155            if let Ok(enabled) = val.parse::<bool>() {
156                config.performance.enable_simd_optimizations = enabled;
157            }
158        }
159        if let Ok(val) = std::env::var("BLVM_CACHE_OPTIMIZATIONS") {
160            if let Ok(enabled) = val.parse::<bool>() {
161                config.performance.enable_cache_optimizations = enabled;
162            }
163        }
164        if let Ok(val) = std::env::var("BLVM_BATCH_UTXO_LOOKUPS") {
165            if let Ok(enabled) = val.parse::<bool>() {
166                config.performance.enable_batch_utxo_lookups = enabled;
167            }
168        }
169        if let Ok(val) = std::env::var("BLVM_IBD_CHUNK_THRESHOLD") {
170            if let Ok(n) = val.parse::<usize>() {
171                config.performance.ibd_chunk_threshold = Some(n);
172            }
173        }
174        if let Ok(val) = std::env::var("BLVM_IBD_MIN_CHUNK_SIZE") {
175            if let Ok(n) = val.parse::<usize>() {
176                config.performance.ibd_min_chunk_size = Some(n);
177            }
178        }
179
180        // Load debug configuration: BLVM_CONSENSUS_DEBUG=assertions,invariants,verbose,profile,rejections or =full
181        if let Ok(val) = std::env::var("BLVM_CONSENSUS_DEBUG") {
182            let parts: Vec<&str> = val.split(',').map(|s| s.trim()).collect();
183            for p in &parts {
184                match *p {
185                    "full" => {
186                        config.debug.enable_runtime_assertions = true;
187                        config.debug.enable_runtime_invariants = true;
188                        config.debug.enable_verbose_logging = true;
189                        config.debug.enable_performance_profiling = true;
190                        config.debug.log_rejections = true;
191                    }
192                    "assertions" => config.debug.enable_runtime_assertions = true,
193                    "invariants" => config.debug.enable_runtime_invariants = true,
194                    "verbose" => config.debug.enable_verbose_logging = true,
195                    "profile" => config.debug.enable_performance_profiling = true,
196                    "rejections" => config.debug.log_rejections = true,
197                    _ => {}
198                }
199            }
200        }
201
202        // Load feature flags: BLVM_CONSENSUS_FEATURES=experimental,bounds_check,reference_checks,aggressive_cache,batch_txid,simd_hash or =full
203        if let Ok(val) = std::env::var("BLVM_CONSENSUS_FEATURES") {
204            let parts: Vec<&str> = val.split(',').map(|s| s.trim()).collect();
205            for p in &parts {
206                match *p {
207                    "full" => {
208                        config.features.enable_experimental_optimizations = true;
209                        config.features.enable_bounds_check_optimizations = true;
210                        config.features.enable_reference_checks = true;
211                        config.features.enable_aggressive_caching = true;
212                        config.features.enable_batch_tx_id_computation = true;
213                        config.features.enable_simd_hash_operations = true;
214                    }
215                    "experimental" => config.features.enable_experimental_optimizations = true,
216                    "bounds_check" => config.features.enable_bounds_check_optimizations = true,
217                    "reference_checks" => config.features.enable_reference_checks = true,
218                    "aggressive_cache" => config.features.enable_aggressive_caching = true,
219                    "batch_txid" => config.features.enable_batch_tx_id_computation = true,
220                    "simd_hash" => config.features.enable_simd_hash_operations = true,
221                    _ => {}
222                }
223            }
224        }
225
226        // Load advanced configuration
227        if let Ok(val) = std::env::var("BLVM_CUSTOM_CHECKPOINTS") {
228            // Parse comma-separated list of heights
229            config.advanced.custom_checkpoints = val
230                .split(',')
231                .filter_map(|s| s.trim().parse::<u64>().ok())
232                .collect();
233        }
234        if let Ok(val) = std::env::var("BLVM_MAX_REORG_DEPTH") {
235            if let Ok(depth) = val.parse::<u64>() {
236                config.advanced.max_reorg_depth = depth;
237            }
238        }
239        if let Ok(val) = std::env::var("BLVM_STRICT_MODE") {
240            if let Ok(enabled) = val.parse::<bool>() {
241                config.advanced.strict_mode = enabled;
242            }
243        }
244        if let Ok(val) = std::env::var("BLVM_MAX_BLOCK_SIZE") {
245            if let Ok(size) = val.parse::<usize>() {
246                config.advanced.max_block_size_override = size;
247            }
248        }
249        if let Ok(val) = std::env::var("BLVM_RBF") {
250            if let Ok(enabled) = val.parse::<bool>() {
251                config.advanced.enable_rbf = enabled;
252            }
253        }
254
255        config
256    }
257
258    /// Get assume-valid height (respects benchmarking override if enabled)
259    #[cfg(feature = "production")]
260    pub fn get_assume_valid_height(&self) -> u64 {
261        // Check for benchmarking override first
262        #[cfg(feature = "benchmarking")]
263        {
264            use std::sync::atomic::{AtomicU64, Ordering};
265            static OVERRIDE: AtomicU64 = AtomicU64::new(u64::MAX);
266            let override_val = OVERRIDE.load(Ordering::Relaxed);
267            if override_val != u64::MAX {
268                return override_val;
269            }
270        }
271
272        self.block_validation.assume_valid_height
273    }
274
275    /// Get assume-valid height (non-production version)
276    #[cfg(not(feature = "production"))]
277    pub fn get_assume_valid_height(&self) -> u64 {
278        self.block_validation.assume_valid_height
279    }
280}
281
282/// Global consensus configuration (cached at first use).
283///
284/// Uses a single OnceLock — from_env() runs once, then we clone.
285/// `init_consensus_config` overrides that cache when the node loads config.
286/// CRITICAL: Was re-running 50+ std::env::var() per block before caching.
287static GLOBAL_CONSENSUS_CONFIG: std::sync::OnceLock<ConsensusConfig> = std::sync::OnceLock::new();
288
289/// Initialize global consensus configuration.
290///
291/// If called before any get_consensus_config(), overrides the default from-env config.
292pub fn init_consensus_config(config: ConsensusConfig) {
293    let _ = GLOBAL_CONSENSUS_CONFIG.set(config);
294}
295
296/// Get global consensus configuration by reference (cached; no clone).
297///
298/// Prefer this over [`get_consensus_config`] in hot paths to avoid cloning.
299pub fn get_consensus_config_ref() -> &'static ConsensusConfig {
300    GLOBAL_CONSENSUS_CONFIG.get_or_init(ConsensusConfig::from_env)
301}
302
303/// Get global consensus configuration (cached; clone for compatibility).
304pub fn get_consensus_config() -> ConsensusConfig {
305    get_consensus_config_ref().clone()
306}
307
308/// Assume-valid height (cached). Used by block and script hot paths.
309/// Runtime override for assume-valid height, shared between get/set.
310/// u64::MAX means "no override" (fall through to config).
311#[cfg(all(feature = "production", feature = "benchmarking"))]
312static ASSUME_VALID_OVERRIDE: std::sync::atomic::AtomicU64 =
313    std::sync::atomic::AtomicU64::new(u64::MAX);
314
315/// Benchmarking: set_assume_valid_height() overrides when feature enabled.
316pub fn get_assume_valid_height() -> u64 {
317    #[cfg(all(feature = "production", feature = "benchmarking"))]
318    {
319        use std::sync::atomic::Ordering;
320        let v = ASSUME_VALID_OVERRIDE.load(Ordering::Relaxed);
321        if v != u64::MAX {
322            return v;
323        }
324    }
325    get_consensus_config_ref()
326        .block_validation
327        .assume_valid_height
328}
329
330/// Assume-valid block hash. When set, block at assume_valid_height must match.
331pub fn get_assume_valid_hash() -> Option<[u8; 32]> {
332    get_consensus_config_ref()
333        .block_validation
334        .assume_valid_hash
335}
336
337/// Minimum chain work. Skip only when best_header_chainwork >= this.
338pub fn get_n_minimum_chain_work() -> u128 {
339    get_consensus_config_ref()
340        .block_validation
341        .n_minimum_chain_work
342}
343
344/// Set assume-valid height for benchmarking (overrides config).
345#[cfg(all(feature = "production", feature = "benchmarking"))]
346pub fn set_assume_valid_height(height: u64) {
347    use std::sync::atomic::Ordering;
348    ASSUME_VALID_OVERRIDE.store(height, Ordering::Relaxed);
349}
350
351/// Reset assume-valid override (benchmarking).
352#[cfg(all(feature = "production", feature = "benchmarking"))]
353pub fn reset_assume_valid_height() {
354    set_assume_valid_height(u64::MAX);
355}
356
357/// Use overlay delta for UTXO merge instead of sync_block_to_batch.
358/// Always enabled. connect_block_ibd returns UtxoDelta for the node to apply to pending_writes
359/// without re-walking the block.
360pub fn use_overlay_delta() -> bool {
361    true
362}
363
364/// Initialize Rayon thread pool for script verification.
365///
366/// Call this at node startup before any block validation.
367/// - When `script_verification_threads` > 0: use that value explicitly.
368/// - When 0: let Rayon use its default (respects RAYON_NUM_THREADS env; typically num_cpus).
369///   IBD scripts set RAYON_NUM_THREADS=nproc-1 for par-1 workers.
370///
371/// Only takes effect once per process.
372#[cfg(all(feature = "production", feature = "rayon"))]
373pub fn init_rayon_for_script_verification() {
374    use std::sync::Once;
375    static INIT: Once = Once::new();
376    INIT.call_once(|| {
377        let config = get_consensus_config_ref();
378        let n = config.performance.script_verification_threads;
379        if n > 0 {
380            if let Err(e) = rayon::ThreadPoolBuilder::new()
381                .num_threads(n)
382                .build_global()
383            {
384                eprintln!(
385                    "Warning: Failed to set Rayon script verification pool to {n} threads: {e}. Using default."
386                );
387            }
388        }
389        // n==0: Rayon uses default pool (reads RAYON_NUM_THREADS if set)
390    });
391}