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}