1use crate::error::ProtocolError;
8use crate::{BitcoinProtocolEngine, NetworkParameters, ProtocolVersion};
9use crate::{Block, Transaction, ValidationResult};
10use blvm_consensus::types::UtxoSet;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14type Result<T> = std::result::Result<T, ProtocolError>;
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct ProtocolValidationRules {
20 pub max_block_size: u32,
22 pub max_tx_size: u32,
24 pub max_script_size: u32,
26 pub segwit_enabled: bool,
28 pub taproot_enabled: bool,
30 pub rbf_enabled: bool,
32 pub min_fee_rate: u64,
34 pub max_fee_rate: u64,
36}
37
38impl ProtocolValidationRules {
39 pub fn for_protocol(version: ProtocolVersion) -> Self {
41 match version {
42 ProtocolVersion::BitcoinV1 => Self::mainnet(),
43 ProtocolVersion::Testnet3 => Self::testnet(),
44 ProtocolVersion::Regtest => Self::regtest(),
45 }
46 }
47
48 pub fn mainnet() -> Self {
50 Self {
51 max_block_size: 4_000_000, max_tx_size: 1_000_000, max_script_size: 10_000, segwit_enabled: true,
55 taproot_enabled: true,
56 rbf_enabled: true,
57 min_fee_rate: 1, max_fee_rate: 1_000_000, }
60 }
61
62 pub fn testnet() -> Self {
64 Self {
65 max_block_size: 4_000_000,
66 max_tx_size: 1_000_000,
67 max_script_size: 10_000,
68 segwit_enabled: true,
69 taproot_enabled: true,
70 rbf_enabled: true,
71 min_fee_rate: 1,
72 max_fee_rate: 1_000_000,
73 }
74 }
75
76 pub fn regtest() -> Self {
78 Self {
79 max_block_size: 4_000_000,
80 max_tx_size: 1_000_000,
81 max_script_size: 10_000,
82 segwit_enabled: true,
83 taproot_enabled: true,
84 rbf_enabled: true,
85 min_fee_rate: 0, max_fee_rate: 1_000_000,
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct ProtocolValidationContext {
94 pub block_height: u64,
96 pub network_params: NetworkParameters,
98 pub validation_rules: ProtocolValidationRules,
100 pub median_time_past: u64,
105 pub network_time: u64,
110 pub context_data: HashMap<String, String>,
112}
113
114impl ProtocolValidationContext {
115 pub fn new(version: ProtocolVersion, block_height: u64) -> Result<Self> {
117 let network_params = NetworkParameters::for_version(version)?;
118 let validation_rules = ProtocolValidationRules::for_protocol(version);
119
120 Ok(Self {
121 block_height,
122 network_params,
123 validation_rules,
124 median_time_past: 0,
126 network_time: 0,
127 context_data: HashMap::new(),
128 })
129 }
130
131 pub fn is_feature_enabled(&self, feature: &str) -> bool {
133 match feature {
134 "segwit" => self.validation_rules.segwit_enabled,
135 "taproot" => self.validation_rules.taproot_enabled,
136 "rbf" => self.validation_rules.rbf_enabled,
137 _ => false,
138 }
139 }
140
141 pub fn get_max_size(&self, component: &str) -> u32 {
143 match component {
144 "block" => self.validation_rules.max_block_size,
145 "transaction" => self.validation_rules.max_tx_size,
146 "script" => self.validation_rules.max_script_size,
147 _ => 0,
148 }
149 }
150}
151
152impl BitcoinProtocolEngine {
153 pub fn validate_block_with_protocol(
155 &self,
156 block: &Block,
157 _utxos: &UtxoSet,
158 _height: u64,
159 context: &ProtocolValidationContext,
160 ) -> Result<ValidationResult> {
161 self.apply_protocol_validation(block, context)?;
163
164 Ok(ValidationResult::Valid)
165 }
166
167 pub fn validate_transaction_with_protocol(
169 &self,
170 tx: &Transaction,
171 context: &ProtocolValidationContext,
172 ) -> Result<ValidationResult> {
173 let consensus_result = self.consensus.validate_transaction(tx)?;
175
176 self.apply_transaction_protocol_validation(tx, context)?;
178
179 Ok(consensus_result)
180 }
181
182 fn apply_protocol_validation(
184 &self,
185 block: &Block,
186 context: &ProtocolValidationContext,
187 ) -> Result<()> {
188 let block_size = self.calculate_block_size(block);
190 if block_size > context.validation_rules.max_block_size {
191 return Err(ProtocolError::Validation(
192 format!(
193 "Block size exceeds maximum: {} bytes (max {} bytes)",
194 block_size, context.validation_rules.max_block_size
195 )
196 .into(),
197 ));
198 }
199
200 if block.transactions.len() > 10000 {
202 return Err(ProtocolError::Validation(
204 "Too many transactions in block (max 10000)".into(),
205 ));
206 }
207
208 for tx in &block.transactions {
210 self.apply_transaction_protocol_validation(tx, context)?;
211 }
212
213 Ok(())
214 }
215
216 fn apply_transaction_protocol_validation(
218 &self,
219 tx: &Transaction,
220 context: &ProtocolValidationContext,
221 ) -> Result<()> {
222 let tx_size = self.calculate_transaction_size(tx);
224 if tx_size > context.validation_rules.max_tx_size {
225 return Err(ProtocolError::Validation(
226 format!(
227 "Transaction size exceeds maximum: {} bytes (max {} bytes)",
228 tx_size, context.validation_rules.max_tx_size
229 )
230 .into(),
231 ));
232 }
233
234 for input in &tx.inputs {
236 if input.script_sig.len() > context.validation_rules.max_script_size as usize {
237 return Err(ProtocolError::Validation(
238 format!(
239 "Script size exceeds maximum: {} bytes (max {} bytes)",
240 input.script_sig.len(),
241 context.validation_rules.max_script_size
242 )
243 .into(),
244 ));
245 }
246 }
247
248 for output in &tx.outputs {
249 if output.script_pubkey.len() > context.validation_rules.max_script_size as usize {
250 return Err(ProtocolError::Validation(
251 format!(
252 "Script size exceeds maximum: {} bytes (max {} bytes)",
253 output.script_pubkey.len(),
254 context.validation_rules.max_script_size
255 )
256 .into(),
257 ));
258 }
259 }
260
261 Ok(())
262 }
263
264 fn calculate_block_size(&self, block: &Block) -> u32 {
266 let header_size = 80; let tx_count_size = 4; let tx_sizes: u32 = block
271 .transactions
272 .iter()
273 .map(|tx| self.calculate_transaction_size(tx))
274 .sum();
275
276 header_size + tx_count_size + tx_sizes
277 }
278
279 fn calculate_transaction_size(&self, tx: &Transaction) -> u32 {
281 blvm_consensus::transaction::calculate_transaction_size(tx) as u32
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292 use blvm_consensus::types::{OutPoint, TransactionInput, TransactionOutput};
293 use blvm_consensus::{Block, BlockHeader, Transaction};
294
295 #[test]
296 fn test_validation_rules() {
297 let mainnet_rules = ProtocolValidationRules::mainnet();
298 assert_eq!(mainnet_rules.max_block_size, 4_000_000);
299 assert!(mainnet_rules.segwit_enabled);
300 assert!(mainnet_rules.taproot_enabled);
301
302 let regtest_rules = ProtocolValidationRules::regtest();
303 assert_eq!(regtest_rules.max_block_size, 4_000_000);
304 assert!(regtest_rules.segwit_enabled);
305 assert_eq!(regtest_rules.min_fee_rate, 0); }
307
308 #[test]
309 fn test_validation_rules_all_protocols() {
310 let mainnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::BitcoinV1);
311 let testnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Testnet3);
312 let regtest_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Regtest);
313
314 assert_eq!(mainnet_rules.max_block_size, testnet_rules.max_block_size);
316 assert_eq!(mainnet_rules.max_tx_size, testnet_rules.max_tx_size);
317 assert_eq!(mainnet_rules.max_script_size, testnet_rules.max_script_size);
318 assert_eq!(mainnet_rules.segwit_enabled, testnet_rules.segwit_enabled);
319 assert_eq!(mainnet_rules.taproot_enabled, testnet_rules.taproot_enabled);
320 assert_eq!(mainnet_rules.rbf_enabled, testnet_rules.rbf_enabled);
321 assert_eq!(mainnet_rules.min_fee_rate, testnet_rules.min_fee_rate);
322 assert_eq!(mainnet_rules.max_fee_rate, testnet_rules.max_fee_rate);
323
324 assert_eq!(regtest_rules.min_fee_rate, 0);
326 assert_eq!(regtest_rules.max_fee_rate, mainnet_rules.max_fee_rate);
327 }
328
329 #[test]
330 fn test_validation_rules_serialization() {
331 let mainnet_rules = ProtocolValidationRules::mainnet();
332 let json = serde_json::to_string(&mainnet_rules).unwrap();
333 let deserialized: ProtocolValidationRules = serde_json::from_str(&json).unwrap();
334
335 assert_eq!(mainnet_rules.max_block_size, deserialized.max_block_size);
336 assert_eq!(mainnet_rules.max_tx_size, deserialized.max_tx_size);
337 assert_eq!(mainnet_rules.max_script_size, deserialized.max_script_size);
338 assert_eq!(mainnet_rules.segwit_enabled, deserialized.segwit_enabled);
339 assert_eq!(mainnet_rules.taproot_enabled, deserialized.taproot_enabled);
340 assert_eq!(mainnet_rules.rbf_enabled, deserialized.rbf_enabled);
341 assert_eq!(mainnet_rules.min_fee_rate, deserialized.min_fee_rate);
342 assert_eq!(mainnet_rules.max_fee_rate, deserialized.max_fee_rate);
343 }
344
345 #[test]
346 fn test_validation_rules_equality() {
347 let mainnet1 = ProtocolValidationRules::mainnet();
348 let mainnet2 = ProtocolValidationRules::mainnet();
349 let testnet = ProtocolValidationRules::testnet();
350
351 assert_eq!(mainnet1, mainnet2);
352 assert_eq!(mainnet1, testnet); }
354
355 #[test]
356 fn test_validation_context() {
357 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
358 assert_eq!(context.block_height, 1000);
359 assert!(context.is_feature_enabled("segwit"));
360 assert!(!context.is_feature_enabled("nonexistent"));
361 assert_eq!(context.get_max_size("block"), 4_000_000);
362 }
363
364 #[test]
365 fn test_validation_context_all_protocols() {
366 let mainnet_context =
367 ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
368 let testnet_context =
369 ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
370 let regtest_context =
371 ProtocolValidationContext::new(ProtocolVersion::Regtest, 1000).unwrap();
372
373 assert_eq!(mainnet_context.block_height, 1000);
375 assert_eq!(testnet_context.block_height, 1000);
376 assert_eq!(regtest_context.block_height, 1000);
377
378 assert!(mainnet_context.is_feature_enabled("segwit"));
380 assert!(testnet_context.is_feature_enabled("segwit"));
381 assert!(regtest_context.is_feature_enabled("segwit"));
382
383 assert!(mainnet_context.is_feature_enabled("taproot"));
384 assert!(testnet_context.is_feature_enabled("taproot"));
385 assert!(regtest_context.is_feature_enabled("taproot"));
386
387 assert!(mainnet_context.is_feature_enabled("rbf"));
388 assert!(testnet_context.is_feature_enabled("rbf"));
389 assert!(regtest_context.is_feature_enabled("rbf"));
390 }
391
392 #[test]
393 fn test_validation_context_feature_queries() {
394 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
395
396 assert!(context.is_feature_enabled("segwit"));
398 assert!(context.is_feature_enabled("taproot"));
399 assert!(context.is_feature_enabled("rbf"));
400
401 assert!(!context.is_feature_enabled("nonexistent"));
403 assert!(!context.is_feature_enabled(""));
404 assert!(!context.is_feature_enabled("fast_mining"));
405 }
406
407 #[test]
408 fn test_validation_context_size_queries() {
409 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
410
411 assert_eq!(context.get_max_size("block"), 4_000_000);
412 assert_eq!(context.get_max_size("transaction"), 1_000_000);
413 assert_eq!(context.get_max_size("script"), 10_000);
414
415 assert_eq!(context.get_max_size("unknown"), 0);
417 }
418
419 #[test]
420 fn test_validation_context_serialization() {
421 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
422 let json = serde_json::to_string(&context).unwrap();
423 let deserialized: ProtocolValidationContext = serde_json::from_str(&json).unwrap();
424
425 assert_eq!(context.block_height, deserialized.block_height);
426 assert_eq!(
427 context.network_params.network_name,
428 deserialized.network_params.network_name
429 );
430 assert_eq!(
431 context.validation_rules.max_block_size,
432 deserialized.validation_rules.max_block_size
433 );
434 }
435
436 #[test]
437 fn test_validation_context_equality() {
438 let context1 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
439 let context2 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
440 let context3 = ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
441
442 assert_eq!(context1, context2);
443 assert_ne!(context1, context3); }
445
446 #[test]
447 fn test_block_size_validation() {
448 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
449 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
450
451 let coinbase_tx = Transaction {
453 version: 1,
454 inputs: blvm_consensus::tx_inputs![TransactionInput {
455 prevout: OutPoint {
456 hash: [0u8; 32],
457 index: 0xffffffff,
458 },
459 script_sig: vec![0x01, 0x00], sequence: 0xffffffff,
461 }],
462 outputs: blvm_consensus::tx_outputs![TransactionOutput {
463 value: 50_0000_0000,
464 script_pubkey: vec![blvm_consensus::opcodes::OP_1],
465 }],
466 lock_time: 0,
467 };
468
469 let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
471 .expect("Should calculate merkle root");
472
473 let small_block = Block {
474 header: BlockHeader {
475 version: 1,
476 prev_block_hash: [0u8; 32],
477 merkle_root,
478 timestamp: 1231006505,
479 bits: 0x1d00ffff,
480 nonce: 0,
481 },
482 transactions: vec![coinbase_tx].into_boxed_slice(),
483 };
484
485 let result =
487 engine.validate_block_with_protocol(&small_block, &UtxoSet::default(), 1000, &context);
488 assert!(result.is_ok());
489 }
490
491 #[test]
492 fn test_transaction_size_validation() {
493 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
494 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
495
496 let small_tx = Transaction {
498 version: 1,
499 inputs: vec![TransactionInput {
500 prevout: OutPoint {
501 hash: [0u8; 32],
502 index: 0,
503 },
504 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
505 sequence: 0xffffffff,
506 }]
507 .into(),
508 outputs: vec![TransactionOutput {
509 value: 50_0000_0000,
510 script_pubkey: vec![
511 blvm_consensus::opcodes::OP_DUP,
512 blvm_consensus::opcodes::OP_HASH160,
513 blvm_consensus::opcodes::PUSH_20_BYTES,
514 0x00,
515 0x00,
516 0x00,
517 0x00,
518 0x00,
519 0x00,
520 0x00,
521 0x00,
522 0x00,
523 0x00,
524 0x00,
525 0x00,
526 0x00,
527 0x00,
528 0x00,
529 0x00,
530 0x00,
531 0x00,
532 0x00,
533 0x00,
534 blvm_consensus::opcodes::OP_EQUALVERIFY,
535 blvm_consensus::opcodes::OP_CHECKSIG,
536 ],
537 }]
538 .into(),
539 lock_time: 0,
540 };
541
542 let result = engine.validate_transaction_with_protocol(&small_tx, &context);
544 assert!(result.is_ok());
545 }
546
547 #[test]
548 fn test_script_size_validation() {
549 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
550 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
551
552 let tx = Transaction {
554 version: 1,
555 inputs: vec![TransactionInput {
556 prevout: OutPoint {
557 hash: [0u8; 32],
558 index: 0,
559 },
560 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
561 sequence: 0xffffffff,
562 }]
563 .into(),
564 outputs: vec![TransactionOutput {
565 value: 50_0000_0000,
566 script_pubkey: vec![
567 blvm_consensus::opcodes::OP_DUP,
568 blvm_consensus::opcodes::OP_HASH160,
569 blvm_consensus::opcodes::PUSH_20_BYTES,
570 0x00,
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 blvm_consensus::opcodes::OP_EQUALVERIFY,
591 blvm_consensus::opcodes::OP_CHECKSIG,
592 ],
593 }]
594 .into(),
595 lock_time: 0,
596 };
597
598 let result = engine.validate_transaction_with_protocol(&tx, &context);
600 assert!(result.is_ok());
601 }
602
603 #[test]
604 fn test_validation_context_data() {
605 let mut context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
606
607 context
609 .context_data
610 .insert("test_key".to_string(), "test_value".to_string());
611
612 assert_eq!(
613 context.context_data.get("test_key"),
614 Some(&"test_value".to_string())
615 );
616 assert_eq!(context.context_data.get("nonexistent"), None);
617 }
618
619 #[test]
620 fn test_validation_rules_boundary_values() {
621 let rules = ProtocolValidationRules::mainnet();
622
623 assert!(rules.max_block_size > 0);
625 assert!(rules.max_tx_size > 0);
626 assert!(rules.max_script_size > 0);
627 assert!(rules.max_fee_rate > rules.min_fee_rate);
628
629 assert!(rules.max_block_size <= 10_000_000); assert!(rules.max_tx_size <= 5_000_000); assert!(rules.max_script_size <= 50_000); }
634}