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 Ok(())
304 }
305
306 fn calculate_transaction_size(&self, tx: &Transaction) -> u32 {
309 blvm_consensus::transaction::calculate_transaction_size(tx) as u32
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use blvm_consensus::types::{OutPoint, TransactionInput, TransactionOutput};
321 use blvm_consensus::{Block, BlockHeader, Transaction};
322
323 #[test]
324 fn test_validation_rules() {
325 let mainnet_rules = ProtocolValidationRules::mainnet();
326 assert_eq!(mainnet_rules.max_block_size, 4_000_000);
327 assert!(mainnet_rules.segwit_enabled);
328 assert!(mainnet_rules.taproot_enabled);
329
330 let regtest_rules = ProtocolValidationRules::regtest();
331 assert_eq!(regtest_rules.max_block_size, 4_000_000);
332 assert!(regtest_rules.segwit_enabled);
333 assert_eq!(regtest_rules.min_fee_rate, 0); }
335
336 #[test]
337 fn test_validation_rules_all_protocols() {
338 let mainnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::BitcoinV1);
339 let testnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Testnet3);
340 let regtest_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Regtest);
341
342 assert_eq!(mainnet_rules.max_block_size, testnet_rules.max_block_size);
344 assert_eq!(mainnet_rules.max_tx_size, testnet_rules.max_tx_size);
345 assert_eq!(mainnet_rules.max_script_size, testnet_rules.max_script_size);
346 assert_eq!(mainnet_rules.segwit_enabled, testnet_rules.segwit_enabled);
347 assert_eq!(mainnet_rules.taproot_enabled, testnet_rules.taproot_enabled);
348 assert_eq!(mainnet_rules.rbf_enabled, testnet_rules.rbf_enabled);
349 assert_eq!(mainnet_rules.min_fee_rate, testnet_rules.min_fee_rate);
350 assert_eq!(mainnet_rules.max_fee_rate, testnet_rules.max_fee_rate);
351
352 assert_eq!(regtest_rules.min_fee_rate, 0);
354 assert_eq!(regtest_rules.max_fee_rate, mainnet_rules.max_fee_rate);
355 }
356
357 #[test]
358 fn test_validation_rules_serialization() {
359 let mainnet_rules = ProtocolValidationRules::mainnet();
360 let json = serde_json::to_string(&mainnet_rules).unwrap();
361 let deserialized: ProtocolValidationRules = serde_json::from_str(&json).unwrap();
362
363 assert_eq!(mainnet_rules.max_block_size, deserialized.max_block_size);
364 assert_eq!(mainnet_rules.max_tx_size, deserialized.max_tx_size);
365 assert_eq!(mainnet_rules.max_script_size, deserialized.max_script_size);
366 assert_eq!(mainnet_rules.segwit_enabled, deserialized.segwit_enabled);
367 assert_eq!(mainnet_rules.taproot_enabled, deserialized.taproot_enabled);
368 assert_eq!(mainnet_rules.rbf_enabled, deserialized.rbf_enabled);
369 assert_eq!(mainnet_rules.min_fee_rate, deserialized.min_fee_rate);
370 assert_eq!(mainnet_rules.max_fee_rate, deserialized.max_fee_rate);
371 }
372
373 #[test]
374 fn test_validation_rules_equality() {
375 let mainnet1 = ProtocolValidationRules::mainnet();
376 let mainnet2 = ProtocolValidationRules::mainnet();
377 let testnet = ProtocolValidationRules::testnet();
378
379 assert_eq!(mainnet1, mainnet2);
380 assert_eq!(mainnet1, testnet); }
382
383 #[test]
384 fn test_validation_context() {
385 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
387 assert_eq!(context.block_height, 800_000);
388 assert!(context.is_feature_enabled("segwit"));
389 assert!(!context.is_feature_enabled("nonexistent"));
390 assert_eq!(context.get_max_size("block"), 4_000_000);
391 }
392
393 #[test]
394 fn test_validation_context_all_protocols() {
395 let mainnet_context =
400 ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
401 let testnet_context =
402 ProtocolValidationContext::new(ProtocolVersion::Testnet3, 2_100_000).unwrap();
403 let regtest_context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
404
405 assert_eq!(mainnet_context.block_height, 800_000);
406 assert_eq!(testnet_context.block_height, 2_100_000);
407 assert_eq!(regtest_context.block_height, 1);
408
409 assert!(mainnet_context.is_feature_enabled("segwit"));
411 assert!(testnet_context.is_feature_enabled("segwit"));
412 assert!(regtest_context.is_feature_enabled("segwit"));
413
414 assert!(mainnet_context.is_feature_enabled("taproot"));
415 assert!(testnet_context.is_feature_enabled("taproot"));
416 assert!(regtest_context.is_feature_enabled("taproot"));
417
418 assert!(mainnet_context.is_feature_enabled("rbf"));
419 assert!(testnet_context.is_feature_enabled("rbf"));
420 assert!(regtest_context.is_feature_enabled("rbf"));
421 }
422
423 #[test]
424 fn test_validation_context_feature_queries() {
425 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
427
428 assert!(context.is_feature_enabled("segwit"));
430 assert!(context.is_feature_enabled("taproot"));
431 assert!(context.is_feature_enabled("rbf"));
432
433 assert!(!context.is_feature_enabled("nonexistent"));
435 assert!(!context.is_feature_enabled(""));
436 assert!(!context.is_feature_enabled("fast_mining"));
437 }
438
439 #[test]
440 fn test_validation_context_size_queries() {
441 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
442
443 assert_eq!(context.get_max_size("block"), 4_000_000);
444 assert_eq!(context.get_max_size("transaction"), 1_000_000);
445 assert_eq!(context.get_max_size("script"), 10_000);
446
447 assert_eq!(context.get_max_size("unknown"), 0);
449 }
450
451 #[test]
452 fn test_validation_context_serialization() {
453 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
454 let json = serde_json::to_string(&context).unwrap();
455 let deserialized: ProtocolValidationContext = serde_json::from_str(&json).unwrap();
456
457 assert_eq!(context.block_height, deserialized.block_height);
458 assert_eq!(
459 context.network_params.network_name,
460 deserialized.network_params.network_name
461 );
462 assert_eq!(
463 context.validation_rules.max_block_size,
464 deserialized.validation_rules.max_block_size
465 );
466 }
467
468 #[test]
469 fn test_validation_context_equality() {
470 let context1 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
471 let context2 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
472 let context3 = ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
473
474 assert_eq!(context1, context2);
475 assert_ne!(context1, context3); }
477
478 #[test]
479 fn test_block_size_validation() {
480 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
481 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
482
483 let coinbase_tx = Transaction {
485 version: 1,
486 inputs: blvm_consensus::tx_inputs![TransactionInput {
487 prevout: OutPoint {
488 hash: [0u8; 32],
489 index: 0xffffffff,
490 },
491 script_sig: vec![0x01, 0x00], sequence: 0xffffffff,
493 }],
494 outputs: blvm_consensus::tx_outputs![TransactionOutput {
495 value: 50_0000_0000,
496 script_pubkey: vec![blvm_consensus::opcodes::OP_1],
497 }],
498 lock_time: 0,
499 };
500
501 let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
503 .expect("Should calculate merkle root");
504
505 let small_block = Block {
506 header: BlockHeader {
507 version: 1,
508 prev_block_hash: [0u8; 32],
509 merkle_root,
510 timestamp: 1231006505,
511 bits: 0x1d00ffff,
512 nonce: 0,
513 },
514 transactions: vec![coinbase_tx].into_boxed_slice(),
515 };
516
517 let result =
519 engine.validate_block_with_protocol(&small_block, &UtxoSet::default(), 1000, &context);
520 assert!(result.is_ok());
521 }
522
523 #[test]
524 fn test_transaction_size_validation() {
525 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
526 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
527
528 let small_tx = Transaction {
530 version: 1,
531 inputs: vec![TransactionInput {
532 prevout: OutPoint {
533 hash: [0u8; 32],
534 index: 0,
535 },
536 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
537 sequence: 0xffffffff,
538 }]
539 .into(),
540 outputs: vec![TransactionOutput {
541 value: 50_0000_0000,
542 script_pubkey: vec![
543 blvm_consensus::opcodes::OP_DUP,
544 blvm_consensus::opcodes::OP_HASH160,
545 blvm_consensus::opcodes::PUSH_20_BYTES,
546 0x00,
547 0x00,
548 0x00,
549 0x00,
550 0x00,
551 0x00,
552 0x00,
553 0x00,
554 0x00,
555 0x00,
556 0x00,
557 0x00,
558 0x00,
559 0x00,
560 0x00,
561 0x00,
562 0x00,
563 0x00,
564 0x00,
565 0x00,
566 blvm_consensus::opcodes::OP_EQUALVERIFY,
567 blvm_consensus::opcodes::OP_CHECKSIG,
568 ],
569 }]
570 .into(),
571 lock_time: 0,
572 };
573
574 let result = engine.validate_transaction_with_protocol(&small_tx, &context);
576 assert!(result.is_ok());
577 }
578
579 #[test]
580 fn test_script_size_validation() {
581 let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
582 let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
583
584 let tx = Transaction {
586 version: 1,
587 inputs: vec![TransactionInput {
588 prevout: OutPoint {
589 hash: [0u8; 32],
590 index: 0,
591 },
592 script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
593 sequence: 0xffffffff,
594 }]
595 .into(),
596 outputs: vec![TransactionOutput {
597 value: 50_0000_0000,
598 script_pubkey: vec![
599 blvm_consensus::opcodes::OP_DUP,
600 blvm_consensus::opcodes::OP_HASH160,
601 blvm_consensus::opcodes::PUSH_20_BYTES,
602 0x00,
603 0x00,
604 0x00,
605 0x00,
606 0x00,
607 0x00,
608 0x00,
609 0x00,
610 0x00,
611 0x00,
612 0x00,
613 0x00,
614 0x00,
615 0x00,
616 0x00,
617 0x00,
618 0x00,
619 0x00,
620 0x00,
621 0x00,
622 blvm_consensus::opcodes::OP_EQUALVERIFY,
623 blvm_consensus::opcodes::OP_CHECKSIG,
624 ],
625 }]
626 .into(),
627 lock_time: 0,
628 };
629
630 let result = engine.validate_transaction_with_protocol(&tx, &context);
632 assert!(result.is_ok());
633 }
634
635 #[test]
636 fn test_validation_context_data() {
637 let mut context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
638
639 context
641 .context_data
642 .insert("test_key".to_string(), "test_value".to_string());
643
644 assert_eq!(
645 context.context_data.get("test_key"),
646 Some(&"test_value".to_string())
647 );
648 assert_eq!(context.context_data.get("nonexistent"), None);
649 }
650
651 #[test]
652 fn test_validation_rules_boundary_values() {
653 let rules = ProtocolValidationRules::mainnet();
654
655 assert!(rules.max_block_size > 0);
657 assert!(rules.max_tx_size > 0);
658 assert!(rules.max_script_size > 0);
659 assert!(rules.max_fee_rate > rules.min_fee_rate);
660
661 assert!(rules.max_block_size <= 10_000_000); assert!(rules.max_tx_size <= 5_000_000); assert!(rules.max_script_size <= 50_000); }
666
667 #[test]
668 fn test_block_weight_counts_witness_element_bytes() {
669 use blvm_consensus::opcodes::OP_1;
670 use blvm_consensus::{
671 Block, BlockHeader, OutPoint, Transaction, TransactionInput, TransactionOutput,
672 };
673
674 let engine = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
675 let mut context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
676 context.validation_rules.max_block_size = 5_000;
677
678 let coinbase = Transaction {
679 version: 1,
680 inputs: vec![TransactionInput {
681 prevout: OutPoint {
682 hash: [0u8; 32],
683 index: 0xffffffff,
684 },
685 script_sig: vec![OP_1].into(),
686 sequence: 0xffffffff,
687 }]
688 .into(),
689 outputs: vec![TransactionOutput {
690 value: 50_0000_0000,
691 script_pubkey: vec![OP_1].into(),
692 }]
693 .into(),
694 lock_time: 0,
695 };
696 let tx = Transaction {
697 version: 2,
698 inputs: vec![TransactionInput {
699 prevout: OutPoint {
700 hash: [1u8; 32],
701 index: 0,
702 },
703 script_sig: vec![OP_1].into(),
704 sequence: 0xffffffff,
705 }]
706 .into(),
707 outputs: vec![TransactionOutput {
708 value: 1_000,
709 script_pubkey: vec![OP_1].into(),
710 }]
711 .into(),
712 lock_time: 0,
713 };
714 let block = Block {
715 header: BlockHeader {
716 version: 1,
717 prev_block_hash: [0u8; 32],
718 merkle_root: [0u8; 32],
719 timestamp: 1_700_000_000,
720 bits: 0x207fffff,
721 nonce: 0,
722 },
723 transactions: vec![coinbase, tx].into(),
724 };
725 let heavy_elem = vec![0u8; 9000];
727 let witnesses: Vec<Vec<blvm_consensus::segwit::Witness>> =
728 vec![vec![vec![]], vec![vec![heavy_elem]]];
729
730 let result = engine.validate_block_with_protocol_and_witnesses(
731 &block,
732 &witnesses,
733 &UtxoSet::default(),
734 1,
735 &context,
736 );
737 assert!(
738 result.is_err(),
739 "heavy witness bytes must exceed reduced max_block_size weight limit"
740 );
741 }
742
743 #[test]
744 fn protocol_block_weight_matches_consensus_including_witness_framing() {
745 use blvm_consensus::opcodes::OP_1;
746 use blvm_consensus::{
747 Block, BlockHeader, OutPoint, Transaction, TransactionInput, TransactionOutput,
748 };
749
750 let engine = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
751 let mut context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
752 let coinbase = Transaction {
753 version: 1,
754 inputs: vec![TransactionInput {
755 prevout: OutPoint {
756 hash: [0u8; 32],
757 index: 0xffffffff,
758 },
759 script_sig: vec![OP_1].into(),
760 sequence: 0xffffffff,
761 }]
762 .into(),
763 outputs: vec![TransactionOutput {
764 value: 50_0000_0000,
765 script_pubkey: vec![OP_1].into(),
766 }]
767 .into(),
768 lock_time: 0,
769 };
770 let tx = Transaction {
771 version: 2,
772 inputs: vec![TransactionInput {
773 prevout: OutPoint {
774 hash: [1u8; 32],
775 index: 0,
776 },
777 script_sig: vec![OP_1].into(),
778 sequence: 0xffffffff,
779 }]
780 .into(),
781 outputs: vec![TransactionOutput {
782 value: 1_000,
783 script_pubkey: vec![OP_1].into(),
784 }]
785 .into(),
786 lock_time: 0,
787 };
788 let block = Block {
789 header: BlockHeader {
790 version: 1,
791 prev_block_hash: [0u8; 32],
792 merkle_root: [0u8; 32],
793 timestamp: 1_700_000_000,
794 bits: 0x207fffff,
795 nonce: 0,
796 },
797 transactions: vec![coinbase, tx].into(),
798 };
799 let witnesses: Vec<Vec<blvm_consensus::segwit::Witness>> =
800 vec![vec![vec![]], vec![vec![vec![0x11]]]];
801 let weight = blvm_consensus::segwit::calculate_block_weight_from_nested(&block, &witnesses)
802 .expect("witness stacks match the inputs");
803 context.validation_rules.max_block_size = weight as u32;
804 assert!(
805 engine
806 .validate_block_with_protocol_and_witnesses(
807 &block,
808 &witnesses,
809 &UtxoSet::default(),
810 1,
811 &context,
812 )
813 .is_ok(),
814 "weight equal to the consensus weight is accepted"
815 );
816 context.validation_rules.max_block_size = (weight - 1) 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_err(),
827 "one weight unit under the consensus weight is rejected"
828 );
829 }
830}