Skip to main content

blvm_protocol/
lib.rs

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