use serde::{Deserialize, Serialize};
pub use blvm_primitives::config::{BlockValidationConfig, NetworkMessageLimits};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MempoolConfig {
#[serde(default = "default_max_mempool_mb")]
pub max_mempool_mb: u64,
#[serde(default = "default_max_mempool_txs")]
pub max_mempool_txs: usize,
#[serde(default = "default_mempool_expiry_hours")]
pub mempool_expiry_hours: u64,
#[serde(default = "default_min_relay_fee_rate")]
pub min_relay_fee_rate: u64,
#[serde(default = "default_min_tx_fee")]
pub min_tx_fee: i64,
#[serde(default = "default_rbf_fee_increment")]
pub rbf_fee_increment: i64,
#[serde(default = "default_max_op_return_size")]
pub max_op_return_size: u32,
#[serde(default = "default_max_op_return_outputs")]
pub max_op_return_outputs: u32,
#[serde(default = "default_reject_multiple_op_return")]
pub reject_multiple_op_return: bool,
#[serde(default = "default_max_standard_script_size")]
pub max_standard_script_size: u32,
#[serde(default = "default_reject_envelope_protocol")]
pub reject_envelope_protocol: bool,
#[serde(default = "default_reject_spam_in_mempool")]
pub reject_spam_in_mempool: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub spam_filter_config: Option<serde_json::Value>,
#[serde(default = "default_min_fee_rate_large_tx")]
pub min_fee_rate_large_tx: u64,
#[serde(default = "default_large_tx_threshold_bytes")]
pub large_tx_threshold_bytes: u64,
}
fn default_rbf_fee_increment() -> i64 {
1000
}
fn default_max_mempool_mb() -> u64 {
300
}
fn default_max_mempool_txs() -> usize {
100_000
}
fn default_mempool_expiry_hours() -> u64 {
336 }
fn default_min_relay_fee_rate() -> u64 {
1 }
fn default_min_tx_fee() -> i64 {
1000
}
fn default_max_op_return_size() -> u32 {
80
}
fn default_max_op_return_outputs() -> u32 {
1
}
fn default_reject_multiple_op_return() -> bool {
true
}
fn default_max_standard_script_size() -> u32 {
200
}
fn default_reject_envelope_protocol() -> bool {
true
}
fn default_reject_spam_in_mempool() -> bool {
false
}
fn default_min_fee_rate_large_tx() -> u64 {
2 }
fn default_large_tx_threshold_bytes() -> u64 {
1000 }
impl Default for MempoolConfig {
fn default() -> Self {
Self {
max_mempool_mb: 300,
max_mempool_txs: 100_000,
mempool_expiry_hours: 336,
min_relay_fee_rate: 1,
min_tx_fee: 1000,
rbf_fee_increment: 1000,
max_op_return_size: 80,
max_op_return_outputs: 1,
reject_multiple_op_return: true,
max_standard_script_size: 200,
reject_envelope_protocol: true,
reject_spam_in_mempool: false,
spam_filter_config: None,
min_fee_rate_large_tx: 2,
large_tx_threshold_bytes: 1000,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UtxoCommitmentConfig {
#[serde(default = "default_max_utxo_commitment_set_mb")]
pub max_utxo_commitment_set_mb: u64,
#[serde(default = "default_max_utxo_count")]
pub max_utxo_count: u64,
#[serde(default = "default_max_historical_commitments")]
pub max_historical_commitments: usize,
#[serde(default = "default_true")]
pub enable_incremental_updates: bool,
}
fn default_max_utxo_commitment_set_mb() -> u64 {
512
}
fn default_max_utxo_count() -> u64 {
100_000_000
}
fn default_max_historical_commitments() -> usize {
1000
}
impl Default for UtxoCommitmentConfig {
fn default() -> Self {
Self {
max_utxo_commitment_set_mb: 512,
max_utxo_count: 100_000_000,
max_historical_commitments: 1000,
enable_incremental_updates: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PerformanceConfig {
#[serde(default)]
pub script_verification_threads: usize,
#[serde(default = "default_parallel_batch_size")]
pub parallel_batch_size: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ibd_chunk_threshold: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ibd_min_chunk_size: Option<usize>,
#[serde(default = "default_true")]
pub enable_simd_optimizations: bool,
#[serde(default = "default_true")]
pub enable_cache_optimizations: bool,
#[serde(default = "default_true")]
pub enable_batch_utxo_lookups: bool,
}
fn default_parallel_batch_size() -> usize {
8
}
impl Default for PerformanceConfig {
fn default() -> Self {
Self {
script_verification_threads: 0, parallel_batch_size: 8,
ibd_chunk_threshold: None,
ibd_min_chunk_size: None,
enable_simd_optimizations: true,
enable_cache_optimizations: true,
enable_batch_utxo_lookups: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct DebugConfig {
#[serde(default = "default_false")]
pub enable_runtime_assertions: bool,
#[serde(default = "default_false")]
pub enable_runtime_invariants: bool,
#[serde(default = "default_false")]
pub enable_verbose_logging: bool,
#[serde(default = "default_false")]
pub enable_performance_profiling: bool,
#[serde(default = "default_false")]
pub log_rejections: bool,
}
fn default_false() -> bool {
false
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FeatureFlagsConfig {
#[serde(default = "default_false")]
pub enable_experimental_optimizations: bool,
#[serde(default = "default_true")]
pub enable_bounds_check_optimizations: bool,
#[serde(default = "default_false")]
pub enable_reference_checks: bool,
#[serde(default = "default_true")]
pub enable_aggressive_caching: bool,
#[serde(default = "default_true")]
pub enable_batch_tx_id_computation: bool,
#[serde(default = "default_true")]
pub enable_simd_hash_operations: bool,
}
impl Default for FeatureFlagsConfig {
fn default() -> Self {
Self {
enable_experimental_optimizations: false,
enable_bounds_check_optimizations: true,
enable_reference_checks: false,
enable_aggressive_caching: true,
enable_batch_tx_id_computation: true,
enable_simd_hash_operations: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AdvancedConfig {
#[serde(default)]
pub custom_checkpoints: Vec<u64>,
#[serde(default = "default_max_reorg_depth")]
pub max_reorg_depth: u64,
#[serde(default = "default_false")]
pub strict_mode: bool,
#[serde(default)]
pub max_block_size_override: usize,
#[serde(default = "default_true")]
pub enable_rbf: bool,
}
fn default_max_reorg_depth() -> u64 {
100
}
impl Default for AdvancedConfig {
fn default() -> Self {
Self {
custom_checkpoints: Vec::new(),
max_reorg_depth: 100,
strict_mode: false,
max_block_size_override: 0,
enable_rbf: true,
}
}
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct ConsensusConfig {
#[serde(default)]
pub network_limits: NetworkMessageLimits,
#[serde(default)]
pub block_validation: BlockValidationConfig,
#[serde(default)]
pub mempool: MempoolConfig,
#[serde(default)]
pub utxo_commitment: UtxoCommitmentConfig,
#[serde(default)]
pub performance: PerformanceConfig,
#[serde(default)]
pub debug: DebugConfig,
#[serde(default)]
pub features: FeatureFlagsConfig,
#[serde(default)]
pub advanced: AdvancedConfig,
}
impl ConsensusConfig {
pub fn from_env() -> Self {
let mut config = Self::default();
if let Ok(val) = std::env::var("BLVM_ASSUME_VALID_HEIGHT") {
if let Ok(height) = val.parse::<u64>() {
config.block_validation.assume_valid_height = height;
}
}
if let Ok(val) = std::env::var("BLVM_MTP_HEADERS") {
if let Ok(count) = val.parse::<usize>() {
config.block_validation.median_time_past_headers = count;
}
}
if let Ok(val) = std::env::var("BLVM_PARALLEL_VALIDATION") {
if let Ok(enabled) = val.parse::<bool>() {
config.block_validation.enable_parallel_validation = enabled;
}
}
if let Ok(val) = std::env::var("BLVM_COINBASE_MATURITY") {
if let Ok(maturity) = val.parse::<u64>() {
config.block_validation.coinbase_maturity_override = maturity;
}
}
if let Ok(val) = std::env::var("BLVM_MAX_SIGOPS_COST") {
if let Ok(cost) = val.parse::<u64>() {
config.block_validation.max_block_sigops_cost_override = cost;
}
}
if let Ok(val) = std::env::var("BLVM_MAX_ADDR_ADDRESSES") {
if let Ok(limit) = val.parse::<usize>() {
config.network_limits.max_addr_addresses = limit;
}
}
if let Ok(val) = std::env::var("BLVM_MAX_INV_ITEMS") {
if let Ok(limit) = val.parse::<usize>() {
config.network_limits.max_inv_items = limit;
}
}
if let Ok(val) = std::env::var("BLVM_MAX_HEADERS") {
if let Ok(limit) = val.parse::<usize>() {
config.network_limits.max_headers = limit;
}
}
if let Ok(val) = std::env::var("BLVM_MAX_USER_AGENT_LENGTH") {
if let Ok(limit) = val.parse::<usize>() {
config.network_limits.max_user_agent_length = limit;
}
}
if let Ok(val) = std::env::var("BLVM_MEMPOOL_MB") {
if let Ok(mb) = val.parse::<u64>() {
config.mempool.max_mempool_mb = mb;
}
}
if let Ok(val) = std::env::var("BLVM_MEMPOOL_TXS") {
if let Ok(count) = val.parse::<usize>() {
config.mempool.max_mempool_txs = count;
}
}
if let Ok(val) = std::env::var("BLVM_MEMPOOL_EXPIRY_HOURS") {
if let Ok(hours) = val.parse::<u64>() {
config.mempool.mempool_expiry_hours = hours;
}
}
if let Ok(val) = std::env::var("BLVM_MEMPOOL_MIN_RELAY_FEE") {
if let Ok(rate) = val.parse::<u64>() {
config.mempool.min_relay_fee_rate = rate;
}
}
if let Ok(val) = std::env::var("BLVM_MEMPOOL_MIN_TX_FEE") {
if let Ok(fee) = val.parse::<i64>() {
config.mempool.min_tx_fee = fee;
}
}
if let Ok(val) = std::env::var("BLVM_MEMPOOL_RBF_FEE_INCREMENT") {
if let Ok(increment) = val.parse::<i64>() {
config.mempool.rbf_fee_increment = increment;
}
}
if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_SET_MB") {
if let Ok(mb) = val.parse::<u64>() {
config.utxo_commitment.max_utxo_commitment_set_mb = mb;
}
}
if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_UTXO_COUNT") {
if let Ok(count) = val.parse::<u64>() {
config.utxo_commitment.max_utxo_count = count;
}
}
if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_MAX_HISTORICAL") {
if let Ok(count) = val.parse::<usize>() {
config.utxo_commitment.max_historical_commitments = count;
}
}
if let Ok(val) = std::env::var("BLVM_UTXO_COMMITMENT_INCREMENTAL") {
if let Ok(enabled) = val.parse::<bool>() {
config.utxo_commitment.enable_incremental_updates = enabled;
}
}
if let Ok(val) = std::env::var("BLVM_SCRIPT_THREADS") {
if let Ok(threads) = val.parse::<usize>() {
config.performance.script_verification_threads = threads;
}
}
if let Ok(val) = std::env::var("BLVM_PARALLEL_BATCH_SIZE") {
if let Ok(size) = val.parse::<usize>() {
config.performance.parallel_batch_size = size;
}
}
if let Ok(val) = std::env::var("BLVM_SIMD") {
if let Ok(enabled) = val.parse::<bool>() {
config.performance.enable_simd_optimizations = enabled;
}
}
if let Ok(val) = std::env::var("BLVM_CACHE_OPTIMIZATIONS") {
if let Ok(enabled) = val.parse::<bool>() {
config.performance.enable_cache_optimizations = enabled;
}
}
if let Ok(val) = std::env::var("BLVM_BATCH_UTXO_LOOKUPS") {
if let Ok(enabled) = val.parse::<bool>() {
config.performance.enable_batch_utxo_lookups = enabled;
}
}
if let Ok(val) = std::env::var("BLVM_IBD_CHUNK_THRESHOLD") {
if let Ok(n) = val.parse::<usize>() {
config.performance.ibd_chunk_threshold = Some(n);
}
}
if let Ok(val) = std::env::var("BLVM_IBD_MIN_CHUNK_SIZE") {
if let Ok(n) = val.parse::<usize>() {
config.performance.ibd_min_chunk_size = Some(n);
}
}
if let Ok(val) = std::env::var("BLVM_CONSENSUS_DEBUG") {
let parts: Vec<&str> = val.split(',').map(|s| s.trim()).collect();
for p in &parts {
match *p {
"full" => {
config.debug.enable_runtime_assertions = true;
config.debug.enable_runtime_invariants = true;
config.debug.enable_verbose_logging = true;
config.debug.enable_performance_profiling = true;
config.debug.log_rejections = true;
}
"assertions" => config.debug.enable_runtime_assertions = true,
"invariants" => config.debug.enable_runtime_invariants = true,
"verbose" => config.debug.enable_verbose_logging = true,
"profile" => config.debug.enable_performance_profiling = true,
"rejections" => config.debug.log_rejections = true,
_ => {}
}
}
}
if let Ok(val) = std::env::var("BLVM_CONSENSUS_FEATURES") {
let parts: Vec<&str> = val.split(',').map(|s| s.trim()).collect();
for p in &parts {
match *p {
"full" => {
config.features.enable_experimental_optimizations = true;
config.features.enable_bounds_check_optimizations = true;
config.features.enable_reference_checks = true;
config.features.enable_aggressive_caching = true;
config.features.enable_batch_tx_id_computation = true;
config.features.enable_simd_hash_operations = true;
}
"experimental" => config.features.enable_experimental_optimizations = true,
"bounds_check" => config.features.enable_bounds_check_optimizations = true,
"reference_checks" => config.features.enable_reference_checks = true,
"aggressive_cache" => config.features.enable_aggressive_caching = true,
"batch_txid" => config.features.enable_batch_tx_id_computation = true,
"simd_hash" => config.features.enable_simd_hash_operations = true,
_ => {}
}
}
}
if let Ok(val) = std::env::var("BLVM_CUSTOM_CHECKPOINTS") {
config.advanced.custom_checkpoints = val
.split(',')
.filter_map(|s| s.trim().parse::<u64>().ok())
.collect();
}
if let Ok(val) = std::env::var("BLVM_MAX_REORG_DEPTH") {
if let Ok(depth) = val.parse::<u64>() {
config.advanced.max_reorg_depth = depth;
}
}
if let Ok(val) = std::env::var("BLVM_STRICT_MODE") {
if let Ok(enabled) = val.parse::<bool>() {
config.advanced.strict_mode = enabled;
}
}
if let Ok(val) = std::env::var("BLVM_MAX_BLOCK_SIZE") {
if let Ok(size) = val.parse::<usize>() {
config.advanced.max_block_size_override = size;
}
}
if let Ok(val) = std::env::var("BLVM_RBF") {
if let Ok(enabled) = val.parse::<bool>() {
config.advanced.enable_rbf = enabled;
}
}
config
}
#[cfg(feature = "production")]
pub fn get_assume_valid_height(&self) -> u64 {
#[cfg(feature = "benchmarking")]
{
use std::sync::atomic::{AtomicU64, Ordering};
static OVERRIDE: AtomicU64 = AtomicU64::new(u64::MAX);
let override_val = OVERRIDE.load(Ordering::Relaxed);
if override_val != u64::MAX {
return override_val;
}
}
self.block_validation.assume_valid_height
}
#[cfg(not(feature = "production"))]
pub fn get_assume_valid_height(&self) -> u64 {
self.block_validation.assume_valid_height
}
}
static GLOBAL_CONSENSUS_CONFIG: std::sync::OnceLock<ConsensusConfig> = std::sync::OnceLock::new();
#[allow(dead_code)] pub fn init_consensus_config(config: ConsensusConfig) {
let _ = GLOBAL_CONSENSUS_CONFIG.set(config);
}
pub fn get_consensus_config_ref() -> &'static ConsensusConfig {
GLOBAL_CONSENSUS_CONFIG.get_or_init(ConsensusConfig::from_env)
}
pub fn get_consensus_config() -> ConsensusConfig {
get_consensus_config_ref().clone()
}
#[cfg(all(feature = "production", feature = "benchmarking"))]
static ASSUME_VALID_OVERRIDE: std::sync::atomic::AtomicU64 =
std::sync::atomic::AtomicU64::new(u64::MAX);
pub fn get_assume_valid_height() -> u64 {
#[cfg(all(feature = "production", feature = "benchmarking"))]
{
use std::sync::atomic::Ordering;
let v = ASSUME_VALID_OVERRIDE.load(Ordering::Relaxed);
if v != u64::MAX {
return v;
}
}
get_consensus_config_ref()
.block_validation
.assume_valid_height
}
pub fn get_assume_valid_hash() -> Option<[u8; 32]> {
get_consensus_config_ref()
.block_validation
.assume_valid_hash
}
pub fn get_n_minimum_chain_work() -> u128 {
get_consensus_config_ref()
.block_validation
.n_minimum_chain_work
}
#[cfg(all(feature = "production", feature = "benchmarking"))]
pub fn set_assume_valid_height(height: u64) {
use std::sync::atomic::Ordering;
ASSUME_VALID_OVERRIDE.store(height, Ordering::Relaxed);
}
#[cfg(all(feature = "production", feature = "benchmarking"))]
pub fn reset_assume_valid_height() {
set_assume_valid_height(u64::MAX);
}
pub fn use_overlay_delta() -> bool {
true
}
#[cfg(all(feature = "production", feature = "rayon"))]
pub fn init_rayon_for_script_verification() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
let config = get_consensus_config_ref();
let n = config.performance.script_verification_threads;
if n > 0 {
if let Err(e) = rayon::ThreadPoolBuilder::new()
.num_threads(n)
.build_global()
{
eprintln!(
"Warning: Failed to set Rayon script verification pool to {n} threads: {e}. Using default."
);
}
}
});
}