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 }
47 }
48
49 pub fn mainnet() -> Self {
51 Self {
52 max_block_size: 4_000_000, max_tx_size: 1_000_000, max_script_size: 10_000, segwit_enabled: true,
56 taproot_enabled: true,
57 rbf_enabled: true,
58 min_fee_rate: 1, max_fee_rate: 1_000_000, }
61 }
62
63 pub fn testnet() -> Self {
65 Self {
66 max_block_size: 4_000_000,
67 max_tx_size: 1_000_000,
68 max_script_size: 10_000,
69 segwit_enabled: true,
70 taproot_enabled: true,
71 rbf_enabled: true,
72 min_fee_rate: 1,
73 max_fee_rate: 1_000_000,
74 }
75 }
76
77 pub fn regtest() -> Self {
79 Self {
80 max_block_size: 4_000_000,
81 max_tx_size: 1_000_000,
82 max_script_size: 10_000,
83 segwit_enabled: true,
84 taproot_enabled: true,
85 rbf_enabled: true,
86 min_fee_rate: 0, max_fee_rate: 1_000_000,
88 }
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94pub struct ProtocolValidationContext {
95 pub block_height: u64,
97 pub network_params: NetworkParameters,
99 pub validation_rules: ProtocolValidationRules,
101 pub feature_registry: FeatureRegistry,
106 pub median_time_past: u64,
111 pub network_time: u64,
116 pub context_data: HashMap<String, String>,
118}
119
120impl ProtocolValidationContext {
121 pub fn new(version: ProtocolVersion, block_height: u64) -> Result<Self> {
123 let network_params = NetworkParameters::for_version(version)?;
124 let validation_rules = ProtocolValidationRules::for_protocol(version);
125
126 let feature_registry = FeatureRegistry::for_protocol(version);
127 Ok(Self {
128 block_height,
129 network_params,
130 validation_rules,
131 feature_registry,
132 median_time_past: 0,
134 network_time: 0,
135 context_data: HashMap::new(),
136 })
137 }
138
139 pub fn is_feature_enabled(&self, feature: &str) -> bool {
144 self.feature_registry
145 .is_feature_active(feature, self.block_height, self.network_time)
146 }
147
148 pub fn get_max_size(&self, component: &str) -> u32 {
150 match component {
151 "block" => self.validation_rules.max_block_size,
152 "transaction" => self.validation_rules.max_tx_size,
153 "script" => self.validation_rules.max_script_size,
154 _ => 0,
155 }
156 }
157}
158
159impl BitcoinProtocolEngine {
160 pub fn validate_block_with_protocol(
165 &self,
166 block: &Block,
167 _utxos: &UtxoSet,
168 _height: u64,
169 context: &ProtocolValidationContext,
170 ) -> Result<ValidationResult> {
171 self.apply_protocol_validation(block, context)?;
172 Ok(ValidationResult::Valid)
173 }
174
175 pub fn validate_block_with_protocol_and_witnesses(
179 &self,
180 block: &Block,
181 witnesses: &[Vec<blvm_consensus::segwit::Witness>],
182 _utxos: &UtxoSet,
183 _height: u64,
184 context: &ProtocolValidationContext,
185 ) -> Result<ValidationResult> {
186 self.apply_protocol_validation_with_witnesses(block, Some(witnesses), context)?;
187 Ok(ValidationResult::Valid)
188 }
189
190 pub fn validate_transaction_with_protocol(
192 &self,
193 tx: &Transaction,
194 context: &ProtocolValidationContext,
195 ) -> Result<ValidationResult> {
196 let consensus_result = self.consensus.validate_transaction(tx)?;
198
199 self.apply_transaction_protocol_validation(tx, context)?;
201
202 Ok(consensus_result)
203 }
204
205 fn apply_protocol_validation(
213 &self,
214 block: &Block,
215 context: &ProtocolValidationContext,
216 ) -> Result<()> {
217 self.apply_protocol_validation_with_witnesses(block, None, context)
218 }
219
220 fn apply_protocol_validation_with_witnesses(
228 &self,
229 block: &Block,
230 witnesses: Option<&[Vec<blvm_consensus::segwit::Witness>]>,
231 context: &ProtocolValidationContext,
232 ) -> Result<()> {
233 let stripped_size = self.calculate_block_size(block);
235 let weight = if let Some(wit) = witnesses {
236 let witness_bytes: u64 = wit
238 .iter()
239 .flat_map(|tx_wit| tx_wit.iter())
240 .map(|w| w.len() as u64)
241 .sum();
242 stripped_size as u64 * 4 + witness_bytes
243 } else {
244 stripped_size as u64 * 4
245 };
246 if weight > context.validation_rules.max_block_size as u64 {
247 return Err(ProtocolError::Validation(
248 format!(
249 "Block weight exceeds maximum: {} WU (max {} WU)",
250 weight, context.validation_rules.max_block_size
251 )
252 .into(),
253 ));
254 }
255
256 if block.transactions.len() > 10000 {
257 return Err(ProtocolError::Validation(
258 "Too many transactions in block (max 10000)".into(),
259 ));
260 }
261
262 for tx in &block.transactions {
263 self.apply_transaction_protocol_validation(tx, context)?;
264 }
265
266 Ok(())
267 }
268
269 fn apply_transaction_protocol_validation(
271 &self,
272 tx: &Transaction,
273 context: &ProtocolValidationContext,
274 ) -> Result<()> {
275 let tx_size = self.calculate_transaction_size(tx);
277 if tx_size > context.validation_rules.max_tx_size {
278 return Err(ProtocolError::Validation(
279 format!(
280 "Transaction size exceeds maximum: {} bytes (max {} bytes)",
281 tx_size, context.validation_rules.max_tx_size
282 )
283 .into(),
284 ));
285 }
286
287 for input in &tx.inputs {
289 if input.script_sig.len() > context.validation_rules.max_script_size as usize {
290 return Err(ProtocolError::Validation(
291 format!(
292 "Script size exceeds maximum: {} bytes (max {} bytes)",
293 input.script_sig.len(),
294 context.validation_rules.max_script_size
295 )
296 .into(),
297 ));
298 }
299 }
300
301 for output in &tx.outputs {
302 if output.script_pubkey.len() > context.validation_rules.max_script_size as usize {
303 return Err(ProtocolError::Validation(
304 format!(
305 "Script size exceeds maximum: {} bytes (max {} bytes)",
306 output.script_pubkey.len(),
307 context.validation_rules.max_script_size
308 )
309 .into(),
310 ));
311 }
312 }
313
314 Ok(())
315 }
316
317 fn calculate_block_size(&self, block: &Block) -> u32 {
319 let header_size = 80; let tx_count_size = 4; let tx_sizes: u32 = block
324 .transactions
325 .iter()
326 .map(|tx| self.calculate_transaction_size(tx))
327 .sum();
328
329 header_size + tx_count_size + tx_sizes
330 }
331
332 fn calculate_transaction_size(&self, tx: &Transaction) -> u32 {
334 blvm_consensus::transaction::calculate_transaction_size(tx) as u32
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345 use blvm_consensus::types::{OutPoint, TransactionInput, TransactionOutput};
346 use blvm_consensus::{Block, BlockHeader, Transaction};
347
348 #[test]
349 fn test_validation_rules() {
350 let mainnet_rules = ProtocolValidationRules::mainnet();
351 assert_eq!(mainnet_rules.max_block_size, 4_000_000);
352 assert!(mainnet_rules.segwit_enabled);
353 assert!(mainnet_rules.taproot_enabled);
354
355 let regtest_rules = ProtocolValidationRules::regtest();
356 assert_eq!(regtest_rules.max_block_size, 4_000_000);
357 assert!(regtest_rules.segwit_enabled);
358 assert_eq!(regtest_rules.min_fee_rate, 0); }
360
361 #[test]
362 fn test_validation_rules_all_protocols() {
363 let mainnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::BitcoinV1);
364 let testnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Testnet3);
365 let regtest_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Regtest);
366
367 assert_eq!(mainnet_rules.max_block_size, testnet_rules.max_block_size);
369 assert_eq!(mainnet_rules.max_tx_size, testnet_rules.max_tx_size);
370 assert_eq!(mainnet_rules.max_script_size, testnet_rules.max_script_size);
371 assert_eq!(mainnet_rules.segwit_enabled, testnet_rules.segwit_enabled);
372 assert_eq!(mainnet_rules.taproot_enabled, testnet_rules.taproot_enabled);
373 assert_eq!(mainnet_rules.rbf_enabled, testnet_rules.rbf_enabled);
374 assert_eq!(mainnet_rules.min_fee_rate, testnet_rules.min_fee_rate);
375 assert_eq!(mainnet_rules.max_fee_rate, testnet_rules.max_fee_rate);
376
377 assert_eq!(regtest_rules.min_fee_rate, 0);
379 assert_eq!(regtest_rules.max_fee_rate, mainnet_rules.max_fee_rate);
380 }
381
382 #[test]
383 fn test_validation_rules_serialization() {
384 let mainnet_rules = ProtocolValidationRules::mainnet();
385 let json = serde_json::to_string(&mainnet_rules).unwrap();
386 let deserialized: ProtocolValidationRules = serde_json::from_str(&json).unwrap();
387
388 assert_eq!(mainnet_rules.max_block_size, deserialized.max_block_size);
389 assert_eq!(mainnet_rules.max_tx_size, deserialized.max_tx_size);
390 assert_eq!(mainnet_rules.max_script_size, deserialized.max_script_size);
391 assert_eq!(mainnet_rules.segwit_enabled, deserialized.segwit_enabled);
392 assert_eq!(mainnet_rules.taproot_enabled, deserialized.taproot_enabled);
393 assert_eq!(mainnet_rules.rbf_enabled, deserialized.rbf_enabled);
394 assert_eq!(mainnet_rules.min_fee_rate, deserialized.min_fee_rate);
395 assert_eq!(mainnet_rules.max_fee_rate, deserialized.max_fee_rate);
396 }
397
398 #[test]
399 fn test_validation_rules_equality() {
400 let mainnet1 = ProtocolValidationRules::mainnet();
401 let mainnet2 = ProtocolValidationRules::mainnet();
402 let testnet = ProtocolValidationRules::testnet();
403
404 assert_eq!(mainnet1, mainnet2);
405 assert_eq!(mainnet1, testnet); }
407
408 #[test]
409 fn test_validation_context() {
410 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
412 assert_eq!(context.block_height, 800_000);
413 assert!(context.is_feature_enabled("segwit"));
414 assert!(!context.is_feature_enabled("nonexistent"));
415 assert_eq!(context.get_max_size("block"), 4_000_000);
416 }
417
418 #[test]
419 fn test_validation_context_all_protocols() {
420 let mainnet_context =
425 ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
426 let testnet_context =
427 ProtocolValidationContext::new(ProtocolVersion::Testnet3, 2_100_000).unwrap();
428 let regtest_context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
429
430 assert_eq!(mainnet_context.block_height, 800_000);
431 assert_eq!(testnet_context.block_height, 2_100_000);
432 assert_eq!(regtest_context.block_height, 1);
433
434 assert!(mainnet_context.is_feature_enabled("segwit"));
436 assert!(testnet_context.is_feature_enabled("segwit"));
437 assert!(regtest_context.is_feature_enabled("segwit"));
438
439 assert!(mainnet_context.is_feature_enabled("taproot"));
440 assert!(testnet_context.is_feature_enabled("taproot"));
441 assert!(regtest_context.is_feature_enabled("taproot"));
442
443 assert!(mainnet_context.is_feature_enabled("rbf"));
444 assert!(testnet_context.is_feature_enabled("rbf"));
445 assert!(regtest_context.is_feature_enabled("rbf"));
446 }
447
448 #[test]
449 fn test_validation_context_feature_queries() {
450 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
452
453 assert!(context.is_feature_enabled("segwit"));
455 assert!(context.is_feature_enabled("taproot"));
456 assert!(context.is_feature_enabled("rbf"));
457
458 assert!(!context.is_feature_enabled("nonexistent"));
460 assert!(!context.is_feature_enabled(""));
461 assert!(!context.is_feature_enabled("fast_mining"));
462 }
463
464 #[test]
465 fn test_validation_context_size_queries() {
466 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
467
468 assert_eq!(context.get_max_size("block"), 4_000_000);
469 assert_eq!(context.get_max_size("transaction"), 1_000_000);
470 assert_eq!(context.get_max_size("script"), 10_000);
471
472 assert_eq!(context.get_max_size("unknown"), 0);
474 }
475
476 #[test]
477 fn test_validation_context_serialization() {
478 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
479 let json = serde_json::to_string(&context).unwrap();
480 let deserialized: ProtocolValidationContext = serde_json::from_str(&json).unwrap();
481
482 assert_eq!(context.block_height, deserialized.block_height);
483 assert_eq!(
484 context.network_params.network_name,
485 deserialized.network_params.network_name
486 );
487 assert_eq!(
488 context.validation_rules.max_block_size,
489 deserialized.validation_rules.max_block_size
490 );
491 }
492
493 #[test]
494 fn test_validation_context_equality() {
495 let context1 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
496 let context2 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
497 let context3 = ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
498
499 assert_eq!(context1, context2);
500 assert_ne!(context1, context3); }
502
503 #[test]
504 fn test_block_size_validation() {
505 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
506 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
507
508 let coinbase_tx = Transaction {
510 version: 1,
511 inputs: blvm_consensus::tx_inputs![TransactionInput {
512 prevout: OutPoint {
513 hash: [0u8; 32],
514 index: 0xffffffff,
515 },
516 script_sig: vec![0x01, 0x00], sequence: 0xffffffff,
518 }],
519 outputs: blvm_consensus::tx_outputs![TransactionOutput {
520 value: 50_0000_0000,
521 script_pubkey: vec![blvm_consensus::opcodes::OP_1],
522 }],
523 lock_time: 0,
524 };
525
526 let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
528 .expect("Should calculate merkle root");
529
530 let small_block = Block {
531 header: BlockHeader {
532 version: 1,
533 prev_block_hash: [0u8; 32],
534 merkle_root,
535 timestamp: 1231006505,
536 bits: 0x1d00ffff,
537 nonce: 0,
538 },
539 transactions: vec![coinbase_tx].into_boxed_slice(),
540 };
541
542 let result =
544 engine.validate_block_with_protocol(&small_block, &UtxoSet::default(), 1000, &context);
545 assert!(result.is_ok());
546 }
547
548 #[test]
549 fn test_transaction_size_validation() {
550 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
551 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
552
553 let small_tx = Transaction {
555 version: 1,
556 inputs: vec![TransactionInput {
557 prevout: OutPoint {
558 hash: [0u8; 32],
559 index: 0,
560 },
561 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
562 sequence: 0xffffffff,
563 }]
564 .into(),
565 outputs: vec![TransactionOutput {
566 value: 50_0000_0000,
567 script_pubkey: vec![
568 blvm_consensus::opcodes::OP_DUP,
569 blvm_consensus::opcodes::OP_HASH160,
570 blvm_consensus::opcodes::PUSH_20_BYTES,
571 0x00,
572 0x00,
573 0x00,
574 0x00,
575 0x00,
576 0x00,
577 0x00,
578 0x00,
579 0x00,
580 0x00,
581 0x00,
582 0x00,
583 0x00,
584 0x00,
585 0x00,
586 0x00,
587 0x00,
588 0x00,
589 0x00,
590 0x00,
591 blvm_consensus::opcodes::OP_EQUALVERIFY,
592 blvm_consensus::opcodes::OP_CHECKSIG,
593 ],
594 }]
595 .into(),
596 lock_time: 0,
597 };
598
599 let result = engine.validate_transaction_with_protocol(&small_tx, &context);
601 assert!(result.is_ok());
602 }
603
604 #[test]
605 fn test_script_size_validation() {
606 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
607 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
608
609 let tx = Transaction {
611 version: 1,
612 inputs: vec![TransactionInput {
613 prevout: OutPoint {
614 hash: [0u8; 32],
615 index: 0,
616 },
617 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
618 sequence: 0xffffffff,
619 }]
620 .into(),
621 outputs: vec![TransactionOutput {
622 value: 50_0000_0000,
623 script_pubkey: vec![
624 blvm_consensus::opcodes::OP_DUP,
625 blvm_consensus::opcodes::OP_HASH160,
626 blvm_consensus::opcodes::PUSH_20_BYTES,
627 0x00,
628 0x00,
629 0x00,
630 0x00,
631 0x00,
632 0x00,
633 0x00,
634 0x00,
635 0x00,
636 0x00,
637 0x00,
638 0x00,
639 0x00,
640 0x00,
641 0x00,
642 0x00,
643 0x00,
644 0x00,
645 0x00,
646 0x00,
647 blvm_consensus::opcodes::OP_EQUALVERIFY,
648 blvm_consensus::opcodes::OP_CHECKSIG,
649 ],
650 }]
651 .into(),
652 lock_time: 0,
653 };
654
655 let result = engine.validate_transaction_with_protocol(&tx, &context);
657 assert!(result.is_ok());
658 }
659
660 #[test]
661 fn test_validation_context_data() {
662 let mut context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
663
664 context
666 .context_data
667 .insert("test_key".to_string(), "test_value".to_string());
668
669 assert_eq!(
670 context.context_data.get("test_key"),
671 Some(&"test_value".to_string())
672 );
673 assert_eq!(context.context_data.get("nonexistent"), None);
674 }
675
676 #[test]
677 fn test_validation_rules_boundary_values() {
678 let rules = ProtocolValidationRules::mainnet();
679
680 assert!(rules.max_block_size > 0);
682 assert!(rules.max_tx_size > 0);
683 assert!(rules.max_script_size > 0);
684 assert!(rules.max_fee_rate > rules.min_fee_rate);
685
686 assert!(rules.max_block_size <= 10_000_000); assert!(rules.max_tx_size <= 5_000_000); assert!(rules.max_script_size <= 50_000); }
691}