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: blvm_consensus::economic::subsidy_halving_interval(
636                blvm_consensus::types::Network::Mainnet,
637            ),
638            network_name: "mainnet".to_string(),
639            is_testnet: false,
640            signet_challenge: None,
641        })
642    }
643
644    /// Bitcoin testnet parameters
645    pub fn testnet() -> Result<Self> {
646        Ok(NetworkParameters {
647            magic_bytes: [0x0b, 0x11, 0x09, 0x07], // Bitcoin testnet magic
648            default_port: 18333,
649            genesis_block: genesis::testnet_genesis(),
650            max_target: 0x1d00ffff,
651            halving_interval: blvm_consensus::economic::subsidy_halving_interval(
652                blvm_consensus::types::Network::Testnet,
653            ),
654            network_name: "testnet".to_string(),
655            is_testnet: true,
656            signet_challenge: None,
657        })
658    }
659
660    /// Bitcoin regtest parameters
661    pub fn regtest() -> Result<Self> {
662        Ok(NetworkParameters {
663            magic_bytes: [0xfa, 0xbf, 0xb5, 0xda], // Bitcoin regtest magic
664            default_port: 18444,
665            genesis_block: genesis::regtest_genesis(),
666            max_target: 0x207fffff, // Easier difficulty for testing
667            halving_interval: blvm_consensus::economic::subsidy_halving_interval(
668                blvm_consensus::types::Network::Regtest,
669            ),
670            network_name: "regtest".to_string(),
671            is_testnet: true,
672            signet_challenge: None,
673        })
674    }
675
676    /// Bitcoin signet parameters (BIP325 default signet)
677    pub fn signet() -> Result<Self> {
678        Ok(NetworkParameters {
679            magic_bytes: [0x0f, 0x1b, 0xf2, 0xe1],
680            default_port: 38333,
681            genesis_block: genesis::signet_genesis(),
682            max_target: 0x1e0377ae,
683            halving_interval: blvm_consensus::economic::subsidy_halving_interval(
684                blvm_consensus::types::Network::Signet,
685            ),
686            network_name: "signet".to_string(),
687            is_testnet: true,
688            signet_challenge: None,
689        })
690    }
691
692    /// Bitcoin testnet4 parameters (BIP94).
693    pub fn testnet4() -> Result<Self> {
694        Ok(NetworkParameters {
695            magic_bytes: [0x1c, 0x16, 0x3f, 0x28],
696            default_port: 48333,
697            genesis_block: genesis::testnet4_genesis(),
698            max_target: 0x1d00ffff,
699            halving_interval: blvm_consensus::economic::subsidy_halving_interval(
700                blvm_consensus::types::Network::Testnet4,
701            ),
702            network_name: "testnet4".to_string(),
703            is_testnet: true,
704            signet_challenge: None,
705        })
706    }
707}
708
709#[cfg(test)]
710mod tests {
711    use super::*;
712    use blvm_consensus::types::{BlockHeader, OutPoint, TransactionInput, TransactionOutput};
713
714    #[test]
715    fn test_blvm_protocol_creation() {
716        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
717        assert_eq!(engine.get_protocol_version(), ProtocolVersion::BitcoinV1);
718        assert_eq!(engine.get_network_params().network_name, "mainnet");
719    }
720
721    #[test]
722    fn test_blvm_protocol_creation_all_variants() {
723        // Test mainnet
724        let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
725        assert_eq!(mainnet.get_protocol_version(), ProtocolVersion::BitcoinV1);
726        assert_eq!(mainnet.get_network_params().network_name, "mainnet");
727        assert!(!mainnet.get_network_params().is_testnet);
728
729        // Test testnet
730        let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
731        assert_eq!(testnet.get_protocol_version(), ProtocolVersion::Testnet3);
732        assert_eq!(testnet.get_network_params().network_name, "testnet");
733        assert!(testnet.get_network_params().is_testnet);
734
735        // Test regtest
736        let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
737        assert_eq!(regtest.get_protocol_version(), ProtocolVersion::Regtest);
738        assert_eq!(regtest.get_network_params().network_name, "regtest");
739        assert!(regtest.get_network_params().is_testnet);
740
741        // Test signet
742        let signet = BitcoinProtocolEngine::new(ProtocolVersion::Signet).unwrap();
743        assert_eq!(signet.get_protocol_version(), ProtocolVersion::Signet);
744        assert_eq!(signet.get_network_params().network_name, "signet");
745        assert!(signet.get_network_params().is_testnet);
746        assert_eq!(signet.get_network_params().default_port, 38333);
747    }
748
749    #[test]
750    fn test_network_parameters() {
751        let mainnet = NetworkParameters::mainnet().unwrap();
752        assert_eq!(mainnet.magic_bytes, [0xf9, 0xbe, 0xb4, 0xd9]);
753        assert_eq!(mainnet.default_port, 8333);
754        assert!(!mainnet.is_testnet);
755
756        let testnet = NetworkParameters::testnet().unwrap();
757        assert_eq!(testnet.magic_bytes, [0x0b, 0x11, 0x09, 0x07]);
758        assert_eq!(testnet.default_port, 18333);
759        assert!(testnet.is_testnet);
760
761        let regtest = NetworkParameters::regtest().unwrap();
762        assert_eq!(regtest.magic_bytes, [0xfa, 0xbf, 0xb5, 0xda]);
763        assert_eq!(regtest.default_port, 18444);
764        assert!(regtest.is_testnet);
765    }
766
767    #[test]
768    fn test_network_parameters_consistency() {
769        let mainnet = NetworkParameters::mainnet().unwrap();
770        assert_eq!(mainnet.max_target, 0x1d00ffff);
771        assert_eq!(mainnet.halving_interval, 210000);
772
773        let testnet = NetworkParameters::testnet().unwrap();
774        assert_eq!(testnet.max_target, 0x1d00ffff);
775        assert_eq!(testnet.halving_interval, 210000);
776
777        let regtest = NetworkParameters::regtest().unwrap();
778        assert_eq!(regtest.max_target, 0x207fffff); // Easier difficulty
779        assert_eq!(regtest.halving_interval, 150); // Faster halving
780    }
781
782    #[test]
783    fn test_feature_support() {
784        let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
785        assert!(mainnet.supports_feature("segwit"));
786        assert!(mainnet.supports_feature("taproot"));
787        assert!(mainnet.supports_feature("rbf"));
788        assert!(mainnet.supports_feature("ctv"));
789        assert!(!mainnet.supports_feature("fast_mining"));
790        assert!(!mainnet.supports_feature("nonexistent"));
791
792        let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
793        assert!(testnet.supports_feature("segwit"));
794        assert!(testnet.supports_feature("taproot"));
795        assert!(testnet.supports_feature("rbf"));
796        assert!(testnet.supports_feature("ctv"));
797        assert!(!testnet.supports_feature("fast_mining"));
798
799        let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
800        assert!(regtest.supports_feature("segwit"));
801        assert!(regtest.supports_feature("taproot"));
802        assert!(regtest.supports_feature("rbf"));
803        assert!(regtest.supports_feature("ctv"));
804        assert!(regtest.supports_feature("fast_mining"));
805
806        let signet = BitcoinProtocolEngine::new(ProtocolVersion::Signet).unwrap();
807        assert!(signet.supports_feature("segwit"));
808        assert!(signet.supports_feature("taproot"));
809        assert!(signet.supports_feature("signet"));
810        assert!(!signet.supports_feature("fast_mining"));
811    }
812
813    #[test]
814    fn test_signet_challenge_override_on_engine() {
815        let engine = BitcoinProtocolEngine::new(ProtocolVersion::Signet)
816            .unwrap()
817            .with_signet_challenge(Some(vec![0x51]));
818        assert_eq!(
819            engine
820                .get_network_params()
821                .signet_challenge
822                .as_ref()
823                .map(|b| b.as_ref()),
824            Some([0x51].as_slice())
825        );
826        let ctx = engine.connect_block_validation_context(None::<&[BlockHeader]>, 0);
827        assert_eq!(
828            ctx.signet_challenge.as_ref().map(|b| b.as_ref()),
829            Some([0x51].as_slice())
830        );
831    }
832
833    #[test]
834    fn test_block_validation_empty_utxos() {
835        use crate::validation::ProtocolValidationContext;
836        use blvm_consensus::opcodes::OP_1;
837
838        let engine = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
839        let utxos = UtxoSet::default();
840
841        let coinbase_tx = Transaction {
842            version: 2,
843            inputs: blvm_consensus::tx_inputs![TransactionInput {
844                prevout: OutPoint {
845                    hash: [0u8; 32],
846                    index: 0xffffffff,
847                },
848                script_sig: vec![0x00, 0xff],
849                sequence: 0xffffffff,
850            }],
851            outputs: blvm_consensus::tx_outputs![TransactionOutput {
852                value: 5_000_000_000,
853                script_pubkey: vec![OP_1],
854            }],
855            lock_time: 0,
856        };
857
858        let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
859            .expect("merkle root");
860
861        let block = Block {
862            header: BlockHeader {
863                version: 4,
864                prev_block_hash: [0u8; 32],
865                merkle_root,
866                timestamp: 1_231_006_505,
867                bits: 0x0300ffff,
868                nonce: 0,
869            },
870            transactions: vec![coinbase_tx].into_boxed_slice(),
871        };
872
873        let witnesses: Vec<Vec<blvm_consensus::segwit::Witness>> = block
874            .transactions
875            .iter()
876            .map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
877            .collect();
878        let mut context = ProtocolValidationContext::new(ProtocolVersion::Regtest, 0).unwrap();
879        context.network_time = block.header.timestamp;
880        context.median_time_past = block.header.timestamp - 1;
881
882        let (result, new_utxos) = engine
883            .validate_and_connect_block(&block, &witnesses, &utxos, 0, None, &context)
884            .unwrap();
885        assert_eq!(result, ValidationResult::Valid);
886        assert!(!new_utxos.is_empty());
887    }
888
889    #[test]
890    fn test_transaction_validation() {
891        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
892
893        // Create a simple transaction
894        let tx = Transaction {
895            version: 1,
896            inputs: vec![TransactionInput {
897                prevout: OutPoint {
898                    hash: [0u8; 32],
899                    index: 0,
900                },
901                script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
902                sequence: 0xffffffff,
903            }]
904            .into(),
905            outputs: vec![TransactionOutput {
906                value: 50_0000_0000,
907                script_pubkey: vec![
908                    blvm_consensus::opcodes::OP_DUP,
909                    blvm_consensus::opcodes::OP_HASH160,
910                    blvm_consensus::opcodes::PUSH_20_BYTES,
911                    0x00,
912                    0x00,
913                    0x00,
914                    0x00,
915                    0x00,
916                    0x00,
917                    0x00,
918                    0x00,
919                    0x00,
920                    0x00,
921                    0x00,
922                    0x00,
923                    0x00,
924                    0x00,
925                    0x00,
926                    0x00,
927                    0x00,
928                    0x00,
929                    0x00,
930                    0x00,
931                    blvm_consensus::opcodes::OP_EQUALVERIFY,
932                    blvm_consensus::opcodes::OP_CHECKSIG,
933                ], // P2PKH
934            }]
935            .into(),
936            lock_time: 0,
937        };
938
939        let result = engine.validate_transaction(&tx);
940        assert!(result.is_ok());
941    }
942
943    #[test]
944    fn test_cross_protocol_validation() {
945        let mainnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
946        let testnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
947
948        // Both engines should support the same features
949        assert_eq!(
950            mainnet_engine.supports_feature("segwit"),
951            testnet_engine.supports_feature("segwit")
952        );
953        assert_eq!(
954            mainnet_engine.supports_feature("taproot"),
955            testnet_engine.supports_feature("taproot")
956        );
957
958        // But they should have different network parameters
959        assert_ne!(
960            mainnet_engine.get_network_params().magic_bytes,
961            testnet_engine.get_network_params().magic_bytes
962        );
963        assert_ne!(
964            mainnet_engine.get_network_params().default_port,
965            testnet_engine.get_network_params().default_port
966        );
967    }
968
969    #[test]
970    fn test_protocol_version_switching() {
971        // Test that we can create engines for different protocol versions
972        let versions = vec![
973            ProtocolVersion::BitcoinV1,
974            ProtocolVersion::Testnet3,
975            ProtocolVersion::Regtest,
976            ProtocolVersion::Signet,
977        ];
978
979        for version in versions {
980            let engine = BitcoinProtocolEngine::new(version).unwrap();
981            assert_eq!(engine.get_protocol_version(), version);
982        }
983    }
984
985    #[test]
986    fn test_network_parameters_serialization() {
987        let mainnet = NetworkParameters::mainnet().unwrap();
988        let testnet = NetworkParameters::testnet().unwrap();
989        let regtest = NetworkParameters::regtest().unwrap();
990
991        // Test that parameters can be serialized and deserialized
992        let mainnet_json = serde_json::to_string(&mainnet).unwrap();
993        let mainnet_deserialized: NetworkParameters = serde_json::from_str(&mainnet_json).unwrap();
994        assert_eq!(mainnet.magic_bytes, mainnet_deserialized.magic_bytes);
995        assert_eq!(mainnet.default_port, mainnet_deserialized.default_port);
996        assert_eq!(mainnet.network_name, mainnet_deserialized.network_name);
997        assert_eq!(mainnet.is_testnet, mainnet_deserialized.is_testnet);
998
999        let testnet_json = serde_json::to_string(&testnet).unwrap();
1000        let testnet_deserialized: NetworkParameters = serde_json::from_str(&testnet_json).unwrap();
1001        assert_eq!(testnet.magic_bytes, testnet_deserialized.magic_bytes);
1002
1003        let regtest_json = serde_json::to_string(&regtest).unwrap();
1004        let regtest_deserialized: NetworkParameters = serde_json::from_str(&regtest_json).unwrap();
1005        assert_eq!(regtest.magic_bytes, regtest_deserialized.magic_bytes);
1006    }
1007
1008    #[test]
1009    fn test_protocol_version_serialization() {
1010        let versions = vec![
1011            ProtocolVersion::BitcoinV1,
1012            ProtocolVersion::Testnet3,
1013            ProtocolVersion::Regtest,
1014            ProtocolVersion::Signet,
1015        ];
1016
1017        for version in versions {
1018            let json = serde_json::to_string(&version).unwrap();
1019            let deserialized: ProtocolVersion = serde_json::from_str(&json).unwrap();
1020            assert_eq!(version, deserialized);
1021        }
1022    }
1023
1024    #[test]
1025    fn test_network_parameters_equality() {
1026        let mainnet1 = NetworkParameters::mainnet().unwrap();
1027        let mainnet2 = NetworkParameters::mainnet().unwrap();
1028        let testnet = NetworkParameters::testnet().unwrap();
1029
1030        assert_eq!(mainnet1, mainnet2);
1031        assert_ne!(mainnet1, testnet);
1032    }
1033
1034    #[test]
1035    fn test_protocol_version_equality() {
1036        assert_eq!(ProtocolVersion::BitcoinV1, ProtocolVersion::BitcoinV1);
1037        assert_ne!(ProtocolVersion::BitcoinV1, ProtocolVersion::Testnet3);
1038        assert_ne!(ProtocolVersion::Testnet3, ProtocolVersion::Regtest);
1039    }
1040
1041    #[test]
1042    fn test_feature_activation_by_height() {
1043        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
1044
1045        // SegWit activates at block 481,824
1046        assert!(!engine.is_feature_active("segwit", 481_823, 1503539000));
1047        assert!(engine.is_feature_active("segwit", 481_824, 1503539857));
1048        assert!(engine.is_feature_active("segwit", 500_000, 1504000000));
1049
1050        // Taproot activates at block 709,632
1051        assert!(!engine.is_feature_active("taproot", 709_631, 1636934000));
1052        assert!(engine.is_feature_active("taproot", 709_632, 1636934400));
1053        assert!(engine.is_feature_active("taproot", 800_000, 1640000000));
1054    }
1055
1056    #[test]
1057    fn test_economic_parameters_access() {
1058        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
1059        let params = engine.get_economic_parameters();
1060
1061        assert_eq!(params.initial_subsidy, 50_0000_0000);
1062        assert_eq!(params.halving_interval, 210_000);
1063        assert_eq!(params.coinbase_maturity, 100);
1064
1065        // Test block subsidy calculation
1066        assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
1067        assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
1068    }
1069
1070    #[test]
1071    fn test_feature_registry_access() {
1072        let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
1073        let registry = engine.get_feature_registry();
1074
1075        assert!(registry.get_feature("segwit").is_some());
1076        assert!(registry.get_feature("taproot").is_some());
1077        assert!(registry.get_feature("nonexistent").is_none());
1078
1079        let features = registry.list_features();
1080        assert!(features.contains(&"segwit".to_string()));
1081        assert!(features.contains(&"taproot".to_string()));
1082    }
1083}