Skip to main content

blvm_primitives/
config.rs

1//! Configuration types for consensus and protocol layers
2//!
3//! Provides foundational config structs used by blvm-consensus and blvm-protocol.
4//! `ConsensusConfig` and other aggregates remain in blvm-consensus; operational
5//! limits used across layers live here.
6
7use serde::{Deserialize, Serialize};
8
9/// Network message size limits configuration
10///
11/// These limits protect against DoS attacks by bounding the size of network messages.
12/// All limits match Bitcoin protocol defaults.
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct NetworkMessageLimits {
15    /// Maximum addresses in an addr message (protocol default: 1000)
16    #[serde(default = "default_max_addr_addresses")]
17    pub max_addr_addresses: usize,
18
19    /// Maximum inventory items in inv/getdata messages (protocol default: 50000)
20    #[serde(default = "default_max_inv_items")]
21    pub max_inv_items: usize,
22
23    /// Maximum headers in a headers message (protocol default: 2000)
24    #[serde(default = "default_max_headers")]
25    pub max_headers: usize,
26
27    /// Maximum user agent length in version message (protocol default: 256 bytes)
28    #[serde(default = "default_max_user_agent_length")]
29    pub max_user_agent_length: usize,
30}
31
32fn default_max_addr_addresses() -> usize {
33    1000
34}
35
36fn default_max_inv_items() -> usize {
37    50000
38}
39
40fn default_max_headers() -> usize {
41    2000
42}
43
44fn default_max_user_agent_length() -> usize {
45    256
46}
47
48impl Default for NetworkMessageLimits {
49    fn default() -> Self {
50        Self {
51            max_addr_addresses: 1000,
52            max_inv_items: 50000,
53            max_headers: 2000,
54            max_user_agent_length: 256,
55        }
56    }
57}
58
59/// Block validation configuration
60#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct BlockValidationConfig {
62    /// Assume-valid height: blocks before this height skip signature verification
63    #[serde(default)]
64    pub assume_valid_height: u64,
65
66    /// Assume-valid block hash: when set, verify block at assume_valid_height matches
67    #[serde(default)]
68    pub assume_valid_hash: Option<[u8; 32]>,
69
70    /// Minimum chain work: skip only when best_header_chainwork >= this
71    #[serde(default)]
72    pub n_minimum_chain_work: u128,
73
74    /// Number of recent headers for median time-past calculation (BIP113)
75    #[serde(default = "default_median_time_past_headers")]
76    pub median_time_past_headers: usize,
77
78    /// Enable parallel transaction validation
79    #[serde(default = "default_true")]
80    pub enable_parallel_validation: bool,
81
82    /// Coinbase maturity override (for testing only)
83    #[serde(default)]
84    pub coinbase_maturity_override: u64,
85
86    /// Maximum block sigop cost override (for testing only)
87    #[serde(default)]
88    pub max_block_sigops_cost_override: u64,
89}
90
91fn default_median_time_past_headers() -> usize {
92    11
93}
94
95fn default_true() -> bool {
96    true
97}
98
99impl Default for BlockValidationConfig {
100    fn default() -> Self {
101        Self {
102            // Matches Bitcoin Core's built-in hashAssumeValid (mainnet block 938343 in Core v28+).
103            // Core skips script/signature verification for all blocks below this height when the
104            // chain's total work meets nMinimumChainWork — BLVM must do the same to avoid false
105            // divergences on blocks that were accepted historically without script validation.
106            assume_valid_height: 938343,
107            assume_valid_hash: None,
108            n_minimum_chain_work: 0,
109            median_time_past_headers: 11,
110            enable_parallel_validation: true,
111            coinbase_maturity_override: 0,
112            max_block_sigops_cost_override: 0,
113        }
114    }
115}
116
117/// Mempool configuration
118///
119/// Controls mempool size limits, fee rates, and transaction expiry.
120/// These are operational parameters, not consensus-critical.
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122pub struct MempoolConfig {
123    /// Maximum mempool size in megabytes (default 300 MB)
124    /// Default: 300 MB
125    #[serde(default = "default_max_mempool_mb")]
126    pub max_mempool_mb: u64,
127
128    /// Maximum number of transactions in mempool (alternative to size-based limit)
129    /// Default: 100000
130    #[serde(default = "default_max_mempool_txs")]
131    pub max_mempool_txs: usize,
132
133    /// Mempool transaction expiry in hours (default 336 = 14 days)
134    /// Transactions older than this are removed from mempool
135    /// Default: 336 (14 days)
136    #[serde(default = "default_mempool_expiry_hours")]
137    pub mempool_expiry_hours: u64,
138
139    /// Minimum relay fee rate in satoshis per virtual byte (default 1 sat/vB)
140    /// Transactions with fee rate below this are not relayed
141    /// Default: 1 sat/vB (1000 sat/kB)
142    #[serde(default = "default_min_relay_fee_rate")]
143    pub min_relay_fee_rate: u64,
144
145    /// Minimum transaction fee in satoshis (absolute minimum, regardless of size)
146    /// Default: 1000 satoshis
147    #[serde(default = "default_min_tx_fee")]
148    pub min_tx_fee: i64,
149
150    /// RBF (Replace-By-Fee) minimum fee increment in satoshis (BIP125)
151    /// Replacement transactions must pay at least this much more than the original
152    /// Default: 1000 satoshis
153    #[serde(default = "default_rbf_fee_increment")]
154    pub rbf_fee_increment: i64,
155
156    /// Maximum OP_RETURN data size in bytes (default 80)
157    /// Default: 80 bytes
158    #[serde(default = "default_max_op_return_size")]
159    pub max_op_return_size: u32,
160
161    /// Maximum number of OP_RETURN outputs allowed (default: 1)
162    /// Transactions with more than this are rejected as non-standard
163    #[serde(default = "default_max_op_return_outputs")]
164    pub max_op_return_outputs: u32,
165
166    /// Reject transactions with multiple OP_RETURN outputs
167    /// Default: true
168    #[serde(default = "default_reject_multiple_op_return")]
169    pub reject_multiple_op_return: bool,
170
171    /// Maximum standard script size in bytes
172    /// Default: 200 bytes
173    #[serde(default = "default_max_standard_script_size")]
174    pub max_standard_script_size: u32,
175
176    /// Reject envelope protocol (OP_FALSE OP_IF) scripts
177    /// Default: true
178    #[serde(default = "default_reject_envelope_protocol")]
179    pub reject_envelope_protocol: bool,
180
181    /// Reject spam transactions at mempool entry (opt-in)
182    /// Default: false (spam filtering is opt-in for mempool)
183    ///
184    /// **Admission:** enforced in `blvm-node` by `MempoolPolicyConfig::reject_spam_in_mempool`
185    /// in `MempoolManager::add_transaction` (uses `blvm-protocol` spam filter).
186    #[serde(default = "default_reject_spam_in_mempool")]
187    pub reject_spam_in_mempool: bool,
188
189    /// Spam filter configuration (if reject_spam_in_mempool is enabled)
190    /// Note: Prefer `blvm-node` `MempoolPolicyConfig::spam_filter` + `SpamFilterConfigSerializable`.
191    #[serde(default, skip_serializing_if = "Option::is_none")]
192    pub spam_filter_config: Option<serde_json::Value>,
193
194    /// Minimum fee rate for large transactions (satoshis per vbyte)
195    /// Transactions larger than large_tx_threshold_bytes must pay at least this fee rate
196    /// Default: 2 sat/vB (higher than standard min_relay_fee_rate)
197    #[serde(default = "default_min_fee_rate_large_tx")]
198    pub min_fee_rate_large_tx: u64,
199
200    /// Large transaction threshold (bytes)
201    /// Transactions larger than this require min_fee_rate_large_tx
202    /// Default: 1000 bytes
203    #[serde(default = "default_large_tx_threshold_bytes")]
204    pub large_tx_threshold_bytes: u64,
205}
206
207fn default_rbf_fee_increment() -> i64 {
208    1000
209}
210
211fn default_max_mempool_mb() -> u64 {
212    300
213}
214
215fn default_max_mempool_txs() -> usize {
216    100_000
217}
218
219fn default_mempool_expiry_hours() -> u64 {
220    336 // 14 days
221}
222
223fn default_min_relay_fee_rate() -> u64 {
224    1 // 1 sat/vB = 1000 sat/kB
225}
226
227fn default_min_tx_fee() -> i64 {
228    1000
229}
230
231fn default_max_op_return_size() -> u32 {
232    80
233}
234
235fn default_max_op_return_outputs() -> u32 {
236    1
237}
238
239fn default_reject_multiple_op_return() -> bool {
240    true
241}
242
243fn default_max_standard_script_size() -> u32 {
244    200
245}
246
247fn default_reject_envelope_protocol() -> bool {
248    true
249}
250
251fn default_reject_spam_in_mempool() -> bool {
252    false
253}
254
255fn default_min_fee_rate_large_tx() -> u64 {
256    2 // 2 sat/vB (higher than standard 1 sat/vB)
257}
258
259fn default_large_tx_threshold_bytes() -> u64 {
260    1000 // 1 KB
261}
262
263impl Default for MempoolConfig {
264    fn default() -> Self {
265        Self {
266            max_mempool_mb: 300,
267            max_mempool_txs: 100_000,
268            mempool_expiry_hours: 336,
269            min_relay_fee_rate: 1,
270            min_tx_fee: 1000,
271            rbf_fee_increment: 1000,
272            max_op_return_size: 80,
273            max_op_return_outputs: 1,
274            reject_multiple_op_return: true,
275            max_standard_script_size: 200,
276            reject_envelope_protocol: true,
277            reject_spam_in_mempool: false,
278            spam_filter_config: None,
279            min_fee_rate_large_tx: 2,
280            large_tx_threshold_bytes: 1000,
281        }
282    }
283}
284
285fn default_false() -> bool {
286    false
287}
288
289/// UTXO Commitment configuration
290///
291/// Controls UTXO commitment set size, storage limits, and performance tuning.
292#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
293pub struct UtxoCommitmentConfig {
294    /// Maximum UTXO commitment set size in megabytes
295    /// This limits the in-memory size of the UTXO Merkle tree
296    /// Default: 512 MB (sufficient for ~100M UTXOs)
297    #[serde(default = "default_max_utxo_commitment_set_mb")]
298    pub max_utxo_commitment_set_mb: u64,
299
300    /// Maximum number of UTXOs in commitment set (alternative to size-based limit)
301    /// Default: 100_000_000 (100 million UTXOs)
302    #[serde(default = "default_max_utxo_count")]
303    pub max_utxo_count: u64,
304
305    /// Maximum number of historical commitments to keep in memory
306    /// Older commitments are stored on disk
307    /// Default: 1000 (keeps last ~7 days of commitments at 1 per block)
308    #[serde(default = "default_max_historical_commitments")]
309    pub max_historical_commitments: usize,
310
311    /// Enable incremental commitment updates (recommended)
312    /// Default: true
313    #[serde(default = "default_true")]
314    pub enable_incremental_updates: bool,
315}
316
317fn default_max_utxo_commitment_set_mb() -> u64 {
318    512
319}
320
321fn default_max_utxo_count() -> u64 {
322    100_000_000
323}
324
325fn default_max_historical_commitments() -> usize {
326    1000
327}
328
329impl Default for UtxoCommitmentConfig {
330    fn default() -> Self {
331        Self {
332            max_utxo_commitment_set_mb: 512,
333            max_utxo_count: 100_000_000,
334            max_historical_commitments: 1000,
335            enable_incremental_updates: true,
336        }
337    }
338}
339
340/// Performance and optimization configuration
341///
342/// Controls performance tuning, parallelization, and optimization features.
343/// These are operational parameters that affect performance but not consensus correctness.
344///
345/// IBD batch tuning: When `ibd_chunk_threshold` / `ibd_min_chunk_size` are `None`,
346/// hardware-derived values are used. When `Some(x)`, config overrides hardware.
347#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
348pub struct PerformanceConfig {
349    /// Number of threads for script verification (default: number of CPU cores)
350    /// Default: 0 (auto-detect from CPU count)
351    #[serde(default)]
352    pub script_verification_threads: usize,
353
354    /// Batch size for parallel transaction validation
355    /// Larger batches improve throughput but increase latency
356    /// Default: 8 transactions per batch
357    #[serde(default = "default_parallel_batch_size")]
358    pub parallel_batch_size: usize,
359
360    /// IBD batch: chunk threshold (parallelize when sig count exceeds this).
361    /// None = use hardware-derived; Some(x) = override.
362    #[serde(default, skip_serializing_if = "Option::is_none")]
363    pub ibd_chunk_threshold: Option<usize>,
364
365    /// IBD batch: minimum chunk size for parallel batches.
366    /// None = use hardware-derived; Some(x) = override.
367    #[serde(default, skip_serializing_if = "Option::is_none")]
368    pub ibd_min_chunk_size: Option<usize>,
369
370    /// Enable SIMD/vectorization optimizations (if available)
371    /// Default: true
372    #[serde(default = "default_true")]
373    pub enable_simd_optimizations: bool,
374
375    /// Enable cache-friendly memory layouts
376    /// Default: true
377    #[serde(default = "default_true")]
378    pub enable_cache_optimizations: bool,
379
380    /// Enable batch UTXO lookups (pre-fetch all UTXOs before validation)
381    /// Default: true
382    #[serde(default = "default_true")]
383    pub enable_batch_utxo_lookups: bool,
384}
385
386fn default_parallel_batch_size() -> usize {
387    8
388}
389
390impl Default for PerformanceConfig {
391    fn default() -> Self {
392        Self {
393            script_verification_threads: 0, // Auto-detect
394            parallel_batch_size: 8,
395            ibd_chunk_threshold: None,
396            ibd_min_chunk_size: None,
397            enable_simd_optimizations: true,
398            enable_cache_optimizations: true,
399            enable_batch_utxo_lookups: true,
400        }
401    }
402}
403
404/// Debug and development configuration
405///
406/// Controls debug assertions, runtime checks, and development features.
407/// These options are safe to enable in production but may impact performance.
408#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
409pub struct DebugConfig {
410    /// Enable runtime assertions (debug_assert! statements)
411    /// Default: false (enabled automatically in debug builds)
412    #[serde(default = "default_false")]
413    pub enable_runtime_assertions: bool,
414
415    /// Enable runtime invariant checks (additional safety checks)
416    /// Default: false
417    #[serde(default = "default_false")]
418    pub enable_runtime_invariants: bool,
419
420    /// Enable verbose logging for consensus operations
421    /// Default: false
422    #[serde(default = "default_false")]
423    pub enable_verbose_logging: bool,
424
425    /// Enable performance profiling (timing measurements)
426    /// Default: false
427    #[serde(default = "default_false")]
428    pub enable_performance_profiling: bool,
429
430    /// Log all rejected transactions/blocks (for debugging)
431    /// Default: false
432    #[serde(default = "default_false")]
433    pub log_rejections: bool,
434}
435
436/// Feature flags configuration
437///
438/// Controls optional features and experimental functionality.
439/// These are safe to enable/disable without affecting consensus.
440#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
441pub struct FeatureFlagsConfig {
442    /// Enable experimental optimizations (may be unstable)
443    /// Default: false
444    #[serde(default = "default_false")]
445    pub enable_experimental_optimizations: bool,
446
447    /// Enable bounds check optimizations (requires formal proofs)
448    /// Default: true (if production feature enabled)
449    #[serde(default = "default_true")]
450    pub enable_bounds_check_optimizations: bool,
451
452    /// Enable reference implementation checks (slower but safer)
453    /// Default: false
454    #[serde(default = "default_false")]
455    pub enable_reference_checks: bool,
456
457    /// Enable aggressive caching (may use more memory)
458    /// Default: true
459    #[serde(default = "default_true")]
460    pub enable_aggressive_caching: bool,
461
462    /// Enable batch transaction ID computation (faster but uses more memory)
463    /// Default: true
464    #[serde(default = "default_true")]
465    pub enable_batch_tx_id_computation: bool,
466
467    /// Enable SIMD hash operations (faster on supported CPUs)
468    /// Default: true
469    #[serde(default = "default_true")]
470    pub enable_simd_hash_operations: bool,
471}
472
473impl Default for FeatureFlagsConfig {
474    fn default() -> Self {
475        Self {
476            enable_experimental_optimizations: false,
477            enable_bounds_check_optimizations: true,
478            enable_reference_checks: false,
479            enable_aggressive_caching: true,
480            enable_batch_tx_id_computation: true,
481            enable_simd_hash_operations: true,
482        }
483    }
484}
485
486/// Advanced configuration options
487///
488/// Advanced settings for power users and specific use cases.
489/// These options provide fine-grained control over behavior.
490#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
491pub struct AdvancedConfig {
492    /// Custom checkpoint heights (additional to assume-valid)
493    /// Format: comma-separated list of block heights
494    /// Example: "100000,200000,300000"
495    /// Default: empty (no custom checkpoints)
496    #[serde(default)]
497    pub custom_checkpoints: Vec<u64>,
498
499    /// Maximum depth for chain reorganization (safety limit)
500    /// Prevents extremely deep reorganizations that could be DoS attacks
501    /// Default: 100 blocks
502    #[serde(default = "default_max_reorg_depth")]
503    pub max_reorg_depth: u64,
504
505    /// Enable strict mode (reject any non-standard transactions)
506    /// Default: false (accept standard transactions)
507    #[serde(default = "default_false")]
508    pub strict_mode: bool,
509
510    /// Maximum block size to accept (override consensus limit for testing)
511    /// Default: 0 (use consensus limit)
512    /// WARNING: Setting this may cause consensus divergence
513    #[serde(default)]
514    pub max_block_size_override: usize,
515
516    /// Enable transaction replacement (RBF) by default
517    /// Default: true
518    #[serde(default = "default_true")]
519    pub enable_rbf: bool,
520}
521
522fn default_max_reorg_depth() -> u64 {
523    100
524}
525
526impl Default for AdvancedConfig {
527    fn default() -> Self {
528        Self {
529            custom_checkpoints: Vec::new(),
530            max_reorg_depth: 100,
531            strict_mode: false,
532            max_block_size_override: 0,
533            enable_rbf: true,
534        }
535    }
536}