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