Skip to main content

blvm_protocol/
lib.rs

1//! Bitcoin Protocol Engine
2//!
3//! This crate provides a Bitcoin protocol abstraction layer that enables:
4//! - Multiple Bitcoin variants (mainnet, testnet, regtest, educational)
5//! - Protocol evolution support (Bitcoin V1, V2, etc.)
6//! - Economic model abstraction (PoW, future variants)
7//! - Educational and research-friendly interfaces
8//!
9//! This is Tier 3 of the 5-tier BTCDecoded architecture:
10//!
11//! 1. Orange Paper (mathematical foundation)
12//! 2. blvm-consensus (pure math implementation)
13//! 3. blvm-protocol (Bitcoin abstraction) ← THIS CRATE
14//! 4. blvm-node (full Bitcoin node)
15//! 5. blvm-sdk (ergonomic API)
16
17use serde::{Deserialize, Serialize};
18
19// Re-export commonly used types for convenience
20// This allows upper layers (like blvm-node) to depend only on blvm-protocol
21pub use blvm_consensus::ConsensusProof;
22pub use blvm_consensus::config::NetworkMessageLimits;
23pub use blvm_consensus::error::{ConsensusError, Result as ConsensusResult};
24pub use blvm_consensus::types::{
25    Block, BlockHeader, ByteString, Hash, Integer, Natural, OutPoint, Transaction,
26    TransactionInput, TransactionOutput, UTXO, UtxoSet, ValidationResult, Witness,
27};
28
29#[cfg(all(feature = "production", feature = "benchmarking"))]
30pub use blvm_consensus::config::{reset_assume_valid_height, set_assume_valid_height};
31/// Buried deployment heights (Core chainparams) for RPC / tooling without a direct `blvm-consensus` dep.
32pub use blvm_consensus::{
33    BIP112_CSV_ACTIVATION_MAINNET, BIP112_CSV_ACTIVATION_REGTEST, BIP112_CSV_ACTIVATION_TESTNET,
34    GENESIS_BLOCK_HASH_INTERNAL, SEGWIT_ACTIVATION_MAINNET, SEGWIT_ACTIVATION_TESTNET,
35    TAPROOT_ACTIVATION_MAINNET, TAPROOT_ACTIVATION_TESTNET,
36};
37
38// Re-export smallvec for macro use when production feature is enabled
39#[cfg(feature = "production")]
40pub use smallvec;
41// Re-export lru and rayon for production caches and parallel validation
42#[cfg(feature = "production")]
43pub use lru;
44#[cfg(feature = "production")]
45pub use rayon;
46
47/// Forwards to `blvm_consensus::profile_log!` so upper layers avoid naming `blvm-consensus` directly.
48/// When the `profile` feature is off on consensus, the inner macro expands to a no-op.
49#[macro_export]
50macro_rules! profile_log {
51    ($($arg:tt)*) => {
52        ::blvm_consensus::profile_log!($($arg)*)
53    };
54}
55
56// Protocol-specific Result type
57pub use error::{ProtocolError, Result};
58
59// Re-export commonly used modules
60pub mod mempool {
61    pub use blvm_consensus::mempool::*;
62}
63pub mod segwit {
64    pub use blvm_consensus::segwit::*;
65}
66pub mod block {
67    pub use blvm_consensus::block::*;
68
69    use crate::types::{BlockHeader, Network};
70
71    /// Forwards to [`BlockValidationContext::from_connect_block_ibd_args`] with no BIP54 activation
72    /// override and no boundary timestamps. Does not invent time or headers; pass the same values
73    /// you would pass to the underlying constructor.
74    #[inline]
75    pub fn block_validation_context_for_connect_ibd<H: AsRef<BlockHeader>>(
76        recent_headers: Option<&[H]>,
77        network_time: u64,
78        network: Network,
79    ) -> BlockValidationContext {
80        BlockValidationContext::from_connect_block_ibd_args(
81            recent_headers,
82            network_time,
83            network,
84            None,
85            None,
86        )
87    }
88}
89pub mod mining {
90    pub use blvm_consensus::mining::*;
91}
92pub mod pow {
93    pub use blvm_consensus::pow::*;
94}
95
96pub mod witness {
97    pub use blvm_consensus::witness::*;
98}
99
100pub mod crypto {
101    pub use blvm_consensus::crypto::*;
102}
103
104pub mod transaction {
105    pub use blvm_consensus::transaction::*;
106}
107
108/// Script interpreter (consensus). Exposed so benches/node avoid a direct `blvm-consensus` dep where possible.
109pub mod script {
110    pub use blvm_consensus::script::*;
111}
112
113/// Transaction sighash / txid helpers from consensus.
114pub mod transaction_hash {
115    pub use blvm_consensus::transaction_hash::*;
116}
117
118/// Production-only batch hashing, preallocation, and related optimization helpers from consensus.
119#[cfg(feature = "production")]
120pub mod optimizations {
121    pub use blvm_consensus::optimizations::*;
122}
123
124/// Consensus constants (`blvm-primitives` / Orange Paper symbols) — same as `blvm_consensus::constants`.
125pub mod constants {
126    pub use blvm_consensus::constants::*;
127}
128
129pub mod bip113 {
130    pub use blvm_consensus::bip113::*;
131}
132
133pub mod bip_validation {
134    pub use blvm_consensus::bip_validation::*;
135}
136
137pub mod utxo_overlay {
138    pub use blvm_consensus::utxo_overlay::*;
139}
140
141pub mod version_bits {
142    pub use blvm_consensus::version_bits::*;
143}
144
145pub mod activation {
146    pub use blvm_consensus::activation::*;
147}
148
149/// Consensus runtime configuration from `blvm-consensus` (distinct from this crate's [`config`] module).
150pub mod consensus_config {
151    pub use blvm_consensus::config::*;
152}
153
154#[cfg(feature = "test-utils")]
155pub mod test_utils {
156    pub use blvm_consensus::test_utils::*;
157}
158
159/// Script opcodes (re-exported from consensus / primitives) for callers that should not name `blvm-consensus` directly.
160pub use blvm_consensus::opcodes;
161
162pub mod sigop {
163    pub use blvm_consensus::sigop::*;
164}
165
166#[cfg(feature = "utxo-commitments")]
167pub mod utxo_commitments;
168
169// Spam filter is always available (not behind utxo-commitments feature)
170pub mod spam_filter;
171pub mod serialization {
172    pub use blvm_consensus::serialization::*;
173}
174pub mod commons;
175pub mod network;
176/// Framed TCP P2P (mainnet magic, command allowlist supplied by the node). Not BIP324 v2 transport.
177pub mod node_tcp;
178
179pub use node_tcp::{ProtocolMessage, TcpFramedParser};
180pub mod p2p_commands;
181pub mod p2p_frame;
182pub mod p2p_framing;
183pub mod service_flags;
184pub mod varint;
185
186// BIP324: v2 encrypted transport
187#[cfg(all(
188    feature = "bip324",
189    any(target_arch = "x86_64", target_arch = "aarch64")
190))]
191pub mod v2_transport;
192
193// Re-export commonly used types for convenience
194pub use commons::{
195    BanListMessage,
196    // Filtered block types
197    FilterPreferences,
198    // Commons-only types kept in blvm-protocol for bridge layers
199    FilteredBlockMessage,
200    GetBanListMessage,
201    GetFilteredBlockMessage,
202    // UTXO proof types
203    GetUTXOProofMessage,
204    GetUTXOSetMessage,
205    // UTXO commitment protocol types
206    UTXOCommitment,
207    UTXOProofMessage,
208    UTXOSetMessage,
209};
210pub use config::{
211    ProtocolConfig, ProtocolFeaturesConfig, ProtocolValidationConfig, ServiceFlagsConfig,
212};
213pub use network::{BlockMessage, CompactBlockMessage, TxMessage};
214pub use service_flags::{commons as service_flags_commons, standard as service_flags_standard};
215// Wire format module - Bitcoin P2P wire protocol serialization
216pub mod wire;
217
218#[cfg(test)]
219mod bip155_serialization_tests;
220pub mod types {
221    pub use blvm_consensus::types::*;
222}
223// Re-export macros from blvm-consensus for convenience
224#[cfg(feature = "production")]
225pub use blvm_consensus::tx_inputs;
226#[cfg(not(feature = "production"))]
227pub use blvm_consensus::tx_inputs;
228#[cfg(feature = "production")]
229pub use blvm_consensus::tx_outputs;
230#[cfg(not(feature = "production"))]
231pub use blvm_consensus::tx_outputs;
232pub mod error;
233
234// Re-export feature and economic modules for convenience
235pub use economic::EconomicParameters;
236pub use features::{ActivationMethod, FeatureActivation, FeatureContext, FeatureRegistry};
237
238pub mod config;
239pub mod economic;
240pub mod features;
241pub mod genesis;
242pub mod network_params;
243pub mod validation;
244pub mod variants;
245
246// Protocol-level BIP implementations
247pub mod address; // BIP173/350/351: Bech32/Bech32m address encoding
248#[cfg(feature = "ctv")]
249pub mod bip119 {
250    pub use blvm_consensus::bip119::*;
251}
252pub mod bip152; // BIP152: Compact block relay (wire types)
253pub mod bip157; // BIP157: Client-side block filtering network protocol
254pub mod bip158; // BIP158: Compact block filters
255pub mod payment; // BIP70: Payment protocol (P2P variant)
256pub mod time;
257
258/// Bitcoin Protocol Engine
259///
260/// Provides protocol abstraction for different Bitcoin variants and evolution.
261/// Acts as a bridge between blvm-consensus (pure math) and blvm-node (implementation).
262pub struct BitcoinProtocolEngine {
263    consensus: ConsensusProof,
264    protocol_version: ProtocolVersion,
265    network_params: NetworkParameters,
266    config: ProtocolConfig,
267}
268
269/// Bitcoin protocol versions
270#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
271pub enum ProtocolVersion {
272    /// Current Bitcoin mainnet protocol
273    BitcoinV1,
274    /// Bitcoin testnet protocol
275    Testnet3,
276    /// Regression test network protocol
277    Regtest,
278}
279
280/// Network parameters for different Bitcoin variants
281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
282pub struct NetworkParameters {
283    /// Network magic bytes for P2P protocol
284    pub magic_bytes: [u8; 4],
285    /// Default P2P port
286    pub default_port: u16,
287    /// Genesis block for this network
288    pub genesis_block: Block,
289    /// Maximum proof-of-work target
290    pub max_target: u32,
291    /// Block subsidy halving interval
292    pub halving_interval: u64,
293    /// Network name for identification
294    pub network_name: String,
295    /// Whether this is a test network
296    pub is_testnet: bool,
297}
298
299impl BitcoinProtocolEngine {
300    /// Create a new protocol engine for the specified variant with default configuration
301    pub fn new(version: ProtocolVersion) -> Result<Self> {
302        Self::with_config(version, ProtocolConfig::default())
303    }
304
305    /// Create a new protocol engine with custom configuration
306    pub fn with_config(version: ProtocolVersion, config: ProtocolConfig) -> Result<Self> {
307        let consensus = ConsensusProof::new();
308        let network_params = NetworkParameters::for_version(version)?;
309
310        Ok(BitcoinProtocolEngine {
311            consensus,
312            protocol_version: version,
313            network_params,
314            config,
315        })
316    }
317
318    /// Get the protocol configuration
319    pub fn get_config(&self) -> &ProtocolConfig {
320        &self.config
321    }
322
323    /// Get mutable reference to protocol configuration
324    pub fn get_config_mut(&mut self) -> &mut ProtocolConfig {
325        &mut self.config
326    }
327
328    /// Get the current protocol version
329    pub fn get_protocol_version(&self) -> ProtocolVersion {
330        self.protocol_version
331    }
332
333    /// Get network parameters for this protocol
334    pub fn get_network_params(&self) -> &NetworkParameters {
335        &self.network_params
336    }
337
338    /// Validate a block using this protocol's rules.
339    ///
340    /// **Deprecated**: this API builds empty witness vectors for every input and passes
341    /// `None` for the time context, so:
342    ///   - SegWit and Taproot scripts are validated against empty witness stacks (always fail
343    ///     on witness-dependent scripts).
344    ///   - Time-dependent checks fall back to wall-clock time instead of the median-time-past
345    ///     computed from actual block headers.
346    ///
347    /// Prefer [`validate_and_connect_block`], which accepts explicit witnesses and a
348    /// [`validation::ProtocolValidationContext`] with a correct `median_time_past`.
349    #[deprecated(
350        since = "0.1.0",
351        note = "Use validate_and_connect_block with explicit witnesses and a ProtocolValidationContext"
352    )]
353    pub fn validate_block(
354        &self,
355        block: &Block,
356        utxos: &UtxoSet,
357        height: u64,
358    ) -> Result<ValidationResult> {
359        let network = match self.protocol_version {
360            ProtocolVersion::BitcoinV1 => types::Network::Mainnet,
361            ProtocolVersion::Testnet3 => types::Network::Testnet,
362            ProtocolVersion::Regtest => types::Network::Regtest,
363        };
364        // Empty witnesses: SegWit/Taproot inputs will fail witness-dependent checks.
365        // This is acceptable here only because callers of this deprecated API either
366        // use pre-SegWit blocks or do not need full witness validation.
367        let witnesses: Vec<Vec<blvm_consensus::segwit::Witness>> = block
368            .transactions
369            .iter()
370            .map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
371            .collect();
372        // Use current wall-clock as the network time context.  For accurate median-time-past
373        // validation, use validate_and_connect_block with an explicit context.
374        let now_secs = std::time::SystemTime::now()
375            .duration_since(std::time::UNIX_EPOCH)
376            .unwrap_or_default()
377            .as_secs();
378        let time_ctx = blvm_consensus::types::TimeContext {
379            network_time: now_secs,
380            // Without prior block history we approximate MTP as the current wall clock;
381            // callers with a real chain tip should use validate_and_connect_block.
382            median_time_past: now_secs,
383        };
384        let (result, _) = self
385            .consensus
386            .validate_block_with_time_context(
387                block,
388                &witnesses,
389                utxos.clone(),
390                height,
391                Some(time_ctx),
392                network,
393            )
394            .map_err(ProtocolError::from)?;
395        Ok(result)
396    }
397
398    /// Validate a transaction using this protocol's rules
399    pub fn validate_transaction(&self, tx: &Transaction) -> Result<ValidationResult> {
400        self.consensus
401            .validate_transaction(tx)
402            .map_err(ProtocolError::from)
403    }
404
405    /// Validate block with protocol rules and update UTXO set
406    ///
407    /// This method combines protocol validation (size limits, feature flags)
408    /// with consensus validation and UTXO set updates. This is the recommended
409    /// method for node implementations that need both validation and state updates.
410    ///
411    /// # Arguments
412    ///
413    /// * `block` - The block to validate and connect
414    /// * `witnesses` - Witness data for each transaction in the block
415    /// * `utxos` - Current UTXO set (will be cloned, not mutated)
416    /// * `height` - Current block height
417    /// * `recent_headers` - Optional recent block headers for median time-past calculation (BIP113)
418    /// * `context` - Protocol validation context
419    ///
420    /// # Returns
421    ///
422    /// Returns `(ValidationResult, UtxoSet)` where:
423    /// - `ValidationResult` indicates if the block is valid
424    /// - `UtxoSet` is the updated UTXO set after applying the block's transactions
425    ///
426    /// # Example
427    ///
428    /// ```rust,no_run
429    /// use blvm_protocol::{BitcoinProtocolEngine, ProtocolVersion};
430    /// use blvm_protocol::validation::ProtocolValidationContext;
431    /// use blvm_protocol::{Block, UtxoSet};
432    ///
433    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
434    /// let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1)?;
435    /// let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 0)?;
436    /// // Create a test block
437    /// let block = Block {
438    ///     header: blvm_consensus::BlockHeader {
439    ///         version: 1,
440    ///         prev_block_hash: [0u8; 32],
441    ///         merkle_root: [0u8; 32],
442    ///         timestamp: 1231006505,
443    ///         bits: 0x1d00ffff,
444    ///         nonce: 0,
445    ///     },
446    ///     transactions: vec![].into_boxed_slice(),
447    /// };
448    /// let witnesses = vec![];
449    /// let utxos = UtxoSet::default();
450    ///
451    /// let (result, new_utxo_set) = engine.validate_and_connect_block(
452    ///     &block,
453    ///     &witnesses,
454    ///     &utxos,
455    ///     0,
456    ///     None,
457    ///     &context,
458    /// )?;
459    /// # Ok(())
460    /// # }
461    /// ```
462    pub fn validate_and_connect_block(
463        &self,
464        block: &Block,
465        witnesses: &[Vec<segwit::Witness>], // CRITICAL FIX: Changed from &[Witness] to &[Vec<Witness>]
466        // witnesses is now Vec<Vec<Witness>> where each Vec<Witness> is for one transaction
467        // and each Witness is for one input
468        utxos: &UtxoSet,
469        height: u64,
470        recent_headers: Option<&[BlockHeader]>,
471        context: &validation::ProtocolValidationContext,
472    ) -> Result<(ValidationResult, UtxoSet)> {
473        // First, protocol validation with witnesses for accurate BIP141 weight check.
474        let protocol_result = self
475            .validate_block_with_protocol_and_witnesses(block, witnesses, utxos, height, context)?;
476        if !matches!(protocol_result, ValidationResult::Valid) {
477            return Ok((protocol_result, utxos.clone()));
478        }
479
480        // Then, consensus validation with UTXO update
481        // Convert protocol version to network type
482        let network = match self.protocol_version {
483            ProtocolVersion::BitcoinV1 => types::Network::Mainnet,
484            ProtocolVersion::Testnet3 => types::Network::Testnet,
485            ProtocolVersion::Regtest => types::Network::Regtest,
486        };
487        let network_time = crate::time::current_timestamp();
488        let context = crate::block::block_validation_context_for_connect_ibd(
489            recent_headers,
490            network_time,
491            network,
492        );
493        let (result, new_utxo_set, _undo_log) = blvm_consensus::block::connect_block(
494            block,
495            witnesses,
496            utxos.clone(),
497            height,
498            &context,
499        )?;
500
501        Ok((result, new_utxo_set))
502    }
503
504    /// Check if this protocol supports a specific feature
505    pub fn supports_feature(&self, feature: &str) -> bool {
506        match self.protocol_version {
507            ProtocolVersion::BitcoinV1 => {
508                matches!(feature, "segwit" | "taproot" | "rbf" | "ctv")
509            }
510            ProtocolVersion::Testnet3 => {
511                matches!(feature, "segwit" | "taproot" | "rbf" | "ctv")
512            }
513            ProtocolVersion::Regtest => {
514                matches!(
515                    feature,
516                    "segwit" | "taproot" | "rbf" | "ctv" | "fast_mining"
517                )
518            }
519        }
520    }
521
522    /// Check if a feature is active at a specific block height and timestamp
523    pub fn is_feature_active(&self, feature: &str, height: u64, timestamp: u64) -> bool {
524        let registry = features::FeatureRegistry::for_protocol(self.protocol_version);
525        registry.is_feature_active(feature, height, timestamp)
526    }
527
528    /// Get economic parameters for this protocol
529    pub fn get_economic_parameters(&self) -> economic::EconomicParameters {
530        economic::EconomicParameters::for_protocol(self.protocol_version)
531    }
532
533    /// Get feature activation registry for this protocol
534    pub fn get_feature_registry(&self) -> features::FeatureRegistry {
535        features::FeatureRegistry::for_protocol(self.protocol_version)
536    }
537
538    /// Create a feature context for a specific block height and timestamp
539    /// This consolidates all feature activation checks into a single context
540    pub fn feature_context(&self, height: u64, timestamp: u64) -> features::FeatureContext {
541        let registry = features::FeatureRegistry::for_protocol(self.protocol_version);
542        registry.create_context(height, timestamp)
543    }
544}
545
546impl NetworkParameters {
547    /// Create network parameters for a specific protocol version
548    pub fn for_version(version: ProtocolVersion) -> Result<Self> {
549        match version {
550            ProtocolVersion::BitcoinV1 => Self::mainnet(),
551            ProtocolVersion::Testnet3 => Self::testnet(),
552            ProtocolVersion::Regtest => Self::regtest(),
553        }
554    }
555
556    /// Bitcoin mainnet parameters
557    pub fn mainnet() -> Result<Self> {
558        Ok(NetworkParameters {
559            magic_bytes: [0xf9, 0xbe, 0xb4, 0xd9], // Bitcoin mainnet magic
560            default_port: 8333,
561            genesis_block: genesis::mainnet_genesis(),
562            max_target: 0x1d00ffff,
563            halving_interval: 210000,
564            network_name: "mainnet".to_string(),
565            is_testnet: false,
566        })
567    }
568
569    /// Bitcoin testnet parameters
570    pub fn testnet() -> Result<Self> {
571        Ok(NetworkParameters {
572            magic_bytes: [0x0b, 0x11, 0x09, 0x07], // Bitcoin testnet magic
573            default_port: 18333,
574            genesis_block: genesis::testnet_genesis(),
575            max_target: 0x1d00ffff,
576            halving_interval: 210000,
577            network_name: "testnet".to_string(),
578            is_testnet: true,
579        })
580    }
581
582    /// Bitcoin regtest parameters
583    pub fn regtest() -> Result<Self> {
584        Ok(NetworkParameters {
585            magic_bytes: [0xfa, 0xbf, 0xb5, 0xda], // Bitcoin regtest magic
586            default_port: 18444,
587            genesis_block: genesis::regtest_genesis(),
588            max_target: 0x207fffff, // Easier difficulty for testing
589            halving_interval: 150,  // Faster halving for testing
590            network_name: "regtest".to_string(),
591            is_testnet: true,
592        })
593    }
594}
595
596#[cfg(test)]
597mod tests {
598    use super::*;
599    use blvm_consensus::types::{BlockHeader, OutPoint, TransactionInput, TransactionOutput};
600
601    #[test]
602    fn test_blvm_protocol_creation() {
603        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
604        assert_eq!(engine.get_protocol_version(), ProtocolVersion::BitcoinV1);
605        assert_eq!(engine.get_network_params().network_name, "mainnet");
606    }
607
608    #[test]
609    fn test_blvm_protocol_creation_all_variants() {
610        // Test mainnet
611        let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
612        assert_eq!(mainnet.get_protocol_version(), ProtocolVersion::BitcoinV1);
613        assert_eq!(mainnet.get_network_params().network_name, "mainnet");
614        assert!(!mainnet.get_network_params().is_testnet);
615
616        // Test testnet
617        let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
618        assert_eq!(testnet.get_protocol_version(), ProtocolVersion::Testnet3);
619        assert_eq!(testnet.get_network_params().network_name, "testnet");
620        assert!(testnet.get_network_params().is_testnet);
621
622        // Test regtest
623        let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
624        assert_eq!(regtest.get_protocol_version(), ProtocolVersion::Regtest);
625        assert_eq!(regtest.get_network_params().network_name, "regtest");
626        assert!(regtest.get_network_params().is_testnet);
627    }
628
629    #[test]
630    fn test_network_parameters() {
631        let mainnet = NetworkParameters::mainnet().unwrap();
632        assert_eq!(mainnet.magic_bytes, [0xf9, 0xbe, 0xb4, 0xd9]);
633        assert_eq!(mainnet.default_port, 8333);
634        assert!(!mainnet.is_testnet);
635
636        let testnet = NetworkParameters::testnet().unwrap();
637        assert_eq!(testnet.magic_bytes, [0x0b, 0x11, 0x09, 0x07]);
638        assert_eq!(testnet.default_port, 18333);
639        assert!(testnet.is_testnet);
640
641        let regtest = NetworkParameters::regtest().unwrap();
642        assert_eq!(regtest.magic_bytes, [0xfa, 0xbf, 0xb5, 0xda]);
643        assert_eq!(regtest.default_port, 18444);
644        assert!(regtest.is_testnet);
645    }
646
647    #[test]
648    fn test_network_parameters_consistency() {
649        let mainnet = NetworkParameters::mainnet().unwrap();
650        assert_eq!(mainnet.max_target, 0x1d00ffff);
651        assert_eq!(mainnet.halving_interval, 210000);
652
653        let testnet = NetworkParameters::testnet().unwrap();
654        assert_eq!(testnet.max_target, 0x1d00ffff);
655        assert_eq!(testnet.halving_interval, 210000);
656
657        let regtest = NetworkParameters::regtest().unwrap();
658        assert_eq!(regtest.max_target, 0x207fffff); // Easier difficulty
659        assert_eq!(regtest.halving_interval, 150); // Faster halving
660    }
661
662    #[test]
663    fn test_feature_support() {
664        let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
665        assert!(mainnet.supports_feature("segwit"));
666        assert!(mainnet.supports_feature("taproot"));
667        assert!(mainnet.supports_feature("rbf"));
668        assert!(mainnet.supports_feature("ctv"));
669        assert!(!mainnet.supports_feature("fast_mining"));
670        assert!(!mainnet.supports_feature("nonexistent"));
671
672        let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
673        assert!(testnet.supports_feature("segwit"));
674        assert!(testnet.supports_feature("taproot"));
675        assert!(testnet.supports_feature("rbf"));
676        assert!(testnet.supports_feature("ctv"));
677        assert!(!testnet.supports_feature("fast_mining"));
678
679        let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
680        assert!(regtest.supports_feature("segwit"));
681        assert!(regtest.supports_feature("taproot"));
682        assert!(regtest.supports_feature("rbf"));
683        assert!(regtest.supports_feature("ctv"));
684        assert!(regtest.supports_feature("fast_mining"));
685    }
686
687    #[test]
688    fn test_block_validation_empty_utxos() {
689        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
690        let utxos = UtxoSet::default();
691
692        // Create a simple block with just a coinbase transaction
693        let coinbase_tx = Transaction {
694            version: 1,
695            inputs: blvm_consensus::tx_inputs![TransactionInput {
696                prevout: OutPoint {
697                    hash: [0u8; 32],
698                    index: 0xffffffff,
699                },
700                script_sig: vec![0x01, 0x00], // Height 0
701                sequence: 0xffffffff,
702            }],
703            outputs: blvm_consensus::tx_outputs![TransactionOutput {
704                value: 50_0000_0000,
705                script_pubkey: vec![
706                    blvm_consensus::opcodes::OP_DUP,
707                    blvm_consensus::opcodes::OP_HASH160,
708                    blvm_consensus::opcodes::PUSH_20_BYTES,
709                    0x00,
710                    0x00,
711                    0x00,
712                    0x00,
713                    0x00,
714                    0x00,
715                    0x00,
716                    0x00,
717                    0x00,
718                    0x00,
719                    0x00,
720                    0x00,
721                    0x00,
722                    0x00,
723                    0x00,
724                    0x00,
725                    0x00,
726                    0x00,
727                    0x00,
728                    0x00,
729                    blvm_consensus::opcodes::OP_EQUALVERIFY,
730                    blvm_consensus::opcodes::OP_CHECKSIG,
731                ], // P2PKH
732            }],
733            lock_time: 0,
734        };
735
736        // Calculate proper merkle root
737        let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
738            .expect("Should calculate merkle root");
739
740        let block = Block {
741            header: BlockHeader {
742                version: 1,
743                prev_block_hash: [0u8; 32],
744                merkle_root,
745                timestamp: 1231006505,
746                bits: 0x1d00ffff,
747                nonce: 0,
748            },
749            transactions: vec![coinbase_tx].into_boxed_slice(),
750        };
751
752        // This should pass validation for a genesis block
753        let result = engine.validate_block(&block, &utxos, 0);
754        assert!(result.is_ok());
755    }
756
757    #[test]
758    fn test_transaction_validation() {
759        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
760
761        // Create a simple transaction
762        let tx = Transaction {
763            version: 1,
764            inputs: vec![TransactionInput {
765                prevout: OutPoint {
766                    hash: [0u8; 32],
767                    index: 0,
768                },
769                script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
770                sequence: 0xffffffff,
771            }]
772            .into(),
773            outputs: vec![TransactionOutput {
774                value: 50_0000_0000,
775                script_pubkey: vec![
776                    blvm_consensus::opcodes::OP_DUP,
777                    blvm_consensus::opcodes::OP_HASH160,
778                    blvm_consensus::opcodes::PUSH_20_BYTES,
779                    0x00,
780                    0x00,
781                    0x00,
782                    0x00,
783                    0x00,
784                    0x00,
785                    0x00,
786                    0x00,
787                    0x00,
788                    0x00,
789                    0x00,
790                    0x00,
791                    0x00,
792                    0x00,
793                    0x00,
794                    0x00,
795                    0x00,
796                    0x00,
797                    0x00,
798                    0x00,
799                    blvm_consensus::opcodes::OP_EQUALVERIFY,
800                    blvm_consensus::opcodes::OP_CHECKSIG,
801                ], // P2PKH
802            }]
803            .into(),
804            lock_time: 0,
805        };
806
807        let result = engine.validate_transaction(&tx);
808        assert!(result.is_ok());
809    }
810
811    #[test]
812    fn test_cross_protocol_validation() {
813        let mainnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
814        let testnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
815
816        // Both engines should support the same features
817        assert_eq!(
818            mainnet_engine.supports_feature("segwit"),
819            testnet_engine.supports_feature("segwit")
820        );
821        assert_eq!(
822            mainnet_engine.supports_feature("taproot"),
823            testnet_engine.supports_feature("taproot")
824        );
825
826        // But they should have different network parameters
827        assert_ne!(
828            mainnet_engine.get_network_params().magic_bytes,
829            testnet_engine.get_network_params().magic_bytes
830        );
831        assert_ne!(
832            mainnet_engine.get_network_params().default_port,
833            testnet_engine.get_network_params().default_port
834        );
835    }
836
837    #[test]
838    fn test_protocol_version_switching() {
839        // Test that we can create engines for different protocol versions
840        let versions = vec![
841            ProtocolVersion::BitcoinV1,
842            ProtocolVersion::Testnet3,
843            ProtocolVersion::Regtest,
844        ];
845
846        for version in versions {
847            let engine = BitcoinProtocolEngine::new(version).unwrap();
848            assert_eq!(engine.get_protocol_version(), version);
849        }
850    }
851
852    #[test]
853    fn test_network_parameters_serialization() {
854        let mainnet = NetworkParameters::mainnet().unwrap();
855        let testnet = NetworkParameters::testnet().unwrap();
856        let regtest = NetworkParameters::regtest().unwrap();
857
858        // Test that parameters can be serialized and deserialized
859        let mainnet_json = serde_json::to_string(&mainnet).unwrap();
860        let mainnet_deserialized: NetworkParameters = serde_json::from_str(&mainnet_json).unwrap();
861        assert_eq!(mainnet.magic_bytes, mainnet_deserialized.magic_bytes);
862        assert_eq!(mainnet.default_port, mainnet_deserialized.default_port);
863        assert_eq!(mainnet.network_name, mainnet_deserialized.network_name);
864        assert_eq!(mainnet.is_testnet, mainnet_deserialized.is_testnet);
865
866        let testnet_json = serde_json::to_string(&testnet).unwrap();
867        let testnet_deserialized: NetworkParameters = serde_json::from_str(&testnet_json).unwrap();
868        assert_eq!(testnet.magic_bytes, testnet_deserialized.magic_bytes);
869
870        let regtest_json = serde_json::to_string(&regtest).unwrap();
871        let regtest_deserialized: NetworkParameters = serde_json::from_str(&regtest_json).unwrap();
872        assert_eq!(regtest.magic_bytes, regtest_deserialized.magic_bytes);
873    }
874
875    #[test]
876    fn test_protocol_version_serialization() {
877        let versions = vec![
878            ProtocolVersion::BitcoinV1,
879            ProtocolVersion::Testnet3,
880            ProtocolVersion::Regtest,
881        ];
882
883        for version in versions {
884            let json = serde_json::to_string(&version).unwrap();
885            let deserialized: ProtocolVersion = serde_json::from_str(&json).unwrap();
886            assert_eq!(version, deserialized);
887        }
888    }
889
890    #[test]
891    fn test_network_parameters_equality() {
892        let mainnet1 = NetworkParameters::mainnet().unwrap();
893        let mainnet2 = NetworkParameters::mainnet().unwrap();
894        let testnet = NetworkParameters::testnet().unwrap();
895
896        assert_eq!(mainnet1, mainnet2);
897        assert_ne!(mainnet1, testnet);
898    }
899
900    #[test]
901    fn test_protocol_version_equality() {
902        assert_eq!(ProtocolVersion::BitcoinV1, ProtocolVersion::BitcoinV1);
903        assert_ne!(ProtocolVersion::BitcoinV1, ProtocolVersion::Testnet3);
904        assert_ne!(ProtocolVersion::Testnet3, ProtocolVersion::Regtest);
905    }
906
907    #[test]
908    fn test_feature_activation_by_height() {
909        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
910
911        // SegWit activates at block 481,824
912        assert!(!engine.is_feature_active("segwit", 481_823, 1503539000));
913        assert!(engine.is_feature_active("segwit", 481_824, 1503539857));
914        assert!(engine.is_feature_active("segwit", 500_000, 1504000000));
915
916        // Taproot activates at block 709,632
917        assert!(!engine.is_feature_active("taproot", 709_631, 1636934000));
918        assert!(engine.is_feature_active("taproot", 709_632, 1636934400));
919        assert!(engine.is_feature_active("taproot", 800_000, 1640000000));
920    }
921
922    #[test]
923    fn test_economic_parameters_access() {
924        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
925        let params = engine.get_economic_parameters();
926
927        assert_eq!(params.initial_subsidy, 50_0000_0000);
928        assert_eq!(params.halving_interval, 210_000);
929        assert_eq!(params.coinbase_maturity, 100);
930
931        // Test block subsidy calculation
932        assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
933        assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
934    }
935
936    #[test]
937    fn test_feature_registry_access() {
938        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
939        let registry = engine.get_feature_registry();
940
941        assert!(registry.get_feature("segwit").is_some());
942        assert!(registry.get_feature("taproot").is_some());
943        assert!(registry.get_feature("nonexistent").is_none());
944
945        let features = registry.list_features();
946        assert!(features.contains(&"segwit".to_string()));
947        assert!(features.contains(&"taproot".to_string()));
948    }
949}