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, dead_code)] // Many paths are feature-gated or used conditionally
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 sigop;
115pub(crate) mod spec_witnesses;
116pub mod taproot;
117pub mod utxo_overlay;
118pub mod version_bits;
119pub mod witness;
120
121// Integration tests link this crate without `cfg(test)` on the library, so `test_utils` cannot be
122// gated only on `test`. Fixture helpers are small; `property-tests`/`proptest` stays gated inside the module.
123pub mod test_utils;
124
125#[cfg(feature = "profile")]
126pub mod profile_log;
127#[cfg(all(feature = "production", feature = "profile"))]
128pub mod script_profile;
129
130/// Consensus Proof - wrapper struct for consensus validation functions
131///
132/// This struct provides a convenient API for accessing all consensus validation
133/// functions. All methods delegate to the corresponding module functions.
134#[derive(Debug, Clone, Copy, Default)]
135pub struct ConsensusProof;
136
137impl ConsensusProof {
138    /// Create a new ConsensusProof instance
139    pub fn new() -> Self {
140        Self
141    }
142
143    /// Validate a transaction according to consensus rules
144    #[spec_locked("5.1", "CheckTransaction")]
145    #[blvm_spec_lock::ensures(result == true || result == false)]
146    pub fn validate_transaction(
147        &self,
148        tx: &types::Transaction,
149    ) -> error::Result<types::ValidationResult> {
150        transaction::check_transaction(tx)
151    }
152
153    /// Validate transaction inputs against UTXO set
154    #[spec_locked("5.1", "CheckTxInputs")]
155    #[blvm_spec_lock::ensures(result_0 == true || result_0 == false)]
156    pub fn validate_tx_inputs(
157        &self,
158        tx: &types::Transaction,
159        utxo_set: &types::UtxoSet,
160        height: types::Natural,
161    ) -> error::Result<(types::ValidationResult, types::Integer)> {
162        transaction::check_tx_inputs(tx, utxo_set, height)
163    }
164
165    /// Validate a complete block
166    #[spec_locked("5.3", "ConnectBlock")]
167    #[blvm_spec_lock::ensures(result_0 == true || result_0 == false)]
168    pub fn validate_block(
169        &self,
170        block: &types::Block,
171        utxo_set: types::UtxoSet,
172        height: types::Natural,
173    ) -> error::Result<(types::ValidationResult, types::UtxoSet)> {
174        // Create empty witnesses for backward compatibility
175        let witnesses: Vec<Vec<segwit::Witness>> =
176            block.transactions.iter().map(|_| Vec::new()).collect();
177        // Safe time read: unwrap_or avoids panic when clock is before epoch.
178        // NOTE: This entry point defaults to Network::Mainnet. Callers that need
179        // a different network should use `validate_block_with_time_context` instead.
180        let network_time = std::time::SystemTime::now()
181            .duration_since(std::time::UNIX_EPOCH)
182            .unwrap_or(std::time::Duration::ZERO)
183            .as_secs();
184        let context = block::block_validation_context_for_connect_ibd(
185            None::<&[types::BlockHeader]>,
186            network_time,
187            types::Network::Mainnet,
188        );
189        let (result, new_utxo_set, _undo_log) =
190            block::connect_block(block, &witnesses, utxo_set, height, &context)?;
191        Ok((result, new_utxo_set))
192    }
193
194    /// Validate a complete block with witness data and time context
195    #[spec_locked("5.3", "ConnectBlock")]
196    #[blvm_spec_lock::ensures(result_0 == true || result_0 == false)]
197    pub fn validate_block_with_time_context(
198        &self,
199        block: &types::Block,
200        witnesses: &[Vec<segwit::Witness>],
201        utxo_set: types::UtxoSet,
202        height: types::Natural,
203        time_context: Option<types::TimeContext>,
204        network: types::Network,
205    ) -> error::Result<(types::ValidationResult, types::UtxoSet)> {
206        let context = block::BlockValidationContext::from_time_context_and_network(
207            time_context,
208            network,
209            None,
210        );
211        let (result, new_utxo_set, _undo_log) =
212            block::connect_block(block, witnesses, utxo_set, height, &context)?;
213        Ok((result, new_utxo_set))
214    }
215
216    /// Verify script execution
217    #[spec_locked("5.2", "VerifyScript")]
218    #[blvm_spec_lock::ensures(result == true || result == false)]
219    pub fn verify_script(
220        &self,
221        script_sig: &types::ByteString,
222        script_pubkey: &types::ByteString,
223        witness: Option<&types::ByteString>,
224        flags: u32,
225    ) -> error::Result<bool> {
226        script::verify_script(script_sig, script_pubkey, witness, flags)
227    }
228
229    /// Check proof of work
230    #[spec_locked("7.2", "CheckProofOfWork")]
231    #[blvm_spec_lock::ensures(result == true || result == false)]
232    pub fn check_proof_of_work(&self, header: &types::BlockHeader) -> error::Result<bool> {
233        pow::check_proof_of_work(header)
234    }
235
236    /// Get block subsidy for height
237    #[spec_locked("6.1", "GetBlockSubsidy")]
238    #[blvm_spec_lock::ensures(result >= 0)]
239    #[blvm_spec_lock::axiom(result <= INITIAL_SUBSIDY)]
240    pub fn get_block_subsidy(&self, height: types::Natural) -> types::Integer {
241        economic::get_block_subsidy(height)
242    }
243
244    /// Calculate total supply at height
245    #[spec_locked("6.2", "TotalSupply")]
246    #[blvm_spec_lock::ensures(result >= 0)]
247    pub fn total_supply(&self, height: types::Natural) -> types::Integer {
248        economic::total_supply(height)
249    }
250
251    /// Get next work required for difficulty adjustment
252    #[spec_locked("7.1", "GetNextWorkRequired")]
253    #[blvm_spec_lock::ensures(result >= 0)]
254    pub fn get_next_work_required(
255        &self,
256        current_header: &types::BlockHeader,
257        prev_headers: &[types::BlockHeader],
258    ) -> error::Result<types::Natural> {
259        pow::get_next_work_required(current_header, prev_headers)
260    }
261
262    /// Accept transaction to memory pool
263    #[spec_locked("9.1", "AcceptToMemoryPool")]
264    #[blvm_spec_lock::ensures(result == true || result == false)]
265    pub fn accept_to_memory_pool(
266        &self,
267        tx: &types::Transaction,
268        utxo_set: &types::UtxoSet,
269        mempool: &mempool::Mempool,
270        height: types::Natural,
271        time_context: Option<types::TimeContext>,
272    ) -> error::Result<mempool::MempoolResult> {
273        mempool::accept_to_memory_pool(tx, None, utxo_set, mempool, height, time_context)
274    }
275
276    /// Check if transaction is standard
277    #[spec_locked("9.2", "IsStandardTx")]
278    #[blvm_spec_lock::ensures(result == true || result == false)]
279    pub fn is_standard_tx(&self, tx: &types::Transaction) -> error::Result<bool> {
280        mempool::is_standard_tx(tx)
281    }
282
283    /// Check if transaction can replace existing one (RBF)
284    #[spec_locked("9.3", "ReplacementChecks")]
285    #[blvm_spec_lock::ensures(result == true || result == false)]
286    pub fn replacement_checks(
287        &self,
288        new_tx: &types::Transaction,
289        existing_tx: &types::Transaction,
290        utxo_set: &types::UtxoSet,
291        mempool: &mempool::Mempool,
292    ) -> error::Result<bool> {
293        mempool::replacement_checks(new_tx, existing_tx, utxo_set, mempool)
294    }
295
296    /// Create new block from mempool transactions
297    #[allow(clippy::too_many_arguments)]
298    #[spec_locked("12.1", "CreateNewBlock")]
299    #[blvm_spec_lock::ensures(result >= 0)]
300    pub fn create_new_block(
301        &self,
302        utxo_set: &types::UtxoSet,
303        mempool_txs: &[types::Transaction],
304        height: types::Natural,
305        prev_header: &types::BlockHeader,
306        prev_headers: &[types::BlockHeader],
307        coinbase_script: &types::ByteString,
308        coinbase_address: &types::ByteString,
309    ) -> error::Result<types::Block> {
310        mining::create_new_block(
311            utxo_set,
312            mempool_txs,
313            height,
314            prev_header,
315            prev_headers,
316            coinbase_script,
317            coinbase_address,
318        )
319    }
320
321    /// Mine a block by finding valid nonce
322    #[spec_locked("12.3", "MineBlock")]
323    #[blvm_spec_lock::ensures(result_0 >= 0)]
324    pub fn mine_block(
325        &self,
326        block: types::Block,
327        max_attempts: types::Natural,
328    ) -> error::Result<(types::Block, mining::MiningResult)> {
329        mining::mine_block(block, max_attempts)
330    }
331
332    /// Create block template for mining
333    #[allow(clippy::too_many_arguments)]
334    #[spec_locked("12.4", "BlockTemplate")]
335    #[blvm_spec_lock::ensures(result >= 0)]
336    pub fn create_block_template(
337        &self,
338        utxo_set: &types::UtxoSet,
339        mempool_txs: &[types::Transaction],
340        height: types::Natural,
341        prev_header: &types::BlockHeader,
342        prev_headers: &[types::BlockHeader],
343        coinbase_script: &types::ByteString,
344        coinbase_address: &types::ByteString,
345    ) -> error::Result<mining::BlockTemplate> {
346        mining::create_block_template(
347            utxo_set,
348            mempool_txs,
349            height,
350            prev_header,
351            prev_headers,
352            coinbase_script,
353            coinbase_address,
354        )
355    }
356
357    /// Reorganize chain when longer chain is found
358    #[spec_locked("11.3")]
359    #[blvm_spec_lock::ensures(result >= 0)]
360    pub fn reorganize_chain(
361        &self,
362        new_chain: &[types::Block],
363        current_chain: &[types::Block],
364        current_utxo_set: types::UtxoSet,
365        current_height: types::Natural,
366        network: types::Network,
367    ) -> error::Result<reorganization::ReorganizationResult> {
368        reorganization::reorganize_chain(
369            new_chain,
370            current_chain,
371            current_utxo_set,
372            current_height,
373            network,
374        )
375    }
376
377    /// Check if reorganization is beneficial
378    #[spec_locked("11.3", "ShouldReorganize")]
379    #[blvm_spec_lock::ensures(result == true || result == false)]
380    pub fn should_reorganize(
381        &self,
382        new_chain: &[types::Block],
383        current_chain: &[types::Block],
384    ) -> error::Result<bool> {
385        reorganization::should_reorganize(new_chain, current_chain)
386    }
387
388    /// Calculate transaction weight for SegWit
389    #[spec_locked("11.1.1", "CalculateTransactionWeight")]
390    #[blvm_spec_lock::ensures(result >= 0)]
391    pub fn calculate_transaction_weight(
392        &self,
393        tx: &types::Transaction,
394        witness: Option<&segwit::Witness>,
395    ) -> error::Result<types::Natural> {
396        segwit::calculate_transaction_weight(tx, witness)
397    }
398
399    /// Validate SegWit block
400    #[spec_locked("11.1.7", "ValidateSegWitBlock")]
401    #[blvm_spec_lock::ensures(result == true || result == false)]
402    pub fn validate_segwit_block(
403        &self,
404        block: &types::Block,
405        witnesses: &[segwit::Witness],
406        max_block_weight: types::Natural,
407    ) -> error::Result<bool> {
408        segwit::validate_segwit_block(block, witnesses, max_block_weight)
409    }
410
411    /// Validate Taproot transaction
412    #[spec_locked("11.2.5", "ValidateTaprootTransaction")]
413    #[blvm_spec_lock::ensures(result == true || result == false)]
414    pub fn validate_taproot_transaction(
415        &self,
416        tx: &types::Transaction,
417        witness: Option<&segwit::Witness>,
418    ) -> error::Result<bool> {
419        taproot::validate_taproot_transaction(tx, witness)
420    }
421
422    /// Check if transaction output is Taproot
423    #[spec_locked("11.2.1", "IsTaprootOutput")]
424    #[blvm_spec_lock::ensures(result == true || result == false)]
425    pub fn is_taproot_output(&self, output: &types::TransactionOutput) -> bool {
426        taproot::is_taproot_output(output)
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use crate::transaction::check_transaction;
433    use crate::types::Transaction;
434
435    #[test]
436    fn test_validate_transaction() {
437        let tx = Transaction {
438            version: 1,
439            inputs: vec![].into(),
440            outputs: vec![].into(),
441            lock_time: 0,
442        };
443        let result = check_transaction(&tx);
444        assert!(result.is_ok());
445    }
446}