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}