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 ProtocolVersion::Testnet4 => Self::testnet4(),
48 }
49 }
50
51 pub fn mainnet() -> Self {
53 Self {
54 max_block_size: 4_000_000, max_tx_size: 1_000_000, max_script_size: 10_000, segwit_enabled: true,
58 taproot_enabled: true,
59 rbf_enabled: true,
60 min_fee_rate: 1, max_fee_rate: 1_000_000, }
63 }
64
65 pub fn testnet() -> Self {
67 Self {
68 max_block_size: 4_000_000,
69 max_tx_size: 1_000_000,
70 max_script_size: 10_000,
71 segwit_enabled: true,
72 taproot_enabled: true,
73 rbf_enabled: true,
74 min_fee_rate: 1,
75 max_fee_rate: 1_000_000,
76 }
77 }
78
79 pub fn regtest() -> Self {
81 Self {
82 max_block_size: 4_000_000,
83 max_tx_size: 1_000_000,
84 max_script_size: 10_000,
85 segwit_enabled: true,
86 taproot_enabled: true,
87 rbf_enabled: true,
88 min_fee_rate: 0, max_fee_rate: 1_000_000,
90 }
91 }
92
93 pub fn testnet4() -> Self {
95 Self::testnet()
96 }
97
98 pub fn signet() -> Self {
100 Self {
101 max_block_size: 4_000_000,
102 max_tx_size: 1_000_000,
103 max_script_size: 10_000,
104 segwit_enabled: true,
105 taproot_enabled: true,
106 rbf_enabled: true,
107 min_fee_rate: 1,
108 max_fee_rate: 1_000_000,
109 }
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115pub struct ProtocolValidationContext {
116 pub block_height: u64,
118 pub network_params: NetworkParameters,
120 pub validation_rules: ProtocolValidationRules,
122 pub feature_registry: FeatureRegistry,
127 pub median_time_past: u64,
132 pub network_time: u64,
137 pub context_data: HashMap<String, String>,
139}
140
141impl ProtocolValidationContext {
142 pub fn new(version: ProtocolVersion, block_height: u64) -> Result<Self> {
144 let network_params = NetworkParameters::for_version(version)?;
145 let validation_rules = ProtocolValidationRules::for_protocol(version);
146
147 let feature_registry = FeatureRegistry::for_protocol(version);
148 Ok(Self {
149 block_height,
150 network_params,
151 validation_rules,
152 feature_registry,
153 median_time_past: 0,
155 network_time: 0,
156 context_data: HashMap::new(),
157 })
158 }
159
160 pub fn is_feature_enabled(&self, feature: &str) -> bool {
165 self.feature_registry
166 .is_feature_active(feature, self.block_height, self.network_time)
167 }
168
169 pub fn get_max_size(&self, component: &str) -> u32 {
171 match component {
172 "block" => self.validation_rules.max_block_size,
173 "transaction" => self.validation_rules.max_tx_size,
174 "script" => self.validation_rules.max_script_size,
175 _ => 0,
176 }
177 }
178}
179
180impl BitcoinProtocolEngine {
181 pub fn validate_block_with_protocol(
186 &self,
187 block: &Block,
188 _utxos: &UtxoSet,
189 _height: u64,
190 context: &ProtocolValidationContext,
191 ) -> Result<ValidationResult> {
192 self.apply_protocol_validation(block, context)?;
193 Ok(ValidationResult::Valid)
194 }
195
196 pub fn validate_block_with_protocol_and_witnesses(
200 &self,
201 block: &Block,
202 witnesses: &[Vec<blvm_consensus::segwit::Witness>],
203 _utxos: &UtxoSet,
204 _height: u64,
205 context: &ProtocolValidationContext,
206 ) -> Result<ValidationResult> {
207 self.apply_protocol_validation_with_witnesses(block, Some(witnesses), context)?;
208 Ok(ValidationResult::Valid)
209 }
210
211 pub fn validate_transaction_with_protocol(
213 &self,
214 tx: &Transaction,
215 context: &ProtocolValidationContext,
216 ) -> Result<ValidationResult> {
217 let consensus_result = self.consensus.validate_transaction(tx)?;
219
220 self.apply_transaction_protocol_validation(tx, context)?;
222
223 Ok(consensus_result)
224 }
225
226 fn apply_protocol_validation(
233 &self,
234 block: &Block,
235 context: &ProtocolValidationContext,
236 ) -> Result<()> {
237 self.apply_protocol_validation_with_witnesses(block, None, context)
238 }
239
240 fn apply_protocol_validation_with_witnesses(
246 &self,
247 block: &Block,
248 witnesses: Option<&[Vec<blvm_consensus::segwit::Witness>]>,
249 context: &ProtocolValidationContext,
250 ) -> Result<()> {
251 let witness_list = witnesses.unwrap_or(&[]);
252 let weight =
253 blvm_consensus::segwit::calculate_block_weight_from_nested(block, witness_list)?;
254 if weight > context.validation_rules.max_block_size as u64 {
255 return Err(ProtocolError::Validation(
256 format!(
257 "Block weight exceeds maximum: {} WU (max {} WU)",
258 weight, context.validation_rules.max_block_size
259 )
260 .into(),
261 ));
262 }
263
264 for tx in &block.transactions {
265 self.apply_transaction_protocol_validation(tx, context)?;
266 }
267
268 Ok(())
269 }
270
271 fn apply_transaction_protocol_validation(
273 &self,
274 tx: &Transaction,
275 context: &ProtocolValidationContext,
276 ) -> Result<()> {
277 let tx_size = self.calculate_transaction_size(tx);
279 if tx_size > context.validation_rules.max_tx_size {
280 return Err(ProtocolError::Validation(
281 format!(
282 "Transaction size exceeds maximum: {} bytes (max {} bytes)",
283 tx_size, context.validation_rules.max_tx_size
284 )
285 .into(),
286 ));
287 }
288
289 for input in &tx.inputs {
291 if input.script_sig.len() > context.validation_rules.max_script_size as usize {
292 return Err(ProtocolError::Validation(
293 format!(
294 "Script size exceeds maximum: {} bytes (max {} bytes)",
295 input.script_sig.len(),
296 context.validation_rules.max_script_size
297 )
298 .into(),
299 ));
300 }
301 }
302
303 for output in &tx.outputs {
304 if output.script_pubkey.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 output.script_pubkey.len(),
309 context.validation_rules.max_script_size
310 )
311 .into(),
312 ));
313 }
314 }
315
316 Ok(())
317 }
318
319 fn calculate_transaction_size(&self, tx: &Transaction) -> u32 {
322 blvm_consensus::transaction::calculate_transaction_size(tx) as u32
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333 use blvm_consensus::types::{OutPoint, TransactionInput, TransactionOutput};
334 use blvm_consensus::{Block, BlockHeader, Transaction};
335
336 #[test]
337 fn test_validation_rules() {
338 let mainnet_rules = ProtocolValidationRules::mainnet();
339 assert_eq!(mainnet_rules.max_block_size, 4_000_000);
340 assert!(mainnet_rules.segwit_enabled);
341 assert!(mainnet_rules.taproot_enabled);
342
343 let regtest_rules = ProtocolValidationRules::regtest();
344 assert_eq!(regtest_rules.max_block_size, 4_000_000);
345 assert!(regtest_rules.segwit_enabled);
346 assert_eq!(regtest_rules.min_fee_rate, 0); }
348
349 #[test]
350 fn test_validation_rules_all_protocols() {
351 let mainnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::BitcoinV1);
352 let testnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Testnet3);
353 let regtest_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Regtest);
354
355 assert_eq!(mainnet_rules.max_block_size, testnet_rules.max_block_size);
357 assert_eq!(mainnet_rules.max_tx_size, testnet_rules.max_tx_size);
358 assert_eq!(mainnet_rules.max_script_size, testnet_rules.max_script_size);
359 assert_eq!(mainnet_rules.segwit_enabled, testnet_rules.segwit_enabled);
360 assert_eq!(mainnet_rules.taproot_enabled, testnet_rules.taproot_enabled);
361 assert_eq!(mainnet_rules.rbf_enabled, testnet_rules.rbf_enabled);
362 assert_eq!(mainnet_rules.min_fee_rate, testnet_rules.min_fee_rate);
363 assert_eq!(mainnet_rules.max_fee_rate, testnet_rules.max_fee_rate);
364
365 assert_eq!(regtest_rules.min_fee_rate, 0);
367 assert_eq!(regtest_rules.max_fee_rate, mainnet_rules.max_fee_rate);
368 }
369
370 #[test]
371 fn test_validation_rules_serialization() {
372 let mainnet_rules = ProtocolValidationRules::mainnet();
373 let json = serde_json::to_string(&mainnet_rules).unwrap();
374 let deserialized: ProtocolValidationRules = serde_json::from_str(&json).unwrap();
375
376 assert_eq!(mainnet_rules.max_block_size, deserialized.max_block_size);
377 assert_eq!(mainnet_rules.max_tx_size, deserialized.max_tx_size);
378 assert_eq!(mainnet_rules.max_script_size, deserialized.max_script_size);
379 assert_eq!(mainnet_rules.segwit_enabled, deserialized.segwit_enabled);
380 assert_eq!(mainnet_rules.taproot_enabled, deserialized.taproot_enabled);
381 assert_eq!(mainnet_rules.rbf_enabled, deserialized.rbf_enabled);
382 assert_eq!(mainnet_rules.min_fee_rate, deserialized.min_fee_rate);
383 assert_eq!(mainnet_rules.max_fee_rate, deserialized.max_fee_rate);
384 }
385
386 #[test]
387 fn test_validation_rules_equality() {
388 let mainnet1 = ProtocolValidationRules::mainnet();
389 let mainnet2 = ProtocolValidationRules::mainnet();
390 let testnet = ProtocolValidationRules::testnet();
391
392 assert_eq!(mainnet1, mainnet2);
393 assert_eq!(mainnet1, testnet); }
395
396 #[test]
397 fn test_validation_context() {
398 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
400 assert_eq!(context.block_height, 800_000);
401 assert!(context.is_feature_enabled("segwit"));
402 assert!(!context.is_feature_enabled("nonexistent"));
403 assert_eq!(context.get_max_size("block"), 4_000_000);
404 }
405
406 #[test]
407 fn test_validation_context_all_protocols() {
408 let mainnet_context =
413 ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
414 let testnet_context =
415 ProtocolValidationContext::new(ProtocolVersion::Testnet3, 2_100_000).unwrap();
416 let regtest_context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
417
418 assert_eq!(mainnet_context.block_height, 800_000);
419 assert_eq!(testnet_context.block_height, 2_100_000);
420 assert_eq!(regtest_context.block_height, 1);
421
422 assert!(mainnet_context.is_feature_enabled("segwit"));
424 assert!(testnet_context.is_feature_enabled("segwit"));
425 assert!(regtest_context.is_feature_enabled("segwit"));
426
427 assert!(mainnet_context.is_feature_enabled("taproot"));
428 assert!(testnet_context.is_feature_enabled("taproot"));
429 assert!(regtest_context.is_feature_enabled("taproot"));
430
431 assert!(mainnet_context.is_feature_enabled("rbf"));
432 assert!(testnet_context.is_feature_enabled("rbf"));
433 assert!(regtest_context.is_feature_enabled("rbf"));
434 }
435
436 #[test]
437 fn test_validation_context_feature_queries() {
438 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
440
441 assert!(context.is_feature_enabled("segwit"));
443 assert!(context.is_feature_enabled("taproot"));
444 assert!(context.is_feature_enabled("rbf"));
445
446 assert!(!context.is_feature_enabled("nonexistent"));
448 assert!(!context.is_feature_enabled(""));
449 assert!(!context.is_feature_enabled("fast_mining"));
450 }
451
452 #[test]
453 fn test_validation_context_size_queries() {
454 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
455
456 assert_eq!(context.get_max_size("block"), 4_000_000);
457 assert_eq!(context.get_max_size("transaction"), 1_000_000);
458 assert_eq!(context.get_max_size("script"), 10_000);
459
460 assert_eq!(context.get_max_size("unknown"), 0);
462 }
463
464 #[test]
465 fn test_validation_context_serialization() {
466 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
467 let json = serde_json::to_string(&context).unwrap();
468 let deserialized: ProtocolValidationContext = serde_json::from_str(&json).unwrap();
469
470 assert_eq!(context.block_height, deserialized.block_height);
471 assert_eq!(
472 context.network_params.network_name,
473 deserialized.network_params.network_name
474 );
475 assert_eq!(
476 context.validation_rules.max_block_size,
477 deserialized.validation_rules.max_block_size
478 );
479 }
480
481 #[test]
482 fn test_validation_context_equality() {
483 let context1 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
484 let context2 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
485 let context3 = ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
486
487 assert_eq!(context1, context2);
488 assert_ne!(context1, context3); }
490
491 #[test]
492 fn test_block_size_validation() {
493 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
494 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
495
496 let coinbase_tx = Transaction {
498 version: 1,
499 inputs: blvm_consensus::tx_inputs![TransactionInput {
500 prevout: OutPoint {
501 hash: [0u8; 32],
502 index: 0xffffffff,
503 },
504 script_sig: vec![0x01, 0x00], sequence: 0xffffffff,
506 }],
507 outputs: blvm_consensus::tx_outputs![TransactionOutput {
508 value: 50_0000_0000,
509 script_pubkey: vec![blvm_consensus::opcodes::OP_1],
510 }],
511 lock_time: 0,
512 };
513
514 let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
516 .expect("Should calculate merkle root");
517
518 let small_block = Block {
519 header: BlockHeader {
520 version: 1,
521 prev_block_hash: [0u8; 32],
522 merkle_root,
523 timestamp: 1231006505,
524 bits: 0x1d00ffff,
525 nonce: 0,
526 },
527 transactions: vec![coinbase_tx].into_boxed_slice(),
528 };
529
530 let result =
532 engine.validate_block_with_protocol(&small_block, &UtxoSet::default(), 1000, &context);
533 assert!(result.is_ok());
534 }
535
536 #[test]
537 fn test_transaction_size_validation() {
538 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
539 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
540
541 let small_tx = Transaction {
543 version: 1,
544 inputs: vec![TransactionInput {
545 prevout: OutPoint {
546 hash: [0u8; 32],
547 index: 0,
548 },
549 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
550 sequence: 0xffffffff,
551 }]
552 .into(),
553 outputs: vec![TransactionOutput {
554 value: 50_0000_0000,
555 script_pubkey: vec![
556 blvm_consensus::opcodes::OP_DUP,
557 blvm_consensus::opcodes::OP_HASH160,
558 blvm_consensus::opcodes::PUSH_20_BYTES,
559 0x00,
560 0x00,
561 0x00,
562 0x00,
563 0x00,
564 0x00,
565 0x00,
566 0x00,
567 0x00,
568 0x00,
569 0x00,
570 0x00,
571 0x00,
572 0x00,
573 0x00,
574 0x00,
575 0x00,
576 0x00,
577 0x00,
578 0x00,
579 blvm_consensus::opcodes::OP_EQUALVERIFY,
580 blvm_consensus::opcodes::OP_CHECKSIG,
581 ],
582 }]
583 .into(),
584 lock_time: 0,
585 };
586
587 let result = engine.validate_transaction_with_protocol(&small_tx, &context);
589 assert!(result.is_ok());
590 }
591
592 #[test]
593 fn test_script_size_validation() {
594 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
595 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
596
597 let tx = Transaction {
599 version: 1,
600 inputs: vec![TransactionInput {
601 prevout: OutPoint {
602 hash: [0u8; 32],
603 index: 0,
604 },
605 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
606 sequence: 0xffffffff,
607 }]
608 .into(),
609 outputs: vec![TransactionOutput {
610 value: 50_0000_0000,
611 script_pubkey: vec![
612 blvm_consensus::opcodes::OP_DUP,
613 blvm_consensus::opcodes::OP_HASH160,
614 blvm_consensus::opcodes::PUSH_20_BYTES,
615 0x00,
616 0x00,
617 0x00,
618 0x00,
619 0x00,
620 0x00,
621 0x00,
622 0x00,
623 0x00,
624 0x00,
625 0x00,
626 0x00,
627 0x00,
628 0x00,
629 0x00,
630 0x00,
631 0x00,
632 0x00,
633 0x00,
634 0x00,
635 blvm_consensus::opcodes::OP_EQUALVERIFY,
636 blvm_consensus::opcodes::OP_CHECKSIG,
637 ],
638 }]
639 .into(),
640 lock_time: 0,
641 };
642
643 let result = engine.validate_transaction_with_protocol(&tx, &context);
645 assert!(result.is_ok());
646 }
647
648 #[test]
649 fn test_validation_context_data() {
650 let mut context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
651
652 context
654 .context_data
655 .insert("test_key".to_string(), "test_value".to_string());
656
657 assert_eq!(
658 context.context_data.get("test_key"),
659 Some(&"test_value".to_string())
660 );
661 assert_eq!(context.context_data.get("nonexistent"), None);
662 }
663
664 #[test]
665 fn test_validation_rules_boundary_values() {
666 let rules = ProtocolValidationRules::mainnet();
667
668 assert!(rules.max_block_size > 0);
670 assert!(rules.max_tx_size > 0);
671 assert!(rules.max_script_size > 0);
672 assert!(rules.max_fee_rate > rules.min_fee_rate);
673
674 assert!(rules.max_block_size <= 10_000_000); assert!(rules.max_tx_size <= 5_000_000); assert!(rules.max_script_size <= 50_000); }
679
680 #[test]
681 fn test_block_weight_counts_witness_element_bytes() {
682 use blvm_consensus::opcodes::OP_1;
683 use blvm_consensus::{
684 Block, BlockHeader, OutPoint, Transaction, TransactionInput, TransactionOutput,
685 };
686
687 let engine = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
688 let mut context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
689 context.validation_rules.max_block_size = 5_000;
690
691 let coinbase = Transaction {
692 version: 1,
693 inputs: vec![TransactionInput {
694 prevout: OutPoint {
695 hash: [0u8; 32],
696 index: 0xffffffff,
697 },
698 script_sig: vec![OP_1].into(),
699 sequence: 0xffffffff,
700 }]
701 .into(),
702 outputs: vec![TransactionOutput {
703 value: 50_0000_0000,
704 script_pubkey: vec![OP_1].into(),
705 }]
706 .into(),
707 lock_time: 0,
708 };
709 let tx = Transaction {
710 version: 2,
711 inputs: vec![TransactionInput {
712 prevout: OutPoint {
713 hash: [1u8; 32],
714 index: 0,
715 },
716 script_sig: vec![OP_1].into(),
717 sequence: 0xffffffff,
718 }]
719 .into(),
720 outputs: vec![TransactionOutput {
721 value: 1_000,
722 script_pubkey: vec![OP_1].into(),
723 }]
724 .into(),
725 lock_time: 0,
726 };
727 let block = Block {
728 header: BlockHeader {
729 version: 1,
730 prev_block_hash: [0u8; 32],
731 merkle_root: [0u8; 32],
732 timestamp: 1_700_000_000,
733 bits: 0x207fffff,
734 nonce: 0,
735 },
736 transactions: vec![coinbase, tx].into(),
737 };
738 let heavy_elem = vec![0u8; 9000];
740 let witnesses: Vec<Vec<blvm_consensus::segwit::Witness>> =
741 vec![vec![vec![]], vec![vec![heavy_elem]]];
742
743 let result = engine.validate_block_with_protocol_and_witnesses(
744 &block,
745 &witnesses,
746 &UtxoSet::default(),
747 1,
748 &context,
749 );
750 assert!(
751 result.is_err(),
752 "heavy witness bytes must exceed reduced max_block_size weight limit"
753 );
754 }
755
756 #[test]
757 fn protocol_block_weight_matches_consensus_including_witness_framing() {
758 use blvm_consensus::opcodes::OP_1;
759 use blvm_consensus::{
760 Block, BlockHeader, OutPoint, Transaction, TransactionInput, TransactionOutput,
761 };
762
763 let engine = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
764 let mut context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
765 let coinbase = Transaction {
766 version: 1,
767 inputs: vec![TransactionInput {
768 prevout: OutPoint {
769 hash: [0u8; 32],
770 index: 0xffffffff,
771 },
772 script_sig: vec![OP_1].into(),
773 sequence: 0xffffffff,
774 }]
775 .into(),
776 outputs: vec![TransactionOutput {
777 value: 50_0000_0000,
778 script_pubkey: vec![OP_1].into(),
779 }]
780 .into(),
781 lock_time: 0,
782 };
783 let tx = Transaction {
784 version: 2,
785 inputs: vec![TransactionInput {
786 prevout: OutPoint {
787 hash: [1u8; 32],
788 index: 0,
789 },
790 script_sig: vec![OP_1].into(),
791 sequence: 0xffffffff,
792 }]
793 .into(),
794 outputs: vec![TransactionOutput {
795 value: 1_000,
796 script_pubkey: vec![OP_1].into(),
797 }]
798 .into(),
799 lock_time: 0,
800 };
801 let block = Block {
802 header: BlockHeader {
803 version: 1,
804 prev_block_hash: [0u8; 32],
805 merkle_root: [0u8; 32],
806 timestamp: 1_700_000_000,
807 bits: 0x207fffff,
808 nonce: 0,
809 },
810 transactions: vec![coinbase, tx].into(),
811 };
812 let witnesses: Vec<Vec<blvm_consensus::segwit::Witness>> =
813 vec![vec![vec![]], vec![vec![vec![0x11]]]];
814 let weight = blvm_consensus::segwit::calculate_block_weight_from_nested(&block, &witnesses)
815 .expect("witness stacks match the inputs");
816 context.validation_rules.max_block_size = weight as u32;
817 assert!(
818 engine
819 .validate_block_with_protocol_and_witnesses(
820 &block,
821 &witnesses,
822 &UtxoSet::default(),
823 1,
824 &context,
825 )
826 .is_ok(),
827 "weight equal to the consensus weight is accepted"
828 );
829 context.validation_rules.max_block_size = (weight - 1) as u32;
830 assert!(
831 engine
832 .validate_block_with_protocol_and_witnesses(
833 &block,
834 &witnesses,
835 &UtxoSet::default(),
836 1,
837 &context,
838 )
839 .is_err(),
840 "one weight unit under the consensus weight is rejected"
841 );
842 }
843}