Skip to main content

blvm_consensus/
lib.rs

1//! # Consensus-Proof
2//!
3//! Direct mathematical implementation of Bitcoin consensus rules from the Orange Paper.
4//!
5//! This crate provides pure, side-effect-free functions that implement the mathematical
6//! specifications defined in the Orange Paper. It serves as the mathematical foundation
7//! for Bitcoin consensus validation.
8//!
9//! ## Architecture
10//!
11//! The system follows a layered architecture:
12//! - Orange Paper (mathematical specifications)
13//! - Consensus Proof (this crate - direct implementation)
14//! - Reference Node (minimal Bitcoin implementation)
15//! - Developer SDK (developer-friendly interface)
16//!
17//! ## Design Principles
18//!
19//! 1. **Pure Functions**: All functions are deterministic and side-effect-free
20//! 2. **Mathematical Accuracy**: Direct implementation of Orange Paper specifications
21//! 3. **Exact Version Pinning**: All consensus-critical dependencies pinned to exact versions
22//! 4. **No Consensus Rule Interpretation**: Only mathematical implementation
23//!
24//! ## Usage
25//!
26//! ```rust
27//! use blvm_consensus::transaction::check_transaction;
28//! use blvm_consensus::types::*;
29//!
30//! let transaction = Transaction {
31//!     version: 1,
32//!     inputs: vec![].into(),
33//!     outputs: vec![TransactionOutput {
34//!         value: 1000,
35//!         script_pubkey: vec![0x51],
36//!     }]
37//!     .into(),
38//!     lock_time: 0,
39//! };
40//! let result = check_transaction(&transaction).unwrap();
41//! ```
42
43#![allow(unused_doc_comments)] // Allow doc comments before macros (proptest, etc.)
44#![allow(unused_variables, unused_assignments)] // Feature-gated and conditional compilation paths
45#![allow(
46    clippy::too_many_arguments,  // Script/block have 8–17 args; struct refactor is large
47    clippy::type_complexity,     // Performance-critical types (OnceLock<RwLock<LruCache<...>>>)
48    clippy::large_enum_variant,  // NetworkResponse::SendMessage; boxing changes layout
49)]
50
51pub mod script;
52pub mod transaction;
53pub mod transaction_hash;
54
55use blvm_spec_lock::spec_locked;
56#[cfg(all(feature = "production", feature = "benchmarking"))]
57pub use config::{reset_assume_valid_height, set_assume_valid_height};
58#[cfg(feature = "production")]
59pub use script::batch_verify_signatures;
60#[cfg(all(feature = "production", feature = "benchmarking"))]
61pub use script::{
62    clear_all_caches, clear_hash_cache, clear_script_cache, clear_stack_pool, disable_caching,
63    reset_benchmarking_state,
64};
65#[cfg(all(feature = "production", feature = "benchmarking"))]
66pub use transaction_hash::clear_sighash_templates;
67
68// Re-export from blvm-primitives for backward compatibility
69pub use blvm_primitives::constants;
70pub use blvm_primitives::crypto;
71pub use blvm_primitives::opcodes;
72pub use blvm_primitives::serialization;
73pub use blvm_primitives::{error, types};
74pub use blvm_primitives::{tx_inputs, tx_outputs};
75pub use constants::*;
76pub use error::ConsensusError;
77pub use types::*;
78
79/// Orange Paper Section 4 symbols (C, H, M_MAX, etc.) — re-export from primitives constants.
80pub mod orange_paper_constants {
81    pub use crate::constants::{C, H, L_ELEMENT, L_OPS, L_SCRIPT, L_STACK, M_MAX, R, S_MAX, W_MAX};
82}
83
84/// Spec-lock / property-test helpers only — not a supported production API.
85/// Functions in this module `panic!` or `unimplemented!` when called outside their
86/// intended spec-validation context. Do **not** call from production code.
87#[doc(hidden)]
88pub mod orange_paper_property_helpers;
89
90pub mod config;
91
92pub mod activation;
93pub mod bip113;
94#[cfg(feature = "ctv")]
95pub mod bip119;
96#[cfg(any(feature = "csfs", feature = "production"))]
97pub mod bip348;
98pub mod bip_validation;
99pub mod block;
100#[cfg(all(feature = "production", feature = "rayon"))]
101pub mod checkqueue;
102pub mod economic;
103pub mod locktime;
104pub mod mempool;
105pub mod mining;
106pub mod optimizations;
107pub mod pow;
108pub mod reorganization;
109#[cfg(all(feature = "production", feature = "rayon"))]
110pub(crate) mod script_exec_cache;
111pub mod secp256k1_backend;
112pub mod segwit;
113pub mod sequence_locks;
114pub mod signet;
115pub mod sigop;
116pub(crate) mod spec_witnesses;
117pub mod taproot;
118pub mod utxo_overlay;
119pub mod version_bits;
120pub mod witness;
121
122// Integration tests link this crate without `cfg(test)` on the library, so `test_utils` cannot be
123// gated only on `test`. Fixture helpers are small; `property-tests`/`proptest` stays gated inside the module.
124pub mod test_utils;
125
126#[cfg(feature = "profile")]
127pub mod profile_log;
128#[cfg(all(feature = "production", feature = "profile"))]
129pub mod script_profile;
130
131/// Consensus Proof - wrapper struct for consensus validation functions
132///
133/// This struct provides a convenient API for accessing all consensus validation
134/// functions. All methods delegate to the corresponding module functions.
135#[derive(Debug, Clone, Copy, Default)]
136pub struct ConsensusProof;
137
138impl ConsensusProof {
139    /// Create a new ConsensusProof instance
140    pub fn new() -> Self {
141        Self
142    }
143
144    /// Validate a transaction according to consensus rules
145    #[spec_locked("5.1", "CheckTransaction")]
146    #[blvm_spec_lock::ensures(result == true || result == false)]
147    pub fn validate_transaction(
148        &self,
149        tx: &types::Transaction,
150    ) -> error::Result<types::ValidationResult> {
151        transaction::check_transaction(tx)
152    }
153
154    /// Validate transaction inputs against UTXO set
155    #[spec_locked("5.1", "CheckTxInputs")]
156    #[blvm_spec_lock::ensures(result_0 == true || result_0 == false)]
157    pub fn validate_tx_inputs(
158        &self,
159        tx: &types::Transaction,
160        utxo_set: &types::UtxoSet,
161        height: types::Natural,
162    ) -> error::Result<(types::ValidationResult, types::Integer)> {
163        transaction::check_tx_inputs(tx, utxo_set, height)
164    }
165
166    /// Validate a complete block
167    ///
168    /// **Deprecated**: builds empty witness stacks and defaults to [`Network::Mainnet`] with
169    /// wall-clock time. Use [`validate_block_with_time_context`] with explicit witnesses,
170    /// network, and median-time-past instead.
171    #[deprecated(
172        since = "0.1.33",
173        note = "Use validate_block_with_time_context with explicit witnesses, network, and TimeContext"
174    )]
175    #[spec_locked("5.3", "ConnectBlock")]
176    #[blvm_spec_lock::ensures(result_0 == true || result_0 == false)]
177    pub fn validate_block(
178        &self,
179        block: &types::Block,
180        utxo_set: types::UtxoSet,
181        height: types::Natural,
182    ) -> error::Result<(types::ValidationResult, types::UtxoSet)> {
183        // Create empty witnesses for backward compatibility
184        let witnesses: Vec<Vec<segwit::Witness>> =
185            block.transactions.iter().map(|_| Vec::new()).collect();
186        // Safe time read: unwrap_or avoids panic when clock is before epoch.
187        // NOTE: This entry point defaults to Network::Mainnet. Callers that need
188        // a different network should use `validate_block_with_time_context` instead.
189        let network_time = std::time::SystemTime::now()
190            .duration_since(std::time::UNIX_EPOCH)
191            .unwrap_or(std::time::Duration::ZERO)
192            .as_secs();
193        let context = block::block_validation_context_for_connect_ibd(
194            None::<&[types::BlockHeader]>,
195            network_time,
196            types::Network::Mainnet,
197        );
198        let (result, new_utxo_set, _undo_log) =
199            block::connect_block(block, &witnesses, utxo_set, height, &context)?;
200        Ok((result, new_utxo_set))
201    }
202
203    /// Validate a complete block with witness data and time context
204    #[spec_locked("5.3", "ConnectBlock")]
205    #[blvm_spec_lock::ensures(result_0 == true || result_0 == false)]
206    pub fn validate_block_with_time_context(
207        &self,
208        block: &types::Block,
209        witnesses: &[Vec<segwit::Witness>],
210        utxo_set: types::UtxoSet,
211        height: types::Natural,
212        time_context: Option<types::TimeContext>,
213        network: types::Network,
214    ) -> error::Result<(types::ValidationResult, types::UtxoSet)> {
215        let context = block::BlockValidationContext::from_time_context_and_network(
216            time_context,
217            network,
218            None,
219        );
220        let (result, new_utxo_set, _undo_log) =
221            block::connect_block(block, witnesses, utxo_set, height, &context)?;
222        Ok((result, new_utxo_set))
223    }
224
225    /// Verify script execution (legacy API — see [`script::verify_script`] deprecation).
226    #[deprecated(
227        since = "0.1.33",
228        note = "Use verify_script_with_context via script module for witness-aware verification"
229    )]
230    #[spec_locked("5.2", "VerifyScript")]
231    #[blvm_spec_lock::ensures(result == true || result == false)]
232    pub fn verify_script(
233        &self,
234        script_sig: &types::ByteString,
235        script_pubkey: &types::ByteString,
236        witness: Option<&types::ByteString>,
237        flags: u32,
238    ) -> error::Result<bool> {
239        #[allow(deprecated)]
240        {
241            script::verify_script(script_sig, script_pubkey, witness, flags)
242        }
243    }
244
245    /// Check proof of work
246    #[spec_locked("7.2", "CheckProofOfWork")]
247    #[blvm_spec_lock::ensures(result == true || result == false)]
248    pub fn check_proof_of_work(&self, header: &types::BlockHeader) -> error::Result<bool> {
249        pow::check_proof_of_work(header)
250    }
251
252    /// Get block subsidy for height
253    #[spec_locked("6.1", "GetBlockSubsidy")]
254    #[blvm_spec_lock::ensures(result >= 0)]
255    #[blvm_spec_lock::axiom(result <= INITIAL_SUBSIDY)]
256    pub fn get_block_subsidy(&self, height: types::Natural) -> types::Integer {
257        economic::get_block_subsidy(height)
258    }
259
260    /// Calculate total supply at height
261    #[spec_locked("6.2", "TotalSupply")]
262    #[blvm_spec_lock::ensures(result >= 0)]
263    pub fn total_supply(&self, height: types::Natural) -> types::Integer {
264        economic::total_supply(height)
265    }
266
267    /// Get next work required for difficulty adjustment
268    #[spec_locked("7.1", "GetNextWorkRequired")]
269    #[blvm_spec_lock::ensures(result >= 0)]
270    pub fn get_next_work_required(
271        &self,
272        current_header: &types::BlockHeader,
273        prev_headers: &[types::BlockHeader],
274    ) -> error::Result<types::Natural> {
275        pow::get_next_work_required(current_header, prev_headers)
276    }
277
278    /// Accept transaction to memory pool
279    #[spec_locked("9.1", "AcceptToMemoryPool")]
280    #[blvm_spec_lock::ensures(result == true || result == false)]
281    pub fn accept_to_memory_pool(
282        &self,
283        tx: &types::Transaction,
284        utxo_set: &types::UtxoSet,
285        mempool: &mempool::Mempool,
286        height: types::Natural,
287        time_context: Option<types::TimeContext>,
288        network: types::Network,
289    ) -> error::Result<mempool::MempoolResult> {
290        mempool::accept_to_memory_pool(tx, None, utxo_set, mempool, height, time_context, network)
291    }
292
293    /// Check if transaction is standard
294    #[spec_locked("9.2", "IsStandardTx")]
295    #[blvm_spec_lock::ensures(result == true || result == false)]
296    pub fn is_standard_tx(&self, tx: &types::Transaction) -> error::Result<bool> {
297        mempool::is_standard_tx(tx)
298    }
299
300    /// Check if transaction can replace existing one (RBF)
301    #[spec_locked("9.3", "ReplacementChecks")]
302    #[blvm_spec_lock::ensures(result == true || result == false)]
303    pub fn replacement_checks(
304        &self,
305        new_tx: &types::Transaction,
306        existing_tx: &types::Transaction,
307        utxo_set: &types::UtxoSet,
308        mempool: &mempool::Mempool,
309    ) -> error::Result<bool> {
310        mempool::replacement_checks(new_tx, existing_tx, utxo_set, mempool)
311    }
312
313    /// Create new block from mempool transactions
314    #[allow(clippy::too_many_arguments)]
315    #[spec_locked("12.1", "CreateNewBlock")]
316    #[blvm_spec_lock::ensures(result >= 0)]
317    pub fn create_new_block(
318        &self,
319        utxo_set: &types::UtxoSet,
320        mempool_txs: &[types::Transaction],
321        height: types::Natural,
322        prev_header: &types::BlockHeader,
323        prev_headers: &[types::BlockHeader],
324        coinbase_script: &types::ByteString,
325        coinbase_address: &types::ByteString,
326    ) -> error::Result<types::Block> {
327        mining::create_new_block(
328            utxo_set,
329            mempool_txs,
330            height,
331            prev_header,
332            prev_headers,
333            coinbase_script,
334            coinbase_address,
335        )
336    }
337
338    /// Create new block with explicit time, network, and optional per-tx witness stacks.
339    #[allow(clippy::too_many_arguments)]
340    #[spec_locked("12.1", "CreateNewBlock")]
341    pub fn create_new_block_with_time(
342        &self,
343        utxo_set: &types::UtxoSet,
344        mempool_txs: &[types::Transaction],
345        height: types::Natural,
346        prev_header: &types::BlockHeader,
347        prev_headers: &[types::BlockHeader],
348        coinbase_script: &types::ByteString,
349        coinbase_address: &types::ByteString,
350        block_time: types::Natural,
351        network: types::Network,
352        mempool_witnesses: Option<&[Option<Vec<segwit::Witness>>]>,
353    ) -> error::Result<types::Block> {
354        mining::create_new_block_with_time(
355            utxo_set,
356            mempool_txs,
357            height,
358            prev_header,
359            prev_headers,
360            coinbase_script,
361            coinbase_address,
362            block_time,
363            network,
364            mempool_witnesses,
365        )
366    }
367
368    /// Mine a block by finding valid nonce
369    #[spec_locked("12.3", "MineBlock")]
370    #[blvm_spec_lock::ensures(result_0 >= 0)]
371    pub fn mine_block(
372        &self,
373        block: types::Block,
374        max_attempts: types::Natural,
375    ) -> error::Result<(types::Block, mining::MiningResult)> {
376        mining::mine_block(block, max_attempts)
377    }
378
379    /// Create block template for mining
380    #[allow(clippy::too_many_arguments)]
381    #[spec_locked("12.4", "BlockTemplate")]
382    #[blvm_spec_lock::ensures(result >= 0)]
383    pub fn create_block_template(
384        &self,
385        utxo_set: &types::UtxoSet,
386        mempool_txs: &[types::Transaction],
387        height: types::Natural,
388        prev_header: &types::BlockHeader,
389        prev_headers: &[types::BlockHeader],
390        coinbase_script: &types::ByteString,
391        coinbase_address: &types::ByteString,
392        network: types::Network,
393        mempool_witnesses: Option<&[Option<Vec<segwit::Witness>>]>,
394    ) -> error::Result<mining::BlockTemplate> {
395        mining::create_block_template(
396            utxo_set,
397            mempool_txs,
398            height,
399            prev_header,
400            prev_headers,
401            coinbase_script,
402            coinbase_address,
403            network,
404            mempool_witnesses,
405        )
406    }
407
408    /// Reorganize chain when a longer chain is found (legacy: synthesizes empty witnesses).
409    #[deprecated(
410        since = "0.1.33",
411        note = "Use reorganize_chain_with_witnesses with explicit witness data for SegWit chains"
412    )]
413    #[spec_locked("11.3")]
414    #[blvm_spec_lock::ensures(result >= 0)]
415    pub fn reorganize_chain(
416        &self,
417        new_chain: &[types::Block],
418        current_chain: &[types::Block],
419        current_utxo_set: types::UtxoSet,
420        current_height: types::Natural,
421        network: types::Network,
422    ) -> error::Result<reorganization::ReorganizationResult> {
423        use crate::segwit::{Witness, is_segwit_transaction};
424        use crate::transaction::is_coinbase;
425
426        for block in new_chain {
427            for tx in &block.transactions {
428                if !is_coinbase(tx) && is_segwit_transaction(tx) {
429                    return Err(error::ConsensusError::BlockValidation(
430                        "reorganize_chain: SegWit transactions require reorganize_chain_with_witnesses"
431                            .into(),
432                    ));
433                }
434            }
435        }
436
437        let witnesses: Vec<Vec<Vec<Witness>>> = new_chain
438            .iter()
439            .map(|block| {
440                block
441                    .transactions
442                    .iter()
443                    .map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
444                    .collect()
445            })
446            .collect();
447        let network_time = new_chain
448            .iter()
449            .map(|b| b.header.timestamp)
450            .max()
451            .unwrap_or(0)
452            .saturating_add(crate::constants::MAX_FUTURE_BLOCK_TIME);
453
454        reorganization::reorganize_chain_with_witnesses(
455            new_chain,
456            &witnesses,
457            None,
458            current_chain,
459            current_utxo_set,
460            current_height,
461            None::<fn(&types::Block) -> Option<Vec<segwit::Witness>>>,
462            None::<fn(types::Natural) -> Option<Vec<types::BlockHeader>>>,
463            None::<fn(&types::Hash) -> Option<reorganization::BlockUndoLog>>,
464            None::<fn(&types::Hash, &reorganization::BlockUndoLog) -> error::Result<()>>,
465            network_time,
466            network,
467        )
468    }
469
470    /// Reorganize chain with explicit witness data (preferred API).
471    #[spec_locked("11.3")]
472    pub fn reorganize_chain_with_witnesses(
473        &self,
474        new_chain: &[types::Block],
475        new_chain_witnesses: &[Vec<Vec<segwit::Witness>>],
476        new_chain_headers: Option<&[types::BlockHeader]>,
477        current_chain: &[types::Block],
478        current_utxo_set: types::UtxoSet,
479        current_height: types::Natural,
480        network_time: types::Natural,
481        network: types::Network,
482    ) -> error::Result<reorganization::ReorganizationResult> {
483        reorganization::reorganize_chain_with_witnesses(
484            new_chain,
485            new_chain_witnesses,
486            new_chain_headers,
487            current_chain,
488            current_utxo_set,
489            current_height,
490            None::<fn(&types::Block) -> Option<Vec<segwit::Witness>>>,
491            None::<fn(types::Natural) -> Option<Vec<types::BlockHeader>>>,
492            None::<fn(&types::Hash) -> Option<reorganization::BlockUndoLog>>,
493            None::<fn(&types::Hash, &reorganization::BlockUndoLog) -> error::Result<()>>,
494            network_time,
495            network,
496        )
497    }
498
499    /// Check if reorganization is beneficial
500    #[spec_locked("11.3", "ShouldReorganize")]
501    #[blvm_spec_lock::ensures(result == true || result == false)]
502    pub fn should_reorganize(
503        &self,
504        new_chain: &[types::Block],
505        current_chain: &[types::Block],
506    ) -> error::Result<bool> {
507        reorganization::should_reorganize(new_chain, current_chain)
508    }
509
510    /// Calculate transaction weight for SegWit
511    #[spec_locked("11.1.1", "CalculateTransactionWeight")]
512    #[blvm_spec_lock::ensures(result >= 0)]
513    pub fn calculate_transaction_weight(
514        &self,
515        tx: &types::Transaction,
516        witness: Option<&segwit::Witness>,
517    ) -> error::Result<types::Natural> {
518        segwit::calculate_transaction_weight(tx, witness)
519    }
520
521    /// Validate SegWit block
522    #[spec_locked("11.1.7", "ValidateSegWitBlock")]
523    #[blvm_spec_lock::ensures(result == true || result == false)]
524    pub fn validate_segwit_block(
525        &self,
526        block: &types::Block,
527        witnesses: &[segwit::Witness],
528        max_block_weight: types::Natural,
529    ) -> error::Result<bool> {
530        segwit::validate_segwit_block(block, witnesses, max_block_weight)
531    }
532
533    /// Validate Taproot transaction
534    #[spec_locked("11.2.5", "ValidateTaprootTransaction")]
535    #[blvm_spec_lock::ensures(result == true || result == false)]
536    pub fn validate_taproot_transaction(
537        &self,
538        tx: &types::Transaction,
539        witness: Option<&segwit::Witness>,
540    ) -> error::Result<bool> {
541        taproot::validate_taproot_transaction(tx, witness)
542    }
543
544    /// Check if transaction output is Taproot
545    #[spec_locked("11.2.1", "IsTaprootOutput")]
546    #[blvm_spec_lock::ensures(result == true || result == false)]
547    pub fn is_taproot_output(&self, output: &types::TransactionOutput) -> bool {
548        taproot::is_taproot_output(output)
549    }
550}
551
552#[cfg(test)]
553mod tests {
554    use crate::transaction::check_transaction;
555    use crate::types::Transaction;
556
557    #[test]
558    fn test_validate_transaction() {
559        let tx = Transaction {
560            version: 1,
561            inputs: vec![].into(),
562            outputs: vec![].into(),
563            lock_time: 0,
564        };
565        let result = check_transaction(&tx);
566        assert!(result.is_ok());
567    }
568}