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    /// Signet test network (BIP325 block-solution challenge)
279    Signet,
280}
281
282impl ProtocolVersion {
283    /// Map protocol variant to consensus `Network` (blvm-primitives / blvm-consensus).
284    pub fn consensus_network(self) -> types::Network {
285        match self {
286            ProtocolVersion::BitcoinV1 => types::Network::Mainnet,
287            ProtocolVersion::Testnet3 => types::Network::Testnet,
288            ProtocolVersion::Regtest => types::Network::Regtest,
289            ProtocolVersion::Signet => types::Network::Signet,
290        }
291    }
292}
293
294/// Network parameters for different Bitcoin variants
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
296pub struct NetworkParameters {
297    /// Network magic bytes for P2P protocol
298    pub magic_bytes: [u8; 4],
299    /// Default P2P port
300    pub default_port: u16,
301    /// Genesis block for this network
302    pub genesis_block: Block,
303    /// Maximum proof-of-work target
304    pub max_target: u32,
305    /// Block subsidy halving interval
306    pub halving_interval: u64,
307    /// Network name for identification
308    pub network_name: String,
309    /// Whether this is a test network
310    pub is_testnet: bool,
311    /// Optional BIP325 signet challenge script override (hex-decoded bytes stored here).
312    #[serde(default, skip_serializing_if = "Option::is_none")]
313    pub signet_challenge: Option<ByteString>,
314}
315
316impl BitcoinProtocolEngine {
317    /// Create a new protocol engine for the specified variant with default configuration
318    pub fn new(version: ProtocolVersion) -> Result<Self> {
319        Self::with_config(version, ProtocolConfig::default())
320    }
321
322    /// Create a new protocol engine with custom configuration
323    pub fn with_config(version: ProtocolVersion, config: ProtocolConfig) -> Result<Self> {
324        let consensus = ConsensusProof::new();
325        let network_params = NetworkParameters::for_version(version)?;
326
327        Ok(BitcoinProtocolEngine {
328            consensus,
329            protocol_version: version,
330            network_params,
331            config,
332        })
333    }
334
335    /// Get the protocol configuration
336    pub fn get_config(&self) -> &ProtocolConfig {
337        &self.config
338    }
339
340    /// Get mutable reference to protocol configuration
341    pub fn get_config_mut(&mut self) -> &mut ProtocolConfig {
342        &mut self.config
343    }
344
345    /// Get the current protocol version
346    pub fn get_protocol_version(&self) -> ProtocolVersion {
347        self.protocol_version
348    }
349
350    /// Get network parameters for this protocol
351    pub fn get_network_params(&self) -> &NetworkParameters {
352        &self.network_params
353    }
354
355    /// Override the BIP325 signet challenge script (custom signet networks).
356    pub fn with_signet_challenge(mut self, script: Option<Vec<u8>>) -> Self {
357        self.network_params.signet_challenge = script;
358        self
359    }
360
361    /// Build consensus [`BlockValidationContext`] including signet challenge from network params.
362    pub fn connect_block_validation_context<H: AsRef<BlockHeader>>(
363        &self,
364        recent_headers: Option<&[H]>,
365        network_time: u64,
366    ) -> block::BlockValidationContext {
367        let mut ctx = block::block_validation_context_for_connect_ibd(
368            recent_headers,
369            network_time,
370            self.protocol_version.consensus_network(),
371        );
372        ctx.signet_challenge = self.network_params.signet_challenge.clone();
373        ctx
374    }
375
376    /// Validate a block using this protocol's rules.
377    ///
378    /// **Deprecated**: this API builds empty witness vectors for every input and passes
379    /// `None` for the time context, so:
380    ///   - SegWit and Taproot scripts are validated against empty witness stacks (always fail
381    ///     on witness-dependent scripts).
382    ///   - Time-dependent checks fall back to wall-clock time instead of the median-time-past
383    ///     computed from actual block headers.
384    ///
385    /// Prefer [`validate_and_connect_block`], which accepts explicit witnesses and a
386    /// [`validation::ProtocolValidationContext`] with a correct `median_time_past`.
387    #[deprecated(
388        since = "0.1.0",
389        note = "Use validate_and_connect_block with explicit witnesses and a ProtocolValidationContext"
390    )]
391    pub fn validate_block(
392        &self,
393        block: &Block,
394        utxos: &UtxoSet,
395        height: u64,
396    ) -> Result<ValidationResult> {
397        let network = self.protocol_version.consensus_network();
398        // Empty witnesses: SegWit/Taproot inputs will fail witness-dependent checks.
399        // This is acceptable here only because callers of this deprecated API either
400        // use pre-SegWit blocks or do not need full witness validation.
401        let witnesses: Vec<Vec<blvm_consensus::segwit::Witness>> = block
402            .transactions
403            .iter()
404            .map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
405            .collect();
406        // Use current wall-clock as the network time context.  For accurate median-time-past
407        // validation, use validate_and_connect_block with an explicit context.
408        let now_secs = std::time::SystemTime::now()
409            .duration_since(std::time::UNIX_EPOCH)
410            .unwrap_or_default()
411            .as_secs();
412        let time_ctx = blvm_consensus::types::TimeContext {
413            network_time: now_secs,
414            // Without prior block history we approximate MTP as the current wall clock;
415            // callers with a real chain tip should use validate_and_connect_block.
416            median_time_past: now_secs,
417        };
418        let (result, _) = self
419            .consensus
420            .validate_block_with_time_context(
421                block,
422                &witnesses,
423                utxos.clone(),
424                height,
425                Some(time_ctx),
426                network,
427            )
428            .map_err(ProtocolError::from)?;
429        Ok(result)
430    }
431
432    /// Validate a transaction using this protocol's rules
433    pub fn validate_transaction(&self, tx: &Transaction) -> Result<ValidationResult> {
434        self.consensus
435            .validate_transaction(tx)
436            .map_err(ProtocolError::from)
437    }
438
439    /// Validate block with protocol rules and update UTXO set
440    ///
441    /// This method combines protocol validation (size limits, feature flags)
442    /// with consensus validation and UTXO set updates. This is the recommended
443    /// method for node implementations that need both validation and state updates.
444    ///
445    /// # Arguments
446    ///
447    /// * `block` - The block to validate and connect
448    /// * `witnesses` - Witness data for each transaction in the block
449    /// * `utxos` - Current UTXO set (will be cloned, not mutated)
450    /// * `height` - Current block height
451    /// * `recent_headers` - Optional recent block headers for median time-past calculation (BIP113)
452    /// * `context` - Protocol validation context
453    ///
454    /// # Returns
455    ///
456    /// Returns `(ValidationResult, UtxoSet)` where:
457    /// - `ValidationResult` indicates if the block is valid
458    /// - `UtxoSet` is the updated UTXO set after applying the block's transactions
459    ///
460    /// # Example
461    ///
462    /// ```rust,no_run
463    /// use blvm_protocol::{BitcoinProtocolEngine, ProtocolVersion};
464    /// use blvm_protocol::validation::ProtocolValidationContext;
465    /// use blvm_protocol::{Block, UtxoSet};
466    ///
467    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
468    /// let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1)?;
469    /// let context = ProtocolValidationContext::new(ProtocolVersion::BitcoinV1, 0)?;
470    /// // Create a test block
471    /// let block = Block {
472    ///     header: blvm_consensus::BlockHeader {
473    ///         version: 1,
474    ///         prev_block_hash: [0u8; 32],
475    ///         merkle_root: [0u8; 32],
476    ///         timestamp: 1231006505,
477    ///         bits: 0x1d00ffff,
478    ///         nonce: 0,
479    ///     },
480    ///     transactions: vec![].into_boxed_slice(),
481    /// };
482    /// let witnesses = vec![];
483    /// let utxos = UtxoSet::default();
484    ///
485    /// let (result, new_utxo_set) = engine.validate_and_connect_block(
486    ///     &block,
487    ///     &witnesses,
488    ///     &utxos,
489    ///     0,
490    ///     None,
491    ///     &context,
492    /// )?;
493    /// # Ok(())
494    /// # }
495    /// ```
496    pub fn validate_and_connect_block(
497        &self,
498        block: &Block,
499        witnesses: &[Vec<segwit::Witness>], // CRITICAL FIX: Changed from &[Witness] to &[Vec<Witness>]
500        // witnesses is now Vec<Vec<Witness>> where each Vec<Witness> is for one transaction
501        // and each Witness is for one input
502        utxos: &UtxoSet,
503        height: u64,
504        recent_headers: Option<&[BlockHeader]>,
505        context: &validation::ProtocolValidationContext,
506    ) -> Result<(ValidationResult, UtxoSet)> {
507        // First, protocol validation with witnesses for accurate BIP141 weight check.
508        let protocol_result = self
509            .validate_block_with_protocol_and_witnesses(block, witnesses, utxos, height, context)?;
510        if !matches!(protocol_result, ValidationResult::Valid) {
511            return Ok((protocol_result, utxos.clone()));
512        }
513
514        // Then, consensus validation with UTXO update
515        let network_time = crate::time::current_timestamp();
516        let context = self.connect_block_validation_context(recent_headers, network_time);
517        let (result, new_utxo_set, _undo_log) = blvm_consensus::block::connect_block(
518            block,
519            witnesses,
520            utxos.clone(),
521            height,
522            &context,
523        )?;
524
525        Ok((result, new_utxo_set))
526    }
527
528    /// Check if this protocol supports a specific feature
529    pub fn supports_feature(&self, feature: &str) -> bool {
530        match self.protocol_version {
531            ProtocolVersion::BitcoinV1 => {
532                matches!(feature, "segwit" | "taproot" | "rbf" | "ctv")
533            }
534            ProtocolVersion::Testnet3 => {
535                matches!(feature, "segwit" | "taproot" | "rbf" | "ctv")
536            }
537            ProtocolVersion::Regtest => {
538                matches!(
539                    feature,
540                    "segwit" | "taproot" | "rbf" | "ctv" | "fast_mining"
541                )
542            }
543            ProtocolVersion::Signet => {
544                matches!(feature, "segwit" | "taproot" | "rbf" | "ctv" | "signet")
545            }
546        }
547    }
548
549    /// Check if a feature is active at a specific block height and timestamp
550    pub fn is_feature_active(&self, feature: &str, height: u64, timestamp: u64) -> bool {
551        let registry = features::FeatureRegistry::for_protocol(self.protocol_version);
552        registry.is_feature_active(feature, height, timestamp)
553    }
554
555    /// Get economic parameters for this protocol
556    pub fn get_economic_parameters(&self) -> economic::EconomicParameters {
557        economic::EconomicParameters::for_protocol(self.protocol_version)
558    }
559
560    /// Get feature activation registry for this protocol
561    pub fn get_feature_registry(&self) -> features::FeatureRegistry {
562        features::FeatureRegistry::for_protocol(self.protocol_version)
563    }
564
565    /// Create a feature context for a specific block height and timestamp
566    /// This consolidates all feature activation checks into a single context
567    pub fn feature_context(&self, height: u64, timestamp: u64) -> features::FeatureContext {
568        let registry = features::FeatureRegistry::for_protocol(self.protocol_version);
569        registry.create_context(height, timestamp)
570    }
571}
572
573impl NetworkParameters {
574    /// Create network parameters for a specific protocol version
575    pub fn for_version(version: ProtocolVersion) -> Result<Self> {
576        match version {
577            ProtocolVersion::BitcoinV1 => Self::mainnet(),
578            ProtocolVersion::Testnet3 => Self::testnet(),
579            ProtocolVersion::Regtest => Self::regtest(),
580            ProtocolVersion::Signet => Self::signet(),
581        }
582    }
583
584    /// Bitcoin mainnet parameters
585    pub fn mainnet() -> Result<Self> {
586        Ok(NetworkParameters {
587            magic_bytes: [0xf9, 0xbe, 0xb4, 0xd9], // Bitcoin mainnet magic
588            default_port: 8333,
589            genesis_block: genesis::mainnet_genesis(),
590            max_target: 0x1d00ffff,
591            halving_interval: 210000,
592            network_name: "mainnet".to_string(),
593            is_testnet: false,
594            signet_challenge: None,
595        })
596    }
597
598    /// Bitcoin testnet parameters
599    pub fn testnet() -> Result<Self> {
600        Ok(NetworkParameters {
601            magic_bytes: [0x0b, 0x11, 0x09, 0x07], // Bitcoin testnet magic
602            default_port: 18333,
603            genesis_block: genesis::testnet_genesis(),
604            max_target: 0x1d00ffff,
605            halving_interval: 210000,
606            network_name: "testnet".to_string(),
607            is_testnet: true,
608            signet_challenge: None,
609        })
610    }
611
612    /// Bitcoin regtest parameters
613    pub fn regtest() -> Result<Self> {
614        Ok(NetworkParameters {
615            magic_bytes: [0xfa, 0xbf, 0xb5, 0xda], // Bitcoin regtest magic
616            default_port: 18444,
617            genesis_block: genesis::regtest_genesis(),
618            max_target: 0x207fffff, // Easier difficulty for testing
619            halving_interval: 150,  // Faster halving for testing
620            network_name: "regtest".to_string(),
621            is_testnet: true,
622            signet_challenge: None,
623        })
624    }
625
626    /// Bitcoin signet parameters (BIP325 default signet)
627    pub fn signet() -> Result<Self> {
628        Ok(NetworkParameters {
629            magic_bytes: [0x0f, 0x1b, 0xf2, 0xe1],
630            default_port: 38333,
631            genesis_block: genesis::signet_genesis(),
632            max_target: 0x1e0377ae,
633            halving_interval: 210_000,
634            network_name: "signet".to_string(),
635            is_testnet: true,
636            signet_challenge: None,
637        })
638    }
639}
640
641#[cfg(test)]
642mod tests {
643    use super::*;
644    use blvm_consensus::types::{BlockHeader, OutPoint, TransactionInput, TransactionOutput};
645
646    #[test]
647    fn test_blvm_protocol_creation() {
648        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
649        assert_eq!(engine.get_protocol_version(), ProtocolVersion::BitcoinV1);
650        assert_eq!(engine.get_network_params().network_name, "mainnet");
651    }
652
653    #[test]
654    fn test_blvm_protocol_creation_all_variants() {
655        // Test mainnet
656        let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
657        assert_eq!(mainnet.get_protocol_version(), ProtocolVersion::BitcoinV1);
658        assert_eq!(mainnet.get_network_params().network_name, "mainnet");
659        assert!(!mainnet.get_network_params().is_testnet);
660
661        // Test testnet
662        let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
663        assert_eq!(testnet.get_protocol_version(), ProtocolVersion::Testnet3);
664        assert_eq!(testnet.get_network_params().network_name, "testnet");
665        assert!(testnet.get_network_params().is_testnet);
666
667        // Test regtest
668        let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
669        assert_eq!(regtest.get_protocol_version(), ProtocolVersion::Regtest);
670        assert_eq!(regtest.get_network_params().network_name, "regtest");
671        assert!(regtest.get_network_params().is_testnet);
672
673        // Test signet
674        let signet = BitcoinProtocolEngine::new(ProtocolVersion::Signet).unwrap();
675        assert_eq!(signet.get_protocol_version(), ProtocolVersion::Signet);
676        assert_eq!(signet.get_network_params().network_name, "signet");
677        assert!(signet.get_network_params().is_testnet);
678        assert_eq!(signet.get_network_params().default_port, 38333);
679    }
680
681    #[test]
682    fn test_network_parameters() {
683        let mainnet = NetworkParameters::mainnet().unwrap();
684        assert_eq!(mainnet.magic_bytes, [0xf9, 0xbe, 0xb4, 0xd9]);
685        assert_eq!(mainnet.default_port, 8333);
686        assert!(!mainnet.is_testnet);
687
688        let testnet = NetworkParameters::testnet().unwrap();
689        assert_eq!(testnet.magic_bytes, [0x0b, 0x11, 0x09, 0x07]);
690        assert_eq!(testnet.default_port, 18333);
691        assert!(testnet.is_testnet);
692
693        let regtest = NetworkParameters::regtest().unwrap();
694        assert_eq!(regtest.magic_bytes, [0xfa, 0xbf, 0xb5, 0xda]);
695        assert_eq!(regtest.default_port, 18444);
696        assert!(regtest.is_testnet);
697    }
698
699    #[test]
700    fn test_network_parameters_consistency() {
701        let mainnet = NetworkParameters::mainnet().unwrap();
702        assert_eq!(mainnet.max_target, 0x1d00ffff);
703        assert_eq!(mainnet.halving_interval, 210000);
704
705        let testnet = NetworkParameters::testnet().unwrap();
706        assert_eq!(testnet.max_target, 0x1d00ffff);
707        assert_eq!(testnet.halving_interval, 210000);
708
709        let regtest = NetworkParameters::regtest().unwrap();
710        assert_eq!(regtest.max_target, 0x207fffff); // Easier difficulty
711        assert_eq!(regtest.halving_interval, 150); // Faster halving
712    }
713
714    #[test]
715    fn test_feature_support() {
716        let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
717        assert!(mainnet.supports_feature("segwit"));
718        assert!(mainnet.supports_feature("taproot"));
719        assert!(mainnet.supports_feature("rbf"));
720        assert!(mainnet.supports_feature("ctv"));
721        assert!(!mainnet.supports_feature("fast_mining"));
722        assert!(!mainnet.supports_feature("nonexistent"));
723
724        let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
725        assert!(testnet.supports_feature("segwit"));
726        assert!(testnet.supports_feature("taproot"));
727        assert!(testnet.supports_feature("rbf"));
728        assert!(testnet.supports_feature("ctv"));
729        assert!(!testnet.supports_feature("fast_mining"));
730
731        let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
732        assert!(regtest.supports_feature("segwit"));
733        assert!(regtest.supports_feature("taproot"));
734        assert!(regtest.supports_feature("rbf"));
735        assert!(regtest.supports_feature("ctv"));
736        assert!(regtest.supports_feature("fast_mining"));
737
738        let signet = BitcoinProtocolEngine::new(ProtocolVersion::Signet).unwrap();
739        assert!(signet.supports_feature("segwit"));
740        assert!(signet.supports_feature("taproot"));
741        assert!(signet.supports_feature("signet"));
742        assert!(!signet.supports_feature("fast_mining"));
743    }
744
745    #[test]
746    fn test_signet_challenge_override_on_engine() {
747        let engine = BitcoinProtocolEngine::new(ProtocolVersion::Signet)
748            .unwrap()
749            .with_signet_challenge(Some(vec![0x51]));
750        assert_eq!(
751            engine
752                .get_network_params()
753                .signet_challenge
754                .as_ref()
755                .map(|b| b.as_ref()),
756            Some([0x51].as_slice())
757        );
758        let ctx = engine.connect_block_validation_context(None::<&[BlockHeader]>, 0);
759        assert_eq!(
760            ctx.signet_challenge.as_ref().map(|b| b.as_ref()),
761            Some([0x51].as_slice())
762        );
763    }
764
765    #[test]
766    fn test_block_validation_empty_utxos() {
767        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
768        let utxos = UtxoSet::default();
769
770        // Create a simple block with just a coinbase transaction
771        let coinbase_tx = Transaction {
772            version: 1,
773            inputs: blvm_consensus::tx_inputs![TransactionInput {
774                prevout: OutPoint {
775                    hash: [0u8; 32],
776                    index: 0xffffffff,
777                },
778                script_sig: vec![0x01, 0x00], // Height 0
779                sequence: 0xffffffff,
780            }],
781            outputs: blvm_consensus::tx_outputs![TransactionOutput {
782                value: 50_0000_0000,
783                script_pubkey: vec![
784                    blvm_consensus::opcodes::OP_DUP,
785                    blvm_consensus::opcodes::OP_HASH160,
786                    blvm_consensus::opcodes::PUSH_20_BYTES,
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                    0x00,
800                    0x00,
801                    0x00,
802                    0x00,
803                    0x00,
804                    0x00,
805                    0x00,
806                    0x00,
807                    blvm_consensus::opcodes::OP_EQUALVERIFY,
808                    blvm_consensus::opcodes::OP_CHECKSIG,
809                ], // P2PKH
810            }],
811            lock_time: 0,
812        };
813
814        // Calculate proper merkle root
815        let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
816            .expect("Should calculate merkle root");
817
818        let block = Block {
819            header: BlockHeader {
820                version: 1,
821                prev_block_hash: [0u8; 32],
822                merkle_root,
823                timestamp: 1231006505,
824                bits: 0x1d00ffff,
825                nonce: 0,
826            },
827            transactions: vec![coinbase_tx].into_boxed_slice(),
828        };
829
830        // This should pass validation for a genesis block
831        let result = engine.validate_block(&block, &utxos, 0);
832        assert!(result.is_ok());
833    }
834
835    #[test]
836    fn test_transaction_validation() {
837        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
838
839        // Create a simple transaction
840        let tx = Transaction {
841            version: 1,
842            inputs: vec![TransactionInput {
843                prevout: OutPoint {
844                    hash: [0u8; 32],
845                    index: 0,
846                },
847                script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
848                sequence: 0xffffffff,
849            }]
850            .into(),
851            outputs: vec![TransactionOutput {
852                value: 50_0000_0000,
853                script_pubkey: vec![
854                    blvm_consensus::opcodes::OP_DUP,
855                    blvm_consensus::opcodes::OP_HASH160,
856                    blvm_consensus::opcodes::PUSH_20_BYTES,
857                    0x00,
858                    0x00,
859                    0x00,
860                    0x00,
861                    0x00,
862                    0x00,
863                    0x00,
864                    0x00,
865                    0x00,
866                    0x00,
867                    0x00,
868                    0x00,
869                    0x00,
870                    0x00,
871                    0x00,
872                    0x00,
873                    0x00,
874                    0x00,
875                    0x00,
876                    0x00,
877                    blvm_consensus::opcodes::OP_EQUALVERIFY,
878                    blvm_consensus::opcodes::OP_CHECKSIG,
879                ], // P2PKH
880            }]
881            .into(),
882            lock_time: 0,
883        };
884
885        let result = engine.validate_transaction(&tx);
886        assert!(result.is_ok());
887    }
888
889    #[test]
890    fn test_cross_protocol_validation() {
891        let mainnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
892        let testnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
893
894        // Both engines should support the same features
895        assert_eq!(
896            mainnet_engine.supports_feature("segwit"),
897            testnet_engine.supports_feature("segwit")
898        );
899        assert_eq!(
900            mainnet_engine.supports_feature("taproot"),
901            testnet_engine.supports_feature("taproot")
902        );
903
904        // But they should have different network parameters
905        assert_ne!(
906            mainnet_engine.get_network_params().magic_bytes,
907            testnet_engine.get_network_params().magic_bytes
908        );
909        assert_ne!(
910            mainnet_engine.get_network_params().default_port,
911            testnet_engine.get_network_params().default_port
912        );
913    }
914
915    #[test]
916    fn test_protocol_version_switching() {
917        // Test that we can create engines for different protocol versions
918        let versions = vec![
919            ProtocolVersion::BitcoinV1,
920            ProtocolVersion::Testnet3,
921            ProtocolVersion::Regtest,
922            ProtocolVersion::Signet,
923        ];
924
925        for version in versions {
926            let engine = BitcoinProtocolEngine::new(version).unwrap();
927            assert_eq!(engine.get_protocol_version(), version);
928        }
929    }
930
931    #[test]
932    fn test_network_parameters_serialization() {
933        let mainnet = NetworkParameters::mainnet().unwrap();
934        let testnet = NetworkParameters::testnet().unwrap();
935        let regtest = NetworkParameters::regtest().unwrap();
936
937        // Test that parameters can be serialized and deserialized
938        let mainnet_json = serde_json::to_string(&mainnet).unwrap();
939        let mainnet_deserialized: NetworkParameters = serde_json::from_str(&mainnet_json).unwrap();
940        assert_eq!(mainnet.magic_bytes, mainnet_deserialized.magic_bytes);
941        assert_eq!(mainnet.default_port, mainnet_deserialized.default_port);
942        assert_eq!(mainnet.network_name, mainnet_deserialized.network_name);
943        assert_eq!(mainnet.is_testnet, mainnet_deserialized.is_testnet);
944
945        let testnet_json = serde_json::to_string(&testnet).unwrap();
946        let testnet_deserialized: NetworkParameters = serde_json::from_str(&testnet_json).unwrap();
947        assert_eq!(testnet.magic_bytes, testnet_deserialized.magic_bytes);
948
949        let regtest_json = serde_json::to_string(&regtest).unwrap();
950        let regtest_deserialized: NetworkParameters = serde_json::from_str(&regtest_json).unwrap();
951        assert_eq!(regtest.magic_bytes, regtest_deserialized.magic_bytes);
952    }
953
954    #[test]
955    fn test_protocol_version_serialization() {
956        let versions = vec![
957            ProtocolVersion::BitcoinV1,
958            ProtocolVersion::Testnet3,
959            ProtocolVersion::Regtest,
960            ProtocolVersion::Signet,
961        ];
962
963        for version in versions {
964            let json = serde_json::to_string(&version).unwrap();
965            let deserialized: ProtocolVersion = serde_json::from_str(&json).unwrap();
966            assert_eq!(version, deserialized);
967        }
968    }
969
970    #[test]
971    fn test_network_parameters_equality() {
972        let mainnet1 = NetworkParameters::mainnet().unwrap();
973        let mainnet2 = NetworkParameters::mainnet().unwrap();
974        let testnet = NetworkParameters::testnet().unwrap();
975
976        assert_eq!(mainnet1, mainnet2);
977        assert_ne!(mainnet1, testnet);
978    }
979
980    #[test]
981    fn test_protocol_version_equality() {
982        assert_eq!(ProtocolVersion::BitcoinV1, ProtocolVersion::BitcoinV1);
983        assert_ne!(ProtocolVersion::BitcoinV1, ProtocolVersion::Testnet3);
984        assert_ne!(ProtocolVersion::Testnet3, ProtocolVersion::Regtest);
985    }
986
987    #[test]
988    fn test_feature_activation_by_height() {
989        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
990
991        // SegWit activates at block 481,824
992        assert!(!engine.is_feature_active("segwit", 481_823, 1503539000));
993        assert!(engine.is_feature_active("segwit", 481_824, 1503539857));
994        assert!(engine.is_feature_active("segwit", 500_000, 1504000000));
995
996        // Taproot activates at block 709,632
997        assert!(!engine.is_feature_active("taproot", 709_631, 1636934000));
998        assert!(engine.is_feature_active("taproot", 709_632, 1636934400));
999        assert!(engine.is_feature_active("taproot", 800_000, 1640000000));
1000    }
1001
1002    #[test]
1003    fn test_economic_parameters_access() {
1004        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
1005        let params = engine.get_economic_parameters();
1006
1007        assert_eq!(params.initial_subsidy, 50_0000_0000);
1008        assert_eq!(params.halving_interval, 210_000);
1009        assert_eq!(params.coinbase_maturity, 100);
1010
1011        // Test block subsidy calculation
1012        assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
1013        assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
1014    }
1015
1016    #[test]
1017    fn test_feature_registry_access() {
1018        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
1019        let registry = engine.get_feature_registry();
1020
1021        assert!(registry.get_feature("segwit").is_some());
1022        assert!(registry.get_feature("taproot").is_some());
1023        assert!(registry.get_feature("nonexistent").is_none());
1024
1025        let features = registry.list_features();
1026        assert!(features.contains(&"segwit".to_string()));
1027        assert!(features.contains(&"taproot".to_string()));
1028    }
1029}