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