1use crate::error::ProtocolError;
8use crate::features::FeatureRegistry;
9use crate::{BitcoinProtocolEngine, NetworkParameters, ProtocolVersion};
10use crate::{Block, Transaction, ValidationResult};
11use blvm_consensus::types::UtxoSet;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14
15type Result<T> = std::result::Result<T, ProtocolError>;
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct ProtocolValidationRules {
21 pub max_block_size: u32,
23 pub max_tx_size: u32,
25 pub max_script_size: u32,
27 pub segwit_enabled: bool,
29 pub taproot_enabled: bool,
31 pub rbf_enabled: bool,
33 pub min_fee_rate: u64,
35 pub max_fee_rate: u64,
37}
38
39impl ProtocolValidationRules {
40 pub fn for_protocol(version: ProtocolVersion) -> Self {
42 match version {
43 ProtocolVersion::BitcoinV1 => Self::mainnet(),
44 ProtocolVersion::Testnet3 => Self::testnet(),
45 ProtocolVersion::Regtest => Self::regtest(),
46 ProtocolVersion::Signet => Self::signet(),
47 }
48 }
49
50 pub fn mainnet() -> Self {
52 Self {
53 max_block_size: 4_000_000, max_tx_size: 1_000_000, max_script_size: 10_000, segwit_enabled: true,
57 taproot_enabled: true,
58 rbf_enabled: true,
59 min_fee_rate: 1, max_fee_rate: 1_000_000, }
62 }
63
64 pub fn testnet() -> Self {
66 Self {
67 max_block_size: 4_000_000,
68 max_tx_size: 1_000_000,
69 max_script_size: 10_000,
70 segwit_enabled: true,
71 taproot_enabled: true,
72 rbf_enabled: true,
73 min_fee_rate: 1,
74 max_fee_rate: 1_000_000,
75 }
76 }
77
78 pub fn regtest() -> Self {
80 Self {
81 max_block_size: 4_000_000,
82 max_tx_size: 1_000_000,
83 max_script_size: 10_000,
84 segwit_enabled: true,
85 taproot_enabled: true,
86 rbf_enabled: true,
87 min_fee_rate: 0, max_fee_rate: 1_000_000,
89 }
90 }
91
92 pub fn signet() -> Self {
94 Self {
95 max_block_size: 4_000_000,
96 max_tx_size: 1_000_000,
97 max_script_size: 10_000,
98 segwit_enabled: true,
99 taproot_enabled: true,
100 rbf_enabled: true,
101 min_fee_rate: 1,
102 max_fee_rate: 1_000_000,
103 }
104 }
105}
106
107#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub struct ProtocolValidationContext {
110 pub block_height: u64,
112 pub network_params: NetworkParameters,
114 pub validation_rules: ProtocolValidationRules,
116 pub feature_registry: FeatureRegistry,
121 pub median_time_past: u64,
126 pub network_time: u64,
131 pub context_data: HashMap<String, String>,
133}
134
135impl ProtocolValidationContext {
136 pub fn new(version: ProtocolVersion, block_height: u64) -> Result<Self> {
138 let network_params = NetworkParameters::for_version(version)?;
139 let validation_rules = ProtocolValidationRules::for_protocol(version);
140
141 let feature_registry = FeatureRegistry::for_protocol(version);
142 Ok(Self {
143 block_height,
144 network_params,
145 validation_rules,
146 feature_registry,
147 median_time_past: 0,
149 network_time: 0,
150 context_data: HashMap::new(),
151 })
152 }
153
154 pub fn is_feature_enabled(&self, feature: &str) -> bool {
159 self.feature_registry
160 .is_feature_active(feature, self.block_height, self.network_time)
161 }
162
163 pub fn get_max_size(&self, component: &str) -> u32 {
165 match component {
166 "block" => self.validation_rules.max_block_size,
167 "transaction" => self.validation_rules.max_tx_size,
168 "script" => self.validation_rules.max_script_size,
169 _ => 0,
170 }
171 }
172}
173
174impl BitcoinProtocolEngine {
175 pub fn validate_block_with_protocol(
180 &self,
181 block: &Block,
182 _utxos: &UtxoSet,
183 _height: u64,
184 context: &ProtocolValidationContext,
185 ) -> Result<ValidationResult> {
186 self.apply_protocol_validation(block, context)?;
187 Ok(ValidationResult::Valid)
188 }
189
190 pub fn validate_block_with_protocol_and_witnesses(
194 &self,
195 block: &Block,
196 witnesses: &[Vec<blvm_consensus::segwit::Witness>],
197 _utxos: &UtxoSet,
198 _height: u64,
199 context: &ProtocolValidationContext,
200 ) -> Result<ValidationResult> {
201 self.apply_protocol_validation_with_witnesses(block, Some(witnesses), context)?;
202 Ok(ValidationResult::Valid)
203 }
204
205 pub fn validate_transaction_with_protocol(
207 &self,
208 tx: &Transaction,
209 context: &ProtocolValidationContext,
210 ) -> Result<ValidationResult> {
211 let consensus_result = self.consensus.validate_transaction(tx)?;
213
214 self.apply_transaction_protocol_validation(tx, context)?;
216
217 Ok(consensus_result)
218 }
219
220 fn apply_protocol_validation(
228 &self,
229 block: &Block,
230 context: &ProtocolValidationContext,
231 ) -> Result<()> {
232 self.apply_protocol_validation_with_witnesses(block, None, context)
233 }
234
235 fn apply_protocol_validation_with_witnesses(
243 &self,
244 block: &Block,
245 witnesses: Option<&[Vec<blvm_consensus::segwit::Witness>]>,
246 context: &ProtocolValidationContext,
247 ) -> Result<()> {
248 let stripped_size = self.calculate_block_size(block);
250 let weight = if let Some(wit) = witnesses {
251 let witness_bytes: u64 = wit
253 .iter()
254 .flat_map(|tx_wit| tx_wit.iter())
255 .map(|w| w.len() as u64)
256 .sum();
257 stripped_size as u64 * 4 + witness_bytes
258 } else {
259 stripped_size as u64 * 4
260 };
261 if weight > context.validation_rules.max_block_size as u64 {
262 return Err(ProtocolError::Validation(
263 format!(
264 "Block weight exceeds maximum: {} WU (max {} WU)",
265 weight, context.validation_rules.max_block_size
266 )
267 .into(),
268 ));
269 }
270
271 if block.transactions.len() > 10000 {
272 return Err(ProtocolError::Validation(
273 "Too many transactions in block (max 10000)".into(),
274 ));
275 }
276
277 for tx in &block.transactions {
278 self.apply_transaction_protocol_validation(tx, context)?;
279 }
280
281 Ok(())
282 }
283
284 fn apply_transaction_protocol_validation(
286 &self,
287 tx: &Transaction,
288 context: &ProtocolValidationContext,
289 ) -> Result<()> {
290 let tx_size = self.calculate_transaction_size(tx);
292 if tx_size > context.validation_rules.max_tx_size {
293 return Err(ProtocolError::Validation(
294 format!(
295 "Transaction size exceeds maximum: {} bytes (max {} bytes)",
296 tx_size, context.validation_rules.max_tx_size
297 )
298 .into(),
299 ));
300 }
301
302 for input in &tx.inputs {
304 if input.script_sig.len() > context.validation_rules.max_script_size as usize {
305 return Err(ProtocolError::Validation(
306 format!(
307 "Script size exceeds maximum: {} bytes (max {} bytes)",
308 input.script_sig.len(),
309 context.validation_rules.max_script_size
310 )
311 .into(),
312 ));
313 }
314 }
315
316 for output in &tx.outputs {
317 if output.script_pubkey.len() > context.validation_rules.max_script_size as usize {
318 return Err(ProtocolError::Validation(
319 format!(
320 "Script size exceeds maximum: {} bytes (max {} bytes)",
321 output.script_pubkey.len(),
322 context.validation_rules.max_script_size
323 )
324 .into(),
325 ));
326 }
327 }
328
329 Ok(())
330 }
331
332 fn calculate_block_size(&self, block: &Block) -> u32 {
334 let header_size = 80; let tx_count_size = 4; let tx_sizes: u32 = block
339 .transactions
340 .iter()
341 .map(|tx| self.calculate_transaction_size(tx))
342 .sum();
343
344 header_size + tx_count_size + tx_sizes
345 }
346
347 fn calculate_transaction_size(&self, tx: &Transaction) -> u32 {
349 blvm_consensus::transaction::calculate_transaction_size(tx) as u32
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use blvm_consensus::types::{OutPoint, TransactionInput, TransactionOutput};
361 use blvm_consensus::{Block, BlockHeader, Transaction};
362
363 #[test]
364 fn test_validation_rules() {
365 let mainnet_rules = ProtocolValidationRules::mainnet();
366 assert_eq!(mainnet_rules.max_block_size, 4_000_000);
367 assert!(mainnet_rules.segwit_enabled);
368 assert!(mainnet_rules.taproot_enabled);
369
370 let regtest_rules = ProtocolValidationRules::regtest();
371 assert_eq!(regtest_rules.max_block_size, 4_000_000);
372 assert!(regtest_rules.segwit_enabled);
373 assert_eq!(regtest_rules.min_fee_rate, 0); }
375
376 #[test]
377 fn test_validation_rules_all_protocols() {
378 let mainnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::BitcoinV1);
379 let testnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Testnet3);
380 let regtest_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Regtest);
381
382 assert_eq!(mainnet_rules.max_block_size, testnet_rules.max_block_size);
384 assert_eq!(mainnet_rules.max_tx_size, testnet_rules.max_tx_size);
385 assert_eq!(mainnet_rules.max_script_size, testnet_rules.max_script_size);
386 assert_eq!(mainnet_rules.segwit_enabled, testnet_rules.segwit_enabled);
387 assert_eq!(mainnet_rules.taproot_enabled, testnet_rules.taproot_enabled);
388 assert_eq!(mainnet_rules.rbf_enabled, testnet_rules.rbf_enabled);
389 assert_eq!(mainnet_rules.min_fee_rate, testnet_rules.min_fee_rate);
390 assert_eq!(mainnet_rules.max_fee_rate, testnet_rules.max_fee_rate);
391
392 assert_eq!(regtest_rules.min_fee_rate, 0);
394 assert_eq!(regtest_rules.max_fee_rate, mainnet_rules.max_fee_rate);
395 }
396
397 #[test]
398 fn test_validation_rules_serialization() {
399 let mainnet_rules = ProtocolValidationRules::mainnet();
400 let json = serde_json::to_string(&mainnet_rules).unwrap();
401 let deserialized: ProtocolValidationRules = serde_json::from_str(&json).unwrap();
402
403 assert_eq!(mainnet_rules.max_block_size, deserialized.max_block_size);
404 assert_eq!(mainnet_rules.max_tx_size, deserialized.max_tx_size);
405 assert_eq!(mainnet_rules.max_script_size, deserialized.max_script_size);
406 assert_eq!(mainnet_rules.segwit_enabled, deserialized.segwit_enabled);
407 assert_eq!(mainnet_rules.taproot_enabled, deserialized.taproot_enabled);
408 assert_eq!(mainnet_rules.rbf_enabled, deserialized.rbf_enabled);
409 assert_eq!(mainnet_rules.min_fee_rate, deserialized.min_fee_rate);
410 assert_eq!(mainnet_rules.max_fee_rate, deserialized.max_fee_rate);
411 }
412
413 #[test]
414 fn test_validation_rules_equality() {
415 let mainnet1 = ProtocolValidationRules::mainnet();
416 let mainnet2 = ProtocolValidationRules::mainnet();
417 let testnet = ProtocolValidationRules::testnet();
418
419 assert_eq!(mainnet1, mainnet2);
420 assert_eq!(mainnet1, testnet); }
422
423 #[test]
424 fn test_validation_context() {
425 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
427 assert_eq!(context.block_height, 800_000);
428 assert!(context.is_feature_enabled("segwit"));
429 assert!(!context.is_feature_enabled("nonexistent"));
430 assert_eq!(context.get_max_size("block"), 4_000_000);
431 }
432
433 #[test]
434 fn test_validation_context_all_protocols() {
435 let mainnet_context =
440 ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
441 let testnet_context =
442 ProtocolValidationContext::new(ProtocolVersion::Testnet3, 2_100_000).unwrap();
443 let regtest_context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
444
445 assert_eq!(mainnet_context.block_height, 800_000);
446 assert_eq!(testnet_context.block_height, 2_100_000);
447 assert_eq!(regtest_context.block_height, 1);
448
449 assert!(mainnet_context.is_feature_enabled("segwit"));
451 assert!(testnet_context.is_feature_enabled("segwit"));
452 assert!(regtest_context.is_feature_enabled("segwit"));
453
454 assert!(mainnet_context.is_feature_enabled("taproot"));
455 assert!(testnet_context.is_feature_enabled("taproot"));
456 assert!(regtest_context.is_feature_enabled("taproot"));
457
458 assert!(mainnet_context.is_feature_enabled("rbf"));
459 assert!(testnet_context.is_feature_enabled("rbf"));
460 assert!(regtest_context.is_feature_enabled("rbf"));
461 }
462
463 #[test]
464 fn test_validation_context_feature_queries() {
465 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
467
468 assert!(context.is_feature_enabled("segwit"));
470 assert!(context.is_feature_enabled("taproot"));
471 assert!(context.is_feature_enabled("rbf"));
472
473 assert!(!context.is_feature_enabled("nonexistent"));
475 assert!(!context.is_feature_enabled(""));
476 assert!(!context.is_feature_enabled("fast_mining"));
477 }
478
479 #[test]
480 fn test_validation_context_size_queries() {
481 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
482
483 assert_eq!(context.get_max_size("block"), 4_000_000);
484 assert_eq!(context.get_max_size("transaction"), 1_000_000);
485 assert_eq!(context.get_max_size("script"), 10_000);
486
487 assert_eq!(context.get_max_size("unknown"), 0);
489 }
490
491 #[test]
492 fn test_validation_context_serialization() {
493 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
494 let json = serde_json::to_string(&context).unwrap();
495 let deserialized: ProtocolValidationContext = serde_json::from_str(&json).unwrap();
496
497 assert_eq!(context.block_height, deserialized.block_height);
498 assert_eq!(
499 context.network_params.network_name,
500 deserialized.network_params.network_name
501 );
502 assert_eq!(
503 context.validation_rules.max_block_size,
504 deserialized.validation_rules.max_block_size
505 );
506 }
507
508 #[test]
509 fn test_validation_context_equality() {
510 let context1 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
511 let context2 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
512 let context3 = ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
513
514 assert_eq!(context1, context2);
515 assert_ne!(context1, context3); }
517
518 #[test]
519 fn test_block_size_validation() {
520 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
521 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
522
523 let coinbase_tx = Transaction {
525 version: 1,
526 inputs: blvm_consensus::tx_inputs![TransactionInput {
527 prevout: OutPoint {
528 hash: [0u8; 32],
529 index: 0xffffffff,
530 },
531 script_sig: vec![0x01, 0x00], sequence: 0xffffffff,
533 }],
534 outputs: blvm_consensus::tx_outputs![TransactionOutput {
535 value: 50_0000_0000,
536 script_pubkey: vec![blvm_consensus::opcodes::OP_1],
537 }],
538 lock_time: 0,
539 };
540
541 let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
543 .expect("Should calculate merkle root");
544
545 let small_block = Block {
546 header: BlockHeader {
547 version: 1,
548 prev_block_hash: [0u8; 32],
549 merkle_root,
550 timestamp: 1231006505,
551 bits: 0x1d00ffff,
552 nonce: 0,
553 },
554 transactions: vec![coinbase_tx].into_boxed_slice(),
555 };
556
557 let result =
559 engine.validate_block_with_protocol(&small_block, &UtxoSet::default(), 1000, &context);
560 assert!(result.is_ok());
561 }
562
563 #[test]
564 fn test_transaction_size_validation() {
565 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
566 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
567
568 let small_tx = Transaction {
570 version: 1,
571 inputs: vec![TransactionInput {
572 prevout: OutPoint {
573 hash: [0u8; 32],
574 index: 0,
575 },
576 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
577 sequence: 0xffffffff,
578 }]
579 .into(),
580 outputs: vec![TransactionOutput {
581 value: 50_0000_0000,
582 script_pubkey: vec![
583 blvm_consensus::opcodes::OP_DUP,
584 blvm_consensus::opcodes::OP_HASH160,
585 blvm_consensus::opcodes::PUSH_20_BYTES,
586 0x00,
587 0x00,
588 0x00,
589 0x00,
590 0x00,
591 0x00,
592 0x00,
593 0x00,
594 0x00,
595 0x00,
596 0x00,
597 0x00,
598 0x00,
599 0x00,
600 0x00,
601 0x00,
602 0x00,
603 0x00,
604 0x00,
605 0x00,
606 blvm_consensus::opcodes::OP_EQUALVERIFY,
607 blvm_consensus::opcodes::OP_CHECKSIG,
608 ],
609 }]
610 .into(),
611 lock_time: 0,
612 };
613
614 let result = engine.validate_transaction_with_protocol(&small_tx, &context);
616 assert!(result.is_ok());
617 }
618
619 #[test]
620 fn test_script_size_validation() {
621 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
622 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
623
624 let tx = Transaction {
626 version: 1,
627 inputs: vec![TransactionInput {
628 prevout: OutPoint {
629 hash: [0u8; 32],
630 index: 0,
631 },
632 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
633 sequence: 0xffffffff,
634 }]
635 .into(),
636 outputs: vec![TransactionOutput {
637 value: 50_0000_0000,
638 script_pubkey: vec![
639 blvm_consensus::opcodes::OP_DUP,
640 blvm_consensus::opcodes::OP_HASH160,
641 blvm_consensus::opcodes::PUSH_20_BYTES,
642 0x00,
643 0x00,
644 0x00,
645 0x00,
646 0x00,
647 0x00,
648 0x00,
649 0x00,
650 0x00,
651 0x00,
652 0x00,
653 0x00,
654 0x00,
655 0x00,
656 0x00,
657 0x00,
658 0x00,
659 0x00,
660 0x00,
661 0x00,
662 blvm_consensus::opcodes::OP_EQUALVERIFY,
663 blvm_consensus::opcodes::OP_CHECKSIG,
664 ],
665 }]
666 .into(),
667 lock_time: 0,
668 };
669
670 let result = engine.validate_transaction_with_protocol(&tx, &context);
672 assert!(result.is_ok());
673 }
674
675 #[test]
676 fn test_validation_context_data() {
677 let mut context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
678
679 context
681 .context_data
682 .insert("test_key".to_string(), "test_value".to_string());
683
684 assert_eq!(
685 context.context_data.get("test_key"),
686 Some(&"test_value".to_string())
687 );
688 assert_eq!(context.context_data.get("nonexistent"), None);
689 }
690
691 #[test]
692 fn test_validation_rules_boundary_values() {
693 let rules = ProtocolValidationRules::mainnet();
694
695 assert!(rules.max_block_size > 0);
697 assert!(rules.max_tx_size > 0);
698 assert!(rules.max_script_size > 0);
699 assert!(rules.max_fee_rate > rules.min_fee_rate);
700
701 assert!(rules.max_block_size <= 10_000_000); assert!(rules.max_tx_size <= 5_000_000); assert!(rules.max_script_size <= 50_000); }
706}