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