use serde::{Deserialize, Serialize};
pub use blvm_consensus::config::NetworkMessageLimits;
pub use blvm_consensus::error::{ConsensusError, Result as ConsensusResult};
pub use blvm_consensus::types::{
Block, BlockHeader, ByteString, Hash, Integer, Natural, OutPoint, Transaction,
TransactionInput, TransactionOutput, UtxoSet, ValidationResult, Witness, UTXO,
};
pub use blvm_consensus::ConsensusProof;
#[cfg(all(feature = "production", feature = "benchmarking"))]
pub use blvm_consensus::config::{reset_assume_valid_height, set_assume_valid_height};
pub use blvm_consensus::{
BIP112_CSV_ACTIVATION_MAINNET, BIP112_CSV_ACTIVATION_REGTEST, BIP112_CSV_ACTIVATION_TESTNET,
GENESIS_BLOCK_HASH_INTERNAL, SEGWIT_ACTIVATION_MAINNET, SEGWIT_ACTIVATION_TESTNET,
TAPROOT_ACTIVATION_MAINNET, TAPROOT_ACTIVATION_TESTNET,
};
#[cfg(feature = "production")]
pub use smallvec;
#[cfg(feature = "production")]
pub use lru;
#[cfg(feature = "production")]
pub use rayon;
#[macro_export]
macro_rules! profile_log {
($($arg:tt)*) => {
::blvm_consensus::profile_log!($($arg)*)
};
}
pub use error::{ProtocolError, Result};
pub mod mempool {
pub use blvm_consensus::mempool::*;
}
pub mod segwit {
pub use blvm_consensus::segwit::*;
}
pub mod block {
pub use blvm_consensus::block::*;
use crate::types::{BlockHeader, Network};
#[inline]
pub fn block_validation_context_for_connect_ibd<H: AsRef<BlockHeader>>(
recent_headers: Option<&[H]>,
network_time: u64,
network: Network,
) -> BlockValidationContext {
BlockValidationContext::from_connect_block_ibd_args(
recent_headers,
network_time,
network,
None,
None,
)
}
}
pub mod mining {
pub use blvm_consensus::mining::*;
}
pub mod pow {
pub use blvm_consensus::pow::*;
}
pub mod witness {
pub use blvm_consensus::witness::*;
}
pub mod crypto {
pub use blvm_consensus::crypto::*;
}
pub mod transaction {
pub use blvm_consensus::transaction::*;
}
pub mod script {
pub use blvm_consensus::script::*;
}
pub mod transaction_hash {
pub use blvm_consensus::transaction_hash::*;
}
#[cfg(feature = "production")]
pub mod optimizations {
pub use blvm_consensus::optimizations::*;
}
pub mod constants {
pub use blvm_consensus::constants::*;
}
pub mod bip113 {
pub use blvm_consensus::bip113::*;
}
pub mod bip_validation {
pub use blvm_consensus::bip_validation::*;
}
pub mod utxo_overlay {
pub use blvm_consensus::utxo_overlay::*;
}
pub mod version_bits {
pub use blvm_consensus::version_bits::*;
}
pub mod activation {
pub use blvm_consensus::activation::*;
}
pub mod consensus_config {
pub use blvm_consensus::config::*;
}
#[cfg(feature = "test-utils")]
pub mod test_utils {
pub use blvm_consensus::test_utils::*;
}
pub use blvm_consensus::opcodes;
pub mod sigop {
pub use blvm_consensus::sigop::*;
}
#[cfg(feature = "utxo-commitments")]
pub mod utxo_commitments;
pub mod spam_filter;
pub mod serialization {
pub use blvm_consensus::serialization::*;
}
pub mod commons;
pub mod network;
pub mod node_tcp;
pub use node_tcp::{ProtocolMessage, TcpFramedParser};
pub mod p2p_commands;
pub mod p2p_frame;
pub mod p2p_framing;
pub mod service_flags;
pub mod varint;
#[cfg(all(
feature = "bip324",
any(target_arch = "x86_64", target_arch = "aarch64")
))]
pub mod v2_transport;
pub use commons::{
BanListMessage,
FilterPreferences,
FilteredBlockMessage,
GetBanListMessage,
GetFilteredBlockMessage,
GetUTXOProofMessage,
GetUTXOSetMessage,
UTXOCommitment,
UTXOProofMessage,
UTXOSetMessage,
};
pub use config::{
ProtocolConfig, ProtocolFeaturesConfig, ProtocolValidationConfig, ServiceFlagsConfig,
};
pub use network::{BlockMessage, CompactBlockMessage, TxMessage};
pub use service_flags::{commons as service_flags_commons, standard as service_flags_standard};
pub mod wire;
#[cfg(test)]
mod bip155_serialization_tests;
pub mod types {
pub use blvm_consensus::types::*;
}
#[cfg(feature = "production")]
pub use blvm_consensus::tx_inputs;
#[cfg(not(feature = "production"))]
pub use blvm_consensus::tx_inputs;
#[cfg(feature = "production")]
pub use blvm_consensus::tx_outputs;
#[cfg(not(feature = "production"))]
pub use blvm_consensus::tx_outputs;
pub mod error;
pub use economic::EconomicParameters;
pub use features::{ActivationMethod, FeatureActivation, FeatureContext, FeatureRegistry};
pub mod config;
pub mod economic;
pub mod features;
pub mod genesis;
pub mod network_params;
pub mod validation;
pub mod variants;
pub mod address; #[cfg(feature = "ctv")]
pub mod bip119 {
pub use blvm_consensus::bip119::*;
}
pub mod bip152; pub mod bip157; pub mod bip158; pub mod payment; pub mod time;
pub struct BitcoinProtocolEngine {
consensus: ConsensusProof,
protocol_version: ProtocolVersion,
network_params: NetworkParameters,
config: ProtocolConfig,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProtocolVersion {
BitcoinV1,
Testnet3,
Regtest,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NetworkParameters {
pub magic_bytes: [u8; 4],
pub default_port: u16,
pub genesis_block: Block,
pub max_target: u32,
pub halving_interval: u64,
pub network_name: String,
pub is_testnet: bool,
}
impl BitcoinProtocolEngine {
pub fn new(version: ProtocolVersion) -> Result<Self> {
Self::with_config(version, ProtocolConfig::default())
}
pub fn with_config(version: ProtocolVersion, config: ProtocolConfig) -> Result<Self> {
let consensus = ConsensusProof::new();
let network_params = NetworkParameters::for_version(version)?;
Ok(BitcoinProtocolEngine {
consensus,
protocol_version: version,
network_params,
config,
})
}
pub fn get_config(&self) -> &ProtocolConfig {
&self.config
}
pub fn get_config_mut(&mut self) -> &mut ProtocolConfig {
&mut self.config
}
pub fn get_protocol_version(&self) -> ProtocolVersion {
self.protocol_version
}
pub fn get_network_params(&self) -> &NetworkParameters {
&self.network_params
}
pub fn validate_block(
&self,
block: &Block,
utxos: &UtxoSet,
height: u64,
) -> Result<ValidationResult> {
let (result, _) = self
.consensus
.validate_block(block, utxos.clone(), height)
.map_err(ProtocolError::from)?;
Ok(result)
}
pub fn validate_transaction(&self, tx: &Transaction) -> Result<ValidationResult> {
self.consensus
.validate_transaction(tx)
.map_err(ProtocolError::from)
}
pub fn validate_and_connect_block(
&self,
block: &Block,
witnesses: &[Vec<segwit::Witness>], utxos: &UtxoSet,
height: u64,
recent_headers: Option<&[BlockHeader]>,
context: &validation::ProtocolValidationContext,
) -> Result<(ValidationResult, UtxoSet)> {
let protocol_result = self.validate_block_with_protocol(block, utxos, height, context)?;
if !matches!(protocol_result, ValidationResult::Valid) {
return Ok((protocol_result, utxos.clone()));
}
let network = match self.protocol_version {
ProtocolVersion::BitcoinV1 => types::Network::Mainnet,
ProtocolVersion::Testnet3 => types::Network::Testnet,
ProtocolVersion::Regtest => types::Network::Regtest,
};
let network_time = crate::time::current_timestamp();
let context = crate::block::block_validation_context_for_connect_ibd(
recent_headers,
network_time,
network,
);
let (result, new_utxo_set, _undo_log) = blvm_consensus::block::connect_block(
block,
witnesses,
utxos.clone(),
height,
&context,
)?;
Ok((result, new_utxo_set))
}
pub fn supports_feature(&self, feature: &str) -> bool {
match self.protocol_version {
ProtocolVersion::BitcoinV1 => {
matches!(feature, "segwit" | "taproot" | "rbf" | "ctv")
}
ProtocolVersion::Testnet3 => {
matches!(feature, "segwit" | "taproot" | "rbf" | "ctv")
}
ProtocolVersion::Regtest => {
matches!(
feature,
"segwit" | "taproot" | "rbf" | "ctv" | "fast_mining"
)
}
}
}
pub fn is_feature_active(&self, feature: &str, height: u64, timestamp: u64) -> bool {
let registry = features::FeatureRegistry::for_protocol(self.protocol_version);
registry.is_feature_active(feature, height, timestamp)
}
pub fn get_economic_parameters(&self) -> economic::EconomicParameters {
economic::EconomicParameters::for_protocol(self.protocol_version)
}
pub fn get_feature_registry(&self) -> features::FeatureRegistry {
features::FeatureRegistry::for_protocol(self.protocol_version)
}
pub fn feature_context(&self, height: u64, timestamp: u64) -> features::FeatureContext {
let registry = features::FeatureRegistry::for_protocol(self.protocol_version);
registry.create_context(height, timestamp)
}
}
impl NetworkParameters {
pub fn for_version(version: ProtocolVersion) -> Result<Self> {
match version {
ProtocolVersion::BitcoinV1 => Self::mainnet(),
ProtocolVersion::Testnet3 => Self::testnet(),
ProtocolVersion::Regtest => Self::regtest(),
}
}
pub fn mainnet() -> Result<Self> {
Ok(NetworkParameters {
magic_bytes: [0xf9, 0xbe, 0xb4, 0xd9], default_port: 8333,
genesis_block: genesis::mainnet_genesis(),
max_target: 0x1d00ffff,
halving_interval: 210000,
network_name: "mainnet".to_string(),
is_testnet: false,
})
}
pub fn testnet() -> Result<Self> {
Ok(NetworkParameters {
magic_bytes: [0x0b, 0x11, 0x09, 0x07], default_port: 18333,
genesis_block: genesis::testnet_genesis(),
max_target: 0x1d00ffff,
halving_interval: 210000,
network_name: "testnet".to_string(),
is_testnet: true,
})
}
pub fn regtest() -> Result<Self> {
Ok(NetworkParameters {
magic_bytes: [0xfa, 0xbf, 0xb5, 0xda], default_port: 18444,
genesis_block: genesis::regtest_genesis(),
max_target: 0x207fffff, halving_interval: 150, network_name: "regtest".to_string(),
is_testnet: true,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use blvm_consensus::types::{BlockHeader, OutPoint, TransactionInput, TransactionOutput};
#[test]
fn test_blvm_protocol_creation() {
let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
assert_eq!(engine.get_protocol_version(), ProtocolVersion::BitcoinV1);
assert_eq!(engine.get_network_params().network_name, "mainnet");
}
#[test]
fn test_blvm_protocol_creation_all_variants() {
let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
assert_eq!(mainnet.get_protocol_version(), ProtocolVersion::BitcoinV1);
assert_eq!(mainnet.get_network_params().network_name, "mainnet");
assert!(!mainnet.get_network_params().is_testnet);
let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
assert_eq!(testnet.get_protocol_version(), ProtocolVersion::Testnet3);
assert_eq!(testnet.get_network_params().network_name, "testnet");
assert!(testnet.get_network_params().is_testnet);
let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
assert_eq!(regtest.get_protocol_version(), ProtocolVersion::Regtest);
assert_eq!(regtest.get_network_params().network_name, "regtest");
assert!(regtest.get_network_params().is_testnet);
}
#[test]
fn test_network_parameters() {
let mainnet = NetworkParameters::mainnet().unwrap();
assert_eq!(mainnet.magic_bytes, [0xf9, 0xbe, 0xb4, 0xd9]);
assert_eq!(mainnet.default_port, 8333);
assert!(!mainnet.is_testnet);
let testnet = NetworkParameters::testnet().unwrap();
assert_eq!(testnet.magic_bytes, [0x0b, 0x11, 0x09, 0x07]);
assert_eq!(testnet.default_port, 18333);
assert!(testnet.is_testnet);
let regtest = NetworkParameters::regtest().unwrap();
assert_eq!(regtest.magic_bytes, [0xfa, 0xbf, 0xb5, 0xda]);
assert_eq!(regtest.default_port, 18444);
assert!(regtest.is_testnet);
}
#[test]
fn test_network_parameters_consistency() {
let mainnet = NetworkParameters::mainnet().unwrap();
assert_eq!(mainnet.max_target, 0x1d00ffff);
assert_eq!(mainnet.halving_interval, 210000);
let testnet = NetworkParameters::testnet().unwrap();
assert_eq!(testnet.max_target, 0x1d00ffff);
assert_eq!(testnet.halving_interval, 210000);
let regtest = NetworkParameters::regtest().unwrap();
assert_eq!(regtest.max_target, 0x207fffff); assert_eq!(regtest.halving_interval, 150); }
#[test]
fn test_feature_support() {
let mainnet = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
assert!(mainnet.supports_feature("segwit"));
assert!(mainnet.supports_feature("taproot"));
assert!(mainnet.supports_feature("rbf"));
assert!(mainnet.supports_feature("ctv"));
assert!(!mainnet.supports_feature("fast_mining"));
assert!(!mainnet.supports_feature("nonexistent"));
let testnet = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
assert!(testnet.supports_feature("segwit"));
assert!(testnet.supports_feature("taproot"));
assert!(testnet.supports_feature("rbf"));
assert!(testnet.supports_feature("ctv"));
assert!(!testnet.supports_feature("fast_mining"));
let regtest = BitcoinProtocolEngine::new(ProtocolVersion::Regtest).unwrap();
assert!(regtest.supports_feature("segwit"));
assert!(regtest.supports_feature("taproot"));
assert!(regtest.supports_feature("rbf"));
assert!(regtest.supports_feature("ctv"));
assert!(regtest.supports_feature("fast_mining"));
}
#[test]
fn test_block_validation_empty_utxos() {
let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
let utxos = UtxoSet::default();
let coinbase_tx = Transaction {
version: 1,
inputs: blvm_consensus::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [0u8; 32],
index: 0xffffffff,
},
script_sig: vec![0x01, 0x00], sequence: 0xffffffff,
}],
outputs: blvm_consensus::tx_outputs![TransactionOutput {
value: 50_0000_0000,
script_pubkey: vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
blvm_consensus::opcodes::OP_EQUALVERIFY,
blvm_consensus::opcodes::OP_CHECKSIG,
], }],
lock_time: 0,
};
let merkle_root = blvm_consensus::mining::calculate_merkle_root(&[coinbase_tx.clone()])
.expect("Should calculate merkle root");
let block = Block {
header: BlockHeader {
version: 1,
prev_block_hash: [0u8; 32],
merkle_root,
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
},
transactions: vec![coinbase_tx].into_boxed_slice(),
};
let result = engine.validate_block(&block, &utxos, 0);
assert!(result.is_ok());
}
#[test]
fn test_transaction_validation() {
let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0u8; 32],
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::PUSH_65_BYTES, 0x04],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_0000_0000,
script_pubkey: vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
blvm_consensus::opcodes::OP_EQUALVERIFY,
blvm_consensus::opcodes::OP_CHECKSIG,
], }]
.into(),
lock_time: 0,
};
let result = engine.validate_transaction(&tx);
assert!(result.is_ok());
}
#[test]
fn test_cross_protocol_validation() {
let mainnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
let testnet_engine = BitcoinProtocolEngine::new(ProtocolVersion::Testnet3).unwrap();
assert_eq!(
mainnet_engine.supports_feature("segwit"),
testnet_engine.supports_feature("segwit")
);
assert_eq!(
mainnet_engine.supports_feature("taproot"),
testnet_engine.supports_feature("taproot")
);
assert_ne!(
mainnet_engine.get_network_params().magic_bytes,
testnet_engine.get_network_params().magic_bytes
);
assert_ne!(
mainnet_engine.get_network_params().default_port,
testnet_engine.get_network_params().default_port
);
}
#[test]
fn test_protocol_version_switching() {
let versions = vec![
ProtocolVersion::BitcoinV1,
ProtocolVersion::Testnet3,
ProtocolVersion::Regtest,
];
for version in versions {
let engine = BitcoinProtocolEngine::new(version).unwrap();
assert_eq!(engine.get_protocol_version(), version);
}
}
#[test]
fn test_network_parameters_serialization() {
let mainnet = NetworkParameters::mainnet().unwrap();
let testnet = NetworkParameters::testnet().unwrap();
let regtest = NetworkParameters::regtest().unwrap();
let mainnet_json = serde_json::to_string(&mainnet).unwrap();
let mainnet_deserialized: NetworkParameters = serde_json::from_str(&mainnet_json).unwrap();
assert_eq!(mainnet.magic_bytes, mainnet_deserialized.magic_bytes);
assert_eq!(mainnet.default_port, mainnet_deserialized.default_port);
assert_eq!(mainnet.network_name, mainnet_deserialized.network_name);
assert_eq!(mainnet.is_testnet, mainnet_deserialized.is_testnet);
let testnet_json = serde_json::to_string(&testnet).unwrap();
let testnet_deserialized: NetworkParameters = serde_json::from_str(&testnet_json).unwrap();
assert_eq!(testnet.magic_bytes, testnet_deserialized.magic_bytes);
let regtest_json = serde_json::to_string(®test).unwrap();
let regtest_deserialized: NetworkParameters = serde_json::from_str(®test_json).unwrap();
assert_eq!(regtest.magic_bytes, regtest_deserialized.magic_bytes);
}
#[test]
fn test_protocol_version_serialization() {
let versions = vec![
ProtocolVersion::BitcoinV1,
ProtocolVersion::Testnet3,
ProtocolVersion::Regtest,
];
for version in versions {
let json = serde_json::to_string(&version).unwrap();
let deserialized: ProtocolVersion = serde_json::from_str(&json).unwrap();
assert_eq!(version, deserialized);
}
}
#[test]
fn test_network_parameters_equality() {
let mainnet1 = NetworkParameters::mainnet().unwrap();
let mainnet2 = NetworkParameters::mainnet().unwrap();
let testnet = NetworkParameters::testnet().unwrap();
assert_eq!(mainnet1, mainnet2);
assert_ne!(mainnet1, testnet);
}
#[test]
fn test_protocol_version_equality() {
assert_eq!(ProtocolVersion::BitcoinV1, ProtocolVersion::BitcoinV1);
assert_ne!(ProtocolVersion::BitcoinV1, ProtocolVersion::Testnet3);
assert_ne!(ProtocolVersion::Testnet3, ProtocolVersion::Regtest);
}
#[test]
fn test_feature_activation_by_height() {
let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
assert!(!engine.is_feature_active("segwit", 481_823, 1503539000));
assert!(engine.is_feature_active("segwit", 481_824, 1503539857));
assert!(engine.is_feature_active("segwit", 500_000, 1504000000));
assert!(!engine.is_feature_active("taproot", 709_631, 1636934000));
assert!(engine.is_feature_active("taproot", 709_632, 1636934400));
assert!(engine.is_feature_active("taproot", 800_000, 1640000000));
}
#[test]
fn test_economic_parameters_access() {
let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
let params = engine.get_economic_parameters();
assert_eq!(params.initial_subsidy, 50_0000_0000);
assert_eq!(params.halving_interval, 210_000);
assert_eq!(params.coinbase_maturity, 100);
assert_eq!(params.get_block_subsidy(0), 50_0000_0000);
assert_eq!(params.get_block_subsidy(210_000), 25_0000_0000);
}
#[test]
fn test_feature_registry_access() {
let engine = BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap();
let registry = engine.get_feature_registry();
assert!(registry.get_feature("segwit").is_some());
assert!(registry.get_feature("taproot").is_some());
assert!(registry.get_feature("nonexistent").is_none());
let features = registry.list_features();
assert!(features.contains(&"segwit".to_string()));
assert!(features.contains(&"taproot".to_string()));
}
}