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}