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 use std::collections::HashMap;
295
296 #[test]
297 fn test_validation_rules() {
298 let mainnet_rules = ProtocolValidationRules::mainnet();
299 assert_eq!(mainnet_rules.max_block_size, 4_000_000);
300 assert!(mainnet_rules.segwit_enabled);
301 assert!(mainnet_rules.taproot_enabled);
302
303 let regtest_rules = ProtocolValidationRules::regtest();
304 assert_eq!(regtest_rules.max_block_size, 4_000_000);
305 assert!(regtest_rules.segwit_enabled);
306 assert_eq!(regtest_rules.min_fee_rate, 0); }
308
309 #[test]
310 fn test_validation_rules_all_protocols() {
311 let mainnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::BitcoinV1);
312 let testnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Testnet3);
313 let regtest_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Regtest);
314
315 assert_eq!(mainnet_rules.max_block_size, testnet_rules.max_block_size);
317 assert_eq!(mainnet_rules.max_tx_size, testnet_rules.max_tx_size);
318 assert_eq!(mainnet_rules.max_script_size, testnet_rules.max_script_size);
319 assert_eq!(mainnet_rules.segwit_enabled, testnet_rules.segwit_enabled);
320 assert_eq!(mainnet_rules.taproot_enabled, testnet_rules.taproot_enabled);
321 assert_eq!(mainnet_rules.rbf_enabled, testnet_rules.rbf_enabled);
322 assert_eq!(mainnet_rules.min_fee_rate, testnet_rules.min_fee_rate);
323 assert_eq!(mainnet_rules.max_fee_rate, testnet_rules.max_fee_rate);
324
325 assert_eq!(regtest_rules.min_fee_rate, 0);
327 assert_eq!(regtest_rules.max_fee_rate, mainnet_rules.max_fee_rate);
328 }
329
330 #[test]
331 fn test_validation_rules_serialization() {
332 let mainnet_rules = ProtocolValidationRules::mainnet();
333 let json = serde_json::to_string(&mainnet_rules).unwrap();
334 let deserialized: ProtocolValidationRules = serde_json::from_str(&json).unwrap();
335
336 assert_eq!(mainnet_rules.max_block_size, deserialized.max_block_size);
337 assert_eq!(mainnet_rules.max_tx_size, deserialized.max_tx_size);
338 assert_eq!(mainnet_rules.max_script_size, deserialized.max_script_size);
339 assert_eq!(mainnet_rules.segwit_enabled, deserialized.segwit_enabled);
340 assert_eq!(mainnet_rules.taproot_enabled, deserialized.taproot_enabled);
341 assert_eq!(mainnet_rules.rbf_enabled, deserialized.rbf_enabled);
342 assert_eq!(mainnet_rules.min_fee_rate, deserialized.min_fee_rate);
343 assert_eq!(mainnet_rules.max_fee_rate, deserialized.max_fee_rate);
344 }
345
346 #[test]
347 fn test_validation_rules_equality() {
348 let mainnet1 = ProtocolValidationRules::mainnet();
349 let mainnet2 = ProtocolValidationRules::mainnet();
350 let testnet = ProtocolValidationRules::testnet();
351
352 assert_eq!(mainnet1, mainnet2);
353 assert_eq!(mainnet1, testnet); }
355
356 #[test]
357 fn test_validation_context() {
358 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
359 assert_eq!(context.block_height, 1000);
360 assert!(context.is_feature_enabled("segwit"));
361 assert!(!context.is_feature_enabled("nonexistent"));
362 assert_eq!(context.get_max_size("block"), 4_000_000);
363 }
364
365 #[test]
366 fn test_validation_context_all_protocols() {
367 let mainnet_context =
368 ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
369 let testnet_context =
370 ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
371 let regtest_context =
372 ProtocolValidationContext::new(ProtocolVersion::Regtest, 1000).unwrap();
373
374 assert_eq!(mainnet_context.block_height, 1000);
376 assert_eq!(testnet_context.block_height, 1000);
377 assert_eq!(regtest_context.block_height, 1000);
378
379 assert!(mainnet_context.is_feature_enabled("segwit"));
381 assert!(testnet_context.is_feature_enabled("segwit"));
382 assert!(regtest_context.is_feature_enabled("segwit"));
383
384 assert!(mainnet_context.is_feature_enabled("taproot"));
385 assert!(testnet_context.is_feature_enabled("taproot"));
386 assert!(regtest_context.is_feature_enabled("taproot"));
387
388 assert!(mainnet_context.is_feature_enabled("rbf"));
389 assert!(testnet_context.is_feature_enabled("rbf"));
390 assert!(regtest_context.is_feature_enabled("rbf"));
391 }
392
393 #[test]
394 fn test_validation_context_feature_queries() {
395 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
396
397 assert!(context.is_feature_enabled("segwit"));
399 assert!(context.is_feature_enabled("taproot"));
400 assert!(context.is_feature_enabled("rbf"));
401
402 assert!(!context.is_feature_enabled("nonexistent"));
404 assert!(!context.is_feature_enabled(""));
405 assert!(!context.is_feature_enabled("fast_mining"));
406 }
407
408 #[test]
409 fn test_validation_context_size_queries() {
410 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
411
412 assert_eq!(context.get_max_size("block"), 4_000_000);
413 assert_eq!(context.get_max_size("transaction"), 1_000_000);
414 assert_eq!(context.get_max_size("script"), 10_000);
415
416 assert_eq!(context.get_max_size("unknown"), 0);
418 }
419
420 #[test]
421 fn test_validation_context_serialization() {
422 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
423 let json = serde_json::to_string(&context).unwrap();
424 let deserialized: ProtocolValidationContext = serde_json::from_str(&json).unwrap();
425
426 assert_eq!(context.block_height, deserialized.block_height);
427 assert_eq!(
428 context.network_params.network_name,
429 deserialized.network_params.network_name
430 );
431 assert_eq!(
432 context.validation_rules.max_block_size,
433 deserialized.validation_rules.max_block_size
434 );
435 }
436
437 #[test]
438 fn test_validation_context_equality() {
439 let context1 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
440 let context2 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
441 let context3 = ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
442
443 assert_eq!(context1, context2);
444 assert_ne!(context1, context3); }
446
447 #[test]
448 fn test_block_size_validation() {
449 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
450 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
451
452 let coinbase_tx = Transaction {
454 version: 1,
455 inputs: blvm_consensus::tx_inputs![TransactionInput {
456 prevout: OutPoint {
457 hash: [0u8; 32],
458 index: 0xffffffff,
459 },
460 script_sig: vec![0x01, 0x00], sequence: 0xffffffff,
462 }],
463 outputs: blvm_consensus::tx_outputs![TransactionOutput {
464 value: 50_0000_0000,
465 script_pubkey: vec![blvm_consensus::opcodes::OP_1],
466 }],
467 lock_time: 0,
468 };
469
470 let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
472 .expect("Should calculate merkle root");
473
474 let small_block = Block {
475 header: BlockHeader {
476 version: 1,
477 prev_block_hash: [0u8; 32],
478 merkle_root,
479 timestamp: 1231006505,
480 bits: 0x1d00ffff,
481 nonce: 0,
482 },
483 transactions: vec![coinbase_tx].into_boxed_slice(),
484 };
485
486 let result =
488 engine.validate_block_with_protocol(&small_block, &UtxoSet::default(), 1000, &context);
489 assert!(result.is_ok());
490 }
491
492 #[test]
493 fn test_transaction_size_validation() {
494 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
495 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
496
497 let small_tx = Transaction {
499 version: 1,
500 inputs: vec![TransactionInput {
501 prevout: OutPoint {
502 hash: [0u8; 32],
503 index: 0,
504 },
505 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
506 sequence: 0xffffffff,
507 }]
508 .into(),
509 outputs: vec![TransactionOutput {
510 value: 50_0000_0000,
511 script_pubkey: vec![
512 blvm_consensus::opcodes::OP_DUP,
513 blvm_consensus::opcodes::OP_HASH160,
514 blvm_consensus::opcodes::PUSH_20_BYTES,
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 0x00,
535 blvm_consensus::opcodes::OP_EQUALVERIFY,
536 blvm_consensus::opcodes::OP_CHECKSIG,
537 ],
538 }]
539 .into(),
540 lock_time: 0,
541 };
542
543 let result = engine.validate_transaction_with_protocol(&small_tx, &context);
545 assert!(result.is_ok());
546 }
547
548 #[test]
549 fn test_script_size_validation() {
550 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
551 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
552
553 let 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(&tx, &context);
601 assert!(result.is_ok());
602 }
603
604 #[test]
605 fn test_validation_context_data() {
606 let mut context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
607
608 context
610 .context_data
611 .insert("test_key".to_string(), "test_value".to_string());
612
613 assert_eq!(
614 context.context_data.get("test_key"),
615 Some(&"test_value".to_string())
616 );
617 assert_eq!(context.context_data.get("nonexistent"), None);
618 }
619
620 #[test]
621 fn test_validation_rules_boundary_values() {
622 let rules = ProtocolValidationRules::mainnet();
623
624 assert!(rules.max_block_size > 0);
626 assert!(rules.max_tx_size > 0);
627 assert!(rules.max_script_size > 0);
628 assert!(rules.max_fee_rate > rules.min_fee_rate);
629
630 assert!(rules.max_block_size <= 10_000_000); assert!(rules.max_tx_size <= 5_000_000); assert!(rules.max_script_size <= 50_000); }
635}