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 UnexecIf (OP_FALSE OP_IF) scripts
177    /// Default: true
178    #[serde(
179        default = "default_reject_unexec_if",
180        alias = "reject_envelope_protocol"
181    )]
182    pub reject_unexec_if: bool,
183
184    /// Reject spam transactions at mempool entry (opt-in)
185    /// Default: false (spam filtering is opt-in for mempool)
186    ///
187    /// **Admission:** enforced in `blvm-node` by `MempoolPolicyConfig::reject_spam_in_mempool`
188    /// in `MempoolManager::add_transaction` (uses `blvm-protocol` spam filter).
189    #[serde(default = "default_reject_spam_in_mempool")]
190    pub reject_spam_in_mempool: bool,
191
192    /// Spam filter configuration (if reject_spam_in_mempool is enabled)
193    /// Note: Prefer `blvm-node` `MempoolPolicyConfig::spam_filter` + `SpamFilterConfigSerializable`.
194    #[serde(default, skip_serializing_if = "Option::is_none")]
195    pub spam_filter_config: Option<serde_json::Value>,
196
197    /// Minimum fee rate for large transactions (satoshis per vbyte)
198    /// Transactions larger than large_tx_threshold_bytes must pay at least this fee rate
199    /// Default: 2 sat/vB (higher than standard min_relay_fee_rate)
200    #[serde(default = "default_min_fee_rate_large_tx")]
201    pub min_fee_rate_large_tx: u64,
202
203    /// Large transaction threshold (bytes)
204    /// Transactions larger than this require min_fee_rate_large_tx
205    /// Default: 1000 bytes
206    #[serde(default = "default_large_tx_threshold_bytes")]
207    pub large_tx_threshold_bytes: u64,
208}
209
210fn default_rbf_fee_increment() -> i64 {
211    1000
212}
213
214fn default_max_mempool_mb() -> u64 {
215    300
216}
217
218fn default_max_mempool_txs() -> usize {
219    100_000
220}
221
222fn default_mempool_expiry_hours() -> u64 {
223    336 // 14 days
224}
225
226fn default_min_relay_fee_rate() -> u64 {
227    1 // 1 sat/vB = 1000 sat/kB
228}
229
230fn default_min_tx_fee() -> i64 {
231    1000
232}
233
234fn default_max_op_return_size() -> u32 {
235    80
236}
237
238fn default_max_op_return_outputs() -> u32 {
239    1
240}
241
242fn default_reject_multiple_op_return() -> bool {
243    true
244}
245
246fn default_max_standard_script_size() -> u32 {
247    200
248}
249
250fn default_reject_unexec_if() -> bool {
251    true
252}
253
254fn default_reject_spam_in_mempool() -> bool {
255    false
256}
257
258fn default_min_fee_rate_large_tx() -> u64 {
259    2 // 2 sat/vB (higher than standard 1 sat/vB)
260}
261
262fn default_large_tx_threshold_bytes() -> u64 {
263    1000 // 1 KB
264}
265
266impl Default for MempoolConfig {
267    fn default() -> Self {
268        Self {
269            max_mempool_mb: 300,
270            max_mempool_txs: 100_000,
271            mempool_expiry_hours: 336,
272            min_relay_fee_rate: 1,
273            min_tx_fee: 1000,
274            rbf_fee_increment: 1000,
275            max_op_return_size: 80,
276            max_op_return_outputs: 1,
277            reject_multiple_op_return: true,
278            max_standard_script_size: 200,
279            reject_unexec_if: true,
280            reject_spam_in_mempool: false,
281            spam_filter_config: None,
282            min_fee_rate_large_tx: 2,
283            large_tx_threshold_bytes: 1000,
284        }
285    }
286}
287
288fn default_false() -> bool {
289    false
290}
291
292/// UTXO Commitment configuration
293///
294/// Controls UTXO commitment set size, storage limits, and performance tuning.
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
296pub struct UtxoCommitmentConfig {
297    /// Maximum UTXO commitment set size in megabytes
298    /// This limits the in-memory size of the UTXO Merkle tree
299    /// Default: 512 MB (sufficient for ~100M UTXOs)
300    #[serde(default = "default_max_utxo_commitment_set_mb")]
301    pub max_utxo_commitment_set_mb: u64,
302
303    /// Maximum number of UTXOs in commitment set (alternative to size-based limit)
304    /// Default: 100_000_000 (100 million UTXOs)
305    #[serde(default = "default_max_utxo_count")]
306    pub max_utxo_count: u64,
307
308    /// Maximum number of historical commitments to keep in memory
309    /// Older commitments are stored on disk
310    /// Default: 1000 (keeps last ~7 days of commitments at 1 per block)
311    #[serde(default = "default_max_historical_commitments")]
312    pub max_historical_commitments: usize,
313
314    /// Enable incremental commitment updates (recommended)
315    /// Default: true
316    #[serde(default = "default_true")]
317    pub enable_incremental_updates: bool,
318}
319
320fn default_max_utxo_commitment_set_mb() -> u64 {
321    512
322}
323
324fn default_max_utxo_count() -> u64 {
325    100_000_000
326}
327
328fn default_max_historical_commitments() -> usize {
329    1000
330}
331
332impl Default for UtxoCommitmentConfig {
333    fn default() -> Self {
334        Self {
335            max_utxo_commitment_set_mb: 512,
336            max_utxo_count: 100_000_000,
337            max_historical_commitments: 1000,
338            enable_incremental_updates: true,
339        }
340    }
341}
342
343/// Performance and optimization configuration
344///
345/// Controls performance tuning, parallelization, and optimization features.
346/// These are operational parameters that affect performance but not consensus correctness.
347///
348/// IBD batch tuning: When `ibd_chunk_threshold` / `ibd_min_chunk_size` are `None`,
349/// hardware-derived values are used. When `Some(x)`, config overrides hardware.
350#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
351pub struct PerformanceConfig {
352    /// Number of threads for script verification (default: number of CPU cores)
353    /// Default: 0 (auto-detect from CPU count)
354    #[serde(default)]
355    pub script_verification_threads: usize,
356
357    /// Batch size for parallel transaction validation
358    /// Larger batches improve throughput but increase latency
359    /// Default: 8 transactions per batch
360    #[serde(default = "default_parallel_batch_size")]
361    pub parallel_batch_size: usize,
362
363    /// IBD batch: chunk threshold (parallelize when sig count exceeds this).
364    /// None = use hardware-derived; Some(x) = override.
365    #[serde(default, skip_serializing_if = "Option::is_none")]
366    pub ibd_chunk_threshold: Option<usize>,
367
368    /// IBD batch: minimum chunk size for parallel batches.
369    /// None = use hardware-derived; Some(x) = override.
370    #[serde(default, skip_serializing_if = "Option::is_none")]
371    pub ibd_min_chunk_size: Option<usize>,
372
373    /// Enable SIMD/vectorization optimizations (if available)
374    /// Default: true
375    #[serde(default = "default_true")]
376    pub enable_simd_optimizations: bool,
377
378    /// Enable cache-friendly memory layouts
379    /// Default: true
380    #[serde(default = "default_true")]
381    pub enable_cache_optimizations: bool,
382
383    /// Enable batch UTXO lookups (pre-fetch all UTXOs before validation)
384    /// Default: true
385    #[serde(default = "default_true")]
386    pub enable_batch_utxo_lookups: bool,
387}
388
389fn default_parallel_batch_size() -> usize {
390    8
391}
392
393impl Default for PerformanceConfig {
394    fn default() -> Self {
395        Self {
396            script_verification_threads: 0, // Auto-detect
397            parallel_batch_size: 8,
398            ibd_chunk_threshold: None,
399            ibd_min_chunk_size: None,
400            enable_simd_optimizations: true,
401            enable_cache_optimizations: true,
402            enable_batch_utxo_lookups: true,
403        }
404    }
405}
406
407/// Debug and development configuration
408///
409/// Controls debug assertions, runtime checks, and development features.
410/// These options are safe to enable in production but may impact performance.
411#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
412pub struct DebugConfig {
413    /// Enable runtime assertions (debug_assert! statements)
414    /// Default: false (enabled automatically in debug builds)
415    #[serde(default = "default_false")]
416    pub enable_runtime_assertions: bool,
417
418    /// Enable runtime invariant checks (additional safety checks)
419    /// Default: false
420    #[serde(default = "default_false")]
421    pub enable_runtime_invariants: bool,
422
423    /// Enable verbose logging for consensus operations
424    /// Default: false
425    #[serde(default = "default_false")]
426    pub enable_verbose_logging: bool,
427
428    /// Enable performance profiling (timing measurements)
429    /// Default: false
430    #[serde(default = "default_false")]
431    pub enable_performance_profiling: bool,
432
433    /// Log all rejected transactions/blocks (for debugging)
434    /// Default: false
435    #[serde(default = "default_false")]
436    pub log_rejections: bool,
437}
438
439/// Feature flags configuration
440///
441/// Controls optional features and experimental functionality.
442/// These are safe to enable/disable without affecting consensus.
443#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
444pub struct FeatureFlagsConfig {
445    /// Enable experimental optimizations (may be unstable)
446    /// Default: false
447    #[serde(default = "default_false")]
448    pub enable_experimental_optimizations: bool,
449
450    /// Enable bounds check optimizations (requires formal proofs)
451    /// Default: true (if production feature enabled)
452    #[serde(default = "default_true")]
453    pub enable_bounds_check_optimizations: bool,
454
455    /// Enable reference implementation checks (slower but safer)
456    /// Default: false
457    #[serde(default = "default_false")]
458    pub enable_reference_checks: bool,
459
460    /// Enable aggressive caching (may use more memory)
461    /// Default: true
462    #[serde(default = "default_true")]
463    pub enable_aggressive_caching: bool,
464
465    /// Enable batch transaction ID computation (faster but uses more memory)
466    /// Default: true
467    #[serde(default = "default_true")]
468    pub enable_batch_tx_id_computation: bool,
469
470    /// Enable SIMD hash operations (faster on supported CPUs)
471    /// Default: true
472    #[serde(default = "default_true")]
473    pub enable_simd_hash_operations: bool,
474}
475
476impl Default for FeatureFlagsConfig {
477    fn default() -> Self {
478        Self {
479            enable_experimental_optimizations: false,
480            enable_bounds_check_optimizations: true,
481            enable_reference_checks: false,
482            enable_aggressive_caching: true,
483            enable_batch_tx_id_computation: true,
484            enable_simd_hash_operations: true,
485        }
486    }
487}
488
489/// Advanced configuration options
490///
491/// Advanced settings for power users and specific use cases.
492/// These options provide fine-grained control over behavior.
493#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
494pub struct AdvancedConfig {
495    /// Custom checkpoint heights (additional to assume-valid)
496    /// Format: comma-separated list of block heights
497    /// Example: "100000,200000,300000"
498    /// Default: empty (no custom checkpoints)
499    #[serde(default)]
500    pub custom_checkpoints: Vec<u64>,
501
502    /// Maximum depth for chain reorganization (safety limit)
503    /// Prevents extremely deep reorganizations that could be DoS attacks
504    /// Default: 100 blocks
505    #[serde(default = "default_max_reorg_depth")]
506    pub max_reorg_depth: u64,
507
508    /// Enable strict mode (reject any non-standard transactions)
509    /// Default: false (accept standard transactions)
510    #[serde(default = "default_false")]
511    pub strict_mode: bool,
512
513    /// Maximum block size to accept (override consensus limit for testing)
514    /// Default: 0 (use consensus limit)
515    /// WARNING: Setting this may cause consensus divergence
516    #[serde(default)]
517    pub max_block_size_override: usize,
518
519    /// Enable transaction replacement (RBF) by default
520    /// Default: true
521    #[serde(default = "default_true")]
522    pub enable_rbf: bool,
523}
524
525fn default_max_reorg_depth() -> u64 {
526    100
527}
528
529impl Default for AdvancedConfig {
530    fn default() -> Self {
531        Self {
532            custom_checkpoints: Vec::new(),
533            max_reorg_depth: 100,
534            strict_mode: false,
535            max_block_size_override: 0,
536            enable_rbf: true,
537        }
538    }
539}