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