Skip to main content

blvm_protocol/
validation.rs

1//! Protocol validation interface
2//!
3//! This module provides protocol-specific validation that extends
4//! the pure mathematical consensus rules with network-specific
5//! and protocol-specific validation logic.
6
7use 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
15// Protocol-specific Result type
16type Result<T> = std::result::Result<T, ProtocolError>;
17
18/// Protocol-specific validation rules
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct ProtocolValidationRules {
21    /// Maximum block size for this protocol
22    pub max_block_size: u32,
23    /// Maximum transaction size for this protocol
24    pub max_tx_size: u32,
25    /// Maximum script size for this protocol
26    pub max_script_size: u32,
27    /// Whether SegWit is enabled
28    pub segwit_enabled: bool,
29    /// Whether Taproot is enabled
30    pub taproot_enabled: bool,
31    /// Whether RBF (Replace-By-Fee) is enabled
32    pub rbf_enabled: bool,
33    /// Minimum transaction fee rate
34    pub min_fee_rate: u64,
35    /// Maximum transaction fee rate
36    pub max_fee_rate: u64,
37}
38
39impl ProtocolValidationRules {
40    /// Get validation rules for a specific protocol version
41    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    /// Mainnet validation rules (strict production rules)
50    pub fn mainnet() -> Self {
51        Self {
52            max_block_size: 4_000_000, // 4MB block size limit
53            max_tx_size: 1_000_000,    // 1MB transaction size limit
54            max_script_size: 10_000,   // 10KB script size limit
55            segwit_enabled: true,
56            taproot_enabled: true,
57            rbf_enabled: true,
58            min_fee_rate: 1,         // 1 sat/vB minimum
59            max_fee_rate: 1_000_000, // 1M sat/vB maximum
60        }
61    }
62
63    /// Testnet validation rules (same as mainnet but with testnet parameters)
64    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    /// Regtest validation rules (relaxed for testing)
78    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, // No minimum fee for testing
87            max_fee_rate: 1_000_000,
88        }
89    }
90}
91
92/// Protocol-specific validation context
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94pub struct ProtocolValidationContext {
95    /// Current block height
96    pub block_height: u64,
97    /// Current network parameters
98    pub network_params: NetworkParameters,
99    /// Protocol validation rules
100    pub validation_rules: ProtocolValidationRules,
101    /// Feature activation registry for this protocol version.
102    ///
103    /// Used by `is_feature_enabled` to derive activation status from
104    /// `block_height` / `network_time` rather than static booleans.
105    pub feature_registry: FeatureRegistry,
106    /// Median time-past used for time-based validation (BIP113)
107    ///
108    /// This is populated by the node using recent headers and is threaded down
109    /// to consensus for timestamp validation.
110    pub median_time_past: u64,
111    /// Current adjusted network time (Unix timestamp)
112    ///
113    /// This is populated by the node from its time source and used by consensus
114    /// to enforce future timestamp limits.
115    pub network_time: u64,
116    /// Additional context data
117    pub context_data: HashMap<String, String>,
118}
119
120impl ProtocolValidationContext {
121    /// Create validation context for a protocol version
122    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            // Default to zero; callers that care about time must set these explicitly.
133            median_time_past: 0,
134            network_time: 0,
135            context_data: HashMap::new(),
136        })
137    }
138
139    /// Check if a feature is enabled at the current block height and network time.
140    ///
141    /// Queries the `FeatureRegistry` so activation reflects the actual deployment
142    /// height/timestamp rather than static booleans that ignore `block_height`.
143    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    /// Get maximum allowed size for a component
149    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    /// Validate a block with protocol-specific rules
161    pub fn validate_block_with_protocol(
162        &self,
163        block: &Block,
164        _utxos: &UtxoSet,
165        _height: u64,
166        context: &ProtocolValidationContext,
167    ) -> Result<ValidationResult> {
168        // First, apply protocol-specific validation
169        self.apply_protocol_validation(block, context)?;
170
171        Ok(ValidationResult::Valid)
172    }
173
174    /// Validate a transaction with protocol-specific rules
175    pub fn validate_transaction_with_protocol(
176        &self,
177        tx: &Transaction,
178        context: &ProtocolValidationContext,
179    ) -> Result<ValidationResult> {
180        // First, run consensus validation
181        let consensus_result = self.consensus.validate_transaction(tx)?;
182
183        // Then, apply protocol-specific validation
184        self.apply_transaction_protocol_validation(tx, context)?;
185
186        Ok(consensus_result)
187    }
188
189    /// Apply protocol-specific validation rules
190    fn apply_protocol_validation(
191        &self,
192        block: &Block,
193        context: &ProtocolValidationContext,
194    ) -> Result<()> {
195        // Check block weight limits (BIP141).
196        // `max_block_size` is in weight units (4,000,000 WU = 4MB weight).
197        //
198        // LIMITATION (P1-H/P1-I): the `Block` type carries no witness data, so we can only
199        // compute base weight here (stripped_size * 4). BIP141 actual weight is:
200        //   weight = stripped_size * 4 + witness_size
201        // A block with stripped_size < 1 MB can still exceed 4 MWU if its witnesses are large.
202        // This check is therefore a LOWER BOUND — it rejects blocks whose base weight alone
203        // exceeds the limit but cannot catch witness-heavy blocks that slip under the base check.
204        // Full witness-aware weight validation requires witnesses to flow into this path (see P1-I).
205        let stripped_size = self.calculate_block_size(block);
206        let base_weight = stripped_size.saturating_mul(4);
207        if base_weight > context.validation_rules.max_block_size {
208            return Err(ProtocolError::Validation(
209                format!(
210                    "Block base weight exceeds maximum: {} WU (max {} WU); stripped_size={}",
211                    base_weight, context.validation_rules.max_block_size, stripped_size
212                )
213                .into(),
214            ));
215        }
216
217        // Check transaction count limits
218        if block.transactions.len() > 10000 {
219            // Reasonable limit
220            return Err(ProtocolError::Validation(
221                "Too many transactions in block (max 10000)".into(),
222            ));
223        }
224
225        // Validate each transaction with protocol rules
226        for tx in &block.transactions {
227            self.apply_transaction_protocol_validation(tx, context)?;
228        }
229
230        Ok(())
231    }
232
233    /// Apply protocol-specific transaction validation
234    fn apply_transaction_protocol_validation(
235        &self,
236        tx: &Transaction,
237        context: &ProtocolValidationContext,
238    ) -> Result<()> {
239        // Check transaction size limits
240        let tx_size = self.calculate_transaction_size(tx);
241        if tx_size > context.validation_rules.max_tx_size {
242            return Err(ProtocolError::Validation(
243                format!(
244                    "Transaction size exceeds maximum: {} bytes (max {} bytes)",
245                    tx_size, context.validation_rules.max_tx_size
246                )
247                .into(),
248            ));
249        }
250
251        // Check script size limits
252        for input in &tx.inputs {
253            if input.script_sig.len() > context.validation_rules.max_script_size as usize {
254                return Err(ProtocolError::Validation(
255                    format!(
256                        "Script size exceeds maximum: {} bytes (max {} bytes)",
257                        input.script_sig.len(),
258                        context.validation_rules.max_script_size
259                    )
260                    .into(),
261                ));
262            }
263        }
264
265        for output in &tx.outputs {
266            if output.script_pubkey.len() > context.validation_rules.max_script_size as usize {
267                return Err(ProtocolError::Validation(
268                    format!(
269                        "Script size exceeds maximum: {} bytes (max {} bytes)",
270                        output.script_pubkey.len(),
271                        context.validation_rules.max_script_size
272                    )
273                    .into(),
274                ));
275            }
276        }
277
278        Ok(())
279    }
280
281    /// Calculate block size in bytes
282    fn calculate_block_size(&self, block: &Block) -> u32 {
283        // Simplified size calculation
284        // In reality, this would include proper serialization
285        let header_size = 80; // Block header is always 80 bytes
286        let tx_count_size = 4; // Varint for transaction count
287        let tx_sizes: u32 = block
288            .transactions
289            .iter()
290            .map(|tx| self.calculate_transaction_size(tx))
291            .sum();
292
293        header_size + tx_count_size + tx_sizes
294    }
295
296    /// Calculate transaction size in bytes
297    fn calculate_transaction_size(&self, tx: &Transaction) -> u32 {
298        // Use canonical serialization-based size from consensus layer (TX_NO_WITNESS size).
299        //
300        // This keeps protocol-level size limits aligned with the exact serialization
301        // used for consensus checks and transaction size tests.
302        blvm_consensus::transaction::calculate_transaction_size(tx) as u32
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309    use blvm_consensus::types::{OutPoint, TransactionInput, TransactionOutput};
310    use blvm_consensus::{Block, BlockHeader, Transaction};
311
312    #[test]
313    fn test_validation_rules() {
314        let mainnet_rules = ProtocolValidationRules::mainnet();
315        assert_eq!(mainnet_rules.max_block_size, 4_000_000);
316        assert!(mainnet_rules.segwit_enabled);
317        assert!(mainnet_rules.taproot_enabled);
318
319        let regtest_rules = ProtocolValidationRules::regtest();
320        assert_eq!(regtest_rules.max_block_size, 4_000_000);
321        assert!(regtest_rules.segwit_enabled);
322        assert_eq!(regtest_rules.min_fee_rate, 0); // No minimum fee for testing
323    }
324
325    #[test]
326    fn test_validation_rules_all_protocols() {
327        let mainnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::BitcoinV1);
328        let testnet_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Testnet3);
329        let regtest_rules = ProtocolValidationRules::for_protocol(ProtocolVersion::Regtest);
330
331        // Mainnet and testnet should have same rules
332        assert_eq!(mainnet_rules.max_block_size, testnet_rules.max_block_size);
333        assert_eq!(mainnet_rules.max_tx_size, testnet_rules.max_tx_size);
334        assert_eq!(mainnet_rules.max_script_size, testnet_rules.max_script_size);
335        assert_eq!(mainnet_rules.segwit_enabled, testnet_rules.segwit_enabled);
336        assert_eq!(mainnet_rules.taproot_enabled, testnet_rules.taproot_enabled);
337        assert_eq!(mainnet_rules.rbf_enabled, testnet_rules.rbf_enabled);
338        assert_eq!(mainnet_rules.min_fee_rate, testnet_rules.min_fee_rate);
339        assert_eq!(mainnet_rules.max_fee_rate, testnet_rules.max_fee_rate);
340
341        // Regtest should have relaxed fee rules
342        assert_eq!(regtest_rules.min_fee_rate, 0);
343        assert_eq!(regtest_rules.max_fee_rate, mainnet_rules.max_fee_rate);
344    }
345
346    #[test]
347    fn test_validation_rules_serialization() {
348        let mainnet_rules = ProtocolValidationRules::mainnet();
349        let json = serde_json::to_string(&mainnet_rules).unwrap();
350        let deserialized: ProtocolValidationRules = serde_json::from_str(&json).unwrap();
351
352        assert_eq!(mainnet_rules.max_block_size, deserialized.max_block_size);
353        assert_eq!(mainnet_rules.max_tx_size, deserialized.max_tx_size);
354        assert_eq!(mainnet_rules.max_script_size, deserialized.max_script_size);
355        assert_eq!(mainnet_rules.segwit_enabled, deserialized.segwit_enabled);
356        assert_eq!(mainnet_rules.taproot_enabled, deserialized.taproot_enabled);
357        assert_eq!(mainnet_rules.rbf_enabled, deserialized.rbf_enabled);
358        assert_eq!(mainnet_rules.min_fee_rate, deserialized.min_fee_rate);
359        assert_eq!(mainnet_rules.max_fee_rate, deserialized.max_fee_rate);
360    }
361
362    #[test]
363    fn test_validation_rules_equality() {
364        let mainnet1 = ProtocolValidationRules::mainnet();
365        let mainnet2 = ProtocolValidationRules::mainnet();
366        let testnet = ProtocolValidationRules::testnet();
367
368        assert_eq!(mainnet1, mainnet2);
369        assert_eq!(mainnet1, testnet); // Mainnet and testnet should be identical
370    }
371
372    #[test]
373    fn test_validation_context() {
374        // Use a height past both segwit (481824) and taproot (709632) activation.
375        let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
376        assert_eq!(context.block_height, 800_000);
377        assert!(context.is_feature_enabled("segwit"));
378        assert!(!context.is_feature_enabled("nonexistent"));
379        assert_eq!(context.get_max_size("block"), 4_000_000);
380    }
381
382    #[test]
383    fn test_validation_context_all_protocols() {
384        // Per-network heights past segwit+taproot activation:
385        //   mainnet: taproot at 709_632   → use 800_000
386        //   testnet3: taproot at 2_016_000 → use 2_100_000
387        //   regtest: all features AlwaysActive → any height works
388        let mainnet_context =
389            ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
390        let testnet_context =
391            ProtocolValidationContext::new(ProtocolVersion::Testnet3, 2_100_000).unwrap();
392        let regtest_context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 1).unwrap();
393
394        assert_eq!(mainnet_context.block_height, 800_000);
395        assert_eq!(testnet_context.block_height, 2_100_000);
396        assert_eq!(regtest_context.block_height, 1);
397
398        // All should support same features
399        assert!(mainnet_context.is_feature_enabled("segwit"));
400        assert!(testnet_context.is_feature_enabled("segwit"));
401        assert!(regtest_context.is_feature_enabled("segwit"));
402
403        assert!(mainnet_context.is_feature_enabled("taproot"));
404        assert!(testnet_context.is_feature_enabled("taproot"));
405        assert!(regtest_context.is_feature_enabled("taproot"));
406
407        assert!(mainnet_context.is_feature_enabled("rbf"));
408        assert!(testnet_context.is_feature_enabled("rbf"));
409        assert!(regtest_context.is_feature_enabled("rbf"));
410    }
411
412    #[test]
413    fn test_validation_context_feature_queries() {
414        // Height past both segwit (481824) and taproot (709632) so all features are active.
415        let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 800_000).unwrap();
416
417        // Test all supported features
418        assert!(context.is_feature_enabled("segwit"));
419        assert!(context.is_feature_enabled("taproot"));
420        assert!(context.is_feature_enabled("rbf"));
421
422        // Test unsupported features
423        assert!(!context.is_feature_enabled("nonexistent"));
424        assert!(!context.is_feature_enabled(""));
425        assert!(!context.is_feature_enabled("fast_mining"));
426    }
427
428    #[test]
429    fn test_validation_context_size_queries() {
430        let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
431
432        assert_eq!(context.get_max_size("block"), 4_000_000);
433        assert_eq!(context.get_max_size("transaction"), 1_000_000);
434        assert_eq!(context.get_max_size("script"), 10_000);
435
436        // Test unknown component
437        assert_eq!(context.get_max_size("unknown"), 0);
438    }
439
440    #[test]
441    fn test_validation_context_serialization() {
442        let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
443        let json = serde_json::to_string(&context).unwrap();
444        let deserialized: ProtocolValidationContext = serde_json::from_str(&json).unwrap();
445
446        assert_eq!(context.block_height, deserialized.block_height);
447        assert_eq!(
448            context.network_params.network_name,
449            deserialized.network_params.network_name
450        );
451        assert_eq!(
452            context.validation_rules.max_block_size,
453            deserialized.validation_rules.max_block_size
454        );
455    }
456
457    #[test]
458    fn test_validation_context_equality() {
459        let context1 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
460        let context2 = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
461        let context3 = ProtocolValidationContext::new(ProtocolVersion::Testnet3, 1000).unwrap();
462
463        assert_eq!(context1, context2);
464        assert_ne!(context1, context3); // Different network parameters
465    }
466
467    #[test]
468    fn test_block_size_validation() {
469        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
470        let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
471
472        // Create a block that's within size limits with a valid coinbase transaction
473        let coinbase_tx = Transaction {
474            version: 1,
475            inputs: blvm_consensus::tx_inputs![TransactionInput {
476                prevout: OutPoint {
477                    hash: [0u8; 32],
478                    index: 0xffffffff,
479                },
480                script_sig: vec![0x01, 0x00], // Height 0
481                sequence: 0xffffffff,
482            }],
483            outputs: blvm_consensus::tx_outputs![TransactionOutput {
484                value: 50_0000_0000,
485                script_pubkey: vec![blvm_consensus::opcodes::OP_1],
486            }],
487            lock_time: 0,
488        };
489
490        // Calculate proper merkle root
491        let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
492            .expect("Should calculate merkle root");
493
494        let small_block = Block {
495            header: BlockHeader {
496                version: 1,
497                prev_block_hash: [0u8; 32],
498                merkle_root,
499                timestamp: 1231006505,
500                bits: 0x1d00ffff,
501                nonce: 0,
502            },
503            transactions: vec![coinbase_tx].into_boxed_slice(),
504        };
505
506        // This should pass validation
507        let result =
508            engine.validate_block_with_protocol(&small_block, &UtxoSet::default(), 1000, &context);
509        assert!(result.is_ok());
510    }
511
512    #[test]
513    fn test_transaction_size_validation() {
514        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
515        let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
516
517        // Create a small transaction
518        let small_tx = Transaction {
519            version: 1,
520            inputs: vec![TransactionInput {
521                prevout: OutPoint {
522                    hash: [0u8; 32],
523                    index: 0,
524                },
525                script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
526                sequence: 0xffffffff,
527            }]
528            .into(),
529            outputs: vec![TransactionOutput {
530                value: 50_0000_0000,
531                script_pubkey: vec![
532                    blvm_consensus::opcodes::OP_DUP,
533                    blvm_consensus::opcodes::OP_HASH160,
534                    blvm_consensus::opcodes::PUSH_20_BYTES,
535                    0x00,
536                    0x00,
537                    0x00,
538                    0x00,
539                    0x00,
540                    0x00,
541                    0x00,
542                    0x00,
543                    0x00,
544                    0x00,
545                    0x00,
546                    0x00,
547                    0x00,
548                    0x00,
549                    0x00,
550                    0x00,
551                    0x00,
552                    0x00,
553                    0x00,
554                    0x00,
555                    blvm_consensus::opcodes::OP_EQUALVERIFY,
556                    blvm_consensus::opcodes::OP_CHECKSIG,
557                ],
558            }]
559            .into(),
560            lock_time: 0,
561        };
562
563        // This should pass validation
564        let result = engine.validate_transaction_with_protocol(&small_tx, &context);
565        assert!(result.is_ok());
566    }
567
568    #[test]
569    fn test_script_size_validation() {
570        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
571        let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
572
573        // Create a transaction with small scripts
574        let tx = Transaction {
575            version: 1,
576            inputs: vec![TransactionInput {
577                prevout: OutPoint {
578                    hash: [0u8; 32],
579                    index: 0,
580                },
581                script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
582                sequence: 0xffffffff,
583            }]
584            .into(),
585            outputs: vec![TransactionOutput {
586                value: 50_0000_0000,
587                script_pubkey: vec![
588                    blvm_consensus::opcodes::OP_DUP,
589                    blvm_consensus::opcodes::OP_HASH160,
590                    blvm_consensus::opcodes::PUSH_20_BYTES,
591                    0x00,
592                    0x00,
593                    0x00,
594                    0x00,
595                    0x00,
596                    0x00,
597                    0x00,
598                    0x00,
599                    0x00,
600                    0x00,
601                    0x00,
602                    0x00,
603                    0x00,
604                    0x00,
605                    0x00,
606                    0x00,
607                    0x00,
608                    0x00,
609                    0x00,
610                    0x00,
611                    blvm_consensus::opcodes::OP_EQUALVERIFY,
612                    blvm_consensus::opcodes::OP_CHECKSIG,
613                ],
614            }]
615            .into(),
616            lock_time: 0,
617        };
618
619        // This should pass validation
620        let result = engine.validate_transaction_with_protocol(&tx, &context);
621        assert!(result.is_ok());
622    }
623
624    #[test]
625    fn test_validation_context_data() {
626        let mut context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 1000).unwrap();
627
628        // Add some context data
629        context
630            .context_data
631            .insert("test_key".to_string(), "test_value".to_string());
632
633        assert_eq!(
634            context.context_data.get("test_key"),
635            Some(&"test_value".to_string())
636        );
637        assert_eq!(context.context_data.get("nonexistent"), None);
638    }
639
640    #[test]
641    fn test_validation_rules_boundary_values() {
642        let rules = ProtocolValidationRules::mainnet();
643
644        // Test boundary values
645        assert!(rules.max_block_size > 0);
646        assert!(rules.max_tx_size > 0);
647        assert!(rules.max_script_size > 0);
648        assert!(rules.max_fee_rate > rules.min_fee_rate);
649
650        // Test that limits are reasonable
651        assert!(rules.max_block_size <= 10_000_000); // Not unreasonably large
652        assert!(rules.max_tx_size <= 5_000_000); // Not unreasonably large
653        assert!(rules.max_script_size <= 50_000); // Not unreasonably large
654    }
655}