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