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