use bitcoin::{Transaction, Block, BlockHeader};
use thiserror::Error;
use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{SystemTime, UNIX_EPOCH, Duration};
use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
use rayon::prelude::*;
use crate::bitcoin::validation::{TransactionValidator, ValidationError, VerificationRecord};
use crate::bitcoin::protocol::{BitcoinProtocol, BPCLevel, BitcoinError};
use crate::hardware_optimization::{HardwareOptimizationManager, OptimizableOperation, HardwareType};
#[derive(Debug, Clone)]
pub struct ConsensusViolation {
pub transaction: Transaction,
pub expected_result: Result<(), ValidationError>,
pub actual_result: Result<(), ValidationError>,
pub timestamp: u64,
pub context: HashMap<String, String>,
}
#[derive(Debug, Error)]
pub enum DifferentialFuzzerError {
#[error("Consensus violation detected: {0}")]
ConsensusViolation(String),
#[error("Reference client error: {0}")]
ReferenceClientError(String),
#[error("Test client error: {0}")]
TestClientError(String),
#[error("Mutation engine error: {0}")]
MutationEngineError(String),
}
pub struct MutationEngine {
base_transactions: Vec<Transaction>,
mutation_rate: f64,
max_mutations: usize,
}
impl MutationEngine {
pub fn new() -> Self {
Self {
base_transactions: Vec::new(),
mutation_rate: 0.05,
max_mutations: 5,
}
}
pub fn with_mutation_rate(mut self, rate: f64) -> Self {
self.mutation_rate = rate.max(0.0).min(1.0);
self
}
pub fn with_max_mutations(mut self, max: usize) -> Self {
self.max_mutations = max;
self
}
pub fn with_base_transactions(mut self, transactions: Vec<Transaction>) -> Self {
self.base_transactions = transactions;
self
}
pub fn generate_test_batch(&self, count: usize) -> Vec<Transaction> {
let mut rng = thread_rng();
let mut result = Vec::with_capacity(count);
if !self.base_transactions.is_empty() {
for _ in 0..count {
let base_idx = rng.gen_range(0..self.base_transactions.len());
let mut tx = self.base_transactions[base_idx].clone();
let mutations = rng.gen_range(1..=self.max_mutations);
for _ in 0..mutations {
if rng.gen_bool(self.mutation_rate) {
self.mutate_transaction(&mut tx);
}
}
result.push(tx);
}
} else {
for _ in 0..count {
result.push(self.generate_random_transaction());
}
}
result
}
fn mutate_transaction(&self, tx: &mut Transaction) {
let mut rng = thread_rng();
let mutation_type = rng.gen_range(0..5);
match mutation_type {
0 => {
tx.version = rng.gen_range(1..=4);
},
1 => {
let new_locktime = rng.gen_range(0..=0xffffffff);
tx.lock_time = bitcoin::LockTime::from_consensus(new_locktime);
},
2 => {
if !tx.input.is_empty() {
let input_idx = rng.gen_range(0..tx.input.len());
let sequence = rng.gen_range(0..=0xffffffff);
tx.input[input_idx].sequence = sequence;
}
},
3 => {
if !tx.output.is_empty() {
let output_idx = rng.gen_range(0..tx.output.len());
let value = rng.gen_range(0..=21_000_000_00000000); tx.output[output_idx].value = value;
}
},
_ => {
}
}
}
fn generate_random_transaction(&self) -> Transaction {
let mut rng = thread_rng();
let version = rng.gen_range(1..=4);
let lock_time = bitcoin::LockTime::from_consensus(rng.gen_range(0..=0xffffffff));
Transaction {
version,
lock_time,
input: vec![],
output: vec![],
}
}
}
pub struct BitcoinReferenceClient {
endpoint: String,
validation_cache: RwLock<HashMap<String, bool>>,
}
impl BitcoinReferenceClient {
pub fn new(endpoint: &str) -> Self {
Self {
endpoint: endpoint.to_string(),
validation_cache: RwLock::new(HashMap::new()),
}
}
pub fn validate_transaction(&self, tx: &Transaction) -> Result<(), ValidationError> {
let tx_hash = tx.txid().to_string();
if let Ok(cache) = self.validation_cache.read() {
if let Some(result) = cache.get(&tx_hash) {
return if *result {
Ok(())
} else {
Err(ValidationError::InvalidTransaction("Cached invalid transaction".into()))
};
}
}
let result = if tx.version < 1 || tx.version > 2 {
Err(ValidationError::InvalidTransaction("Invalid version".into()))
} else {
Ok(())
};
if let Ok(mut cache) = self.validation_cache.write() {
cache.insert(tx_hash, result.is_ok());
}
result
}
}
pub struct DifferentialFuzzer {
reference_client: Arc<BitcoinReferenceClient>,
test_client: Arc<TransactionValidator>,
mutation_engine: MutationEngine,
consensus_violations: RwLock<Vec<ConsensusViolation>>,
config: DifferentialFuzzerConfig,
}
#[derive(Debug, Clone)]
pub struct DifferentialFuzzerConfig {
pub iterations: usize,
pub batch_size: usize,
pub parallel: bool,
pub fail_fast: bool,
}
impl Default for DifferentialFuzzerConfig {
fn default() -> Self {
Self {
iterations: 100,
batch_size: 100,
parallel: true,
fail_fast: false,
}
}
}
impl DifferentialFuzzer {
pub fn new(
reference_client: Arc<BitcoinReferenceClient>,
test_client: Arc<TransactionValidator>,
) -> Self {
Self {
reference_client,
test_client,
mutation_engine: MutationEngine::new(),
consensus_violations: RwLock::new(Vec::new()),
config: DifferentialFuzzerConfig::default(),
}
}
pub fn with_mutation_engine(mut self, engine: MutationEngine) -> Self {
self.mutation_engine = engine;
self
}
pub fn with_config(mut self, config: DifferentialFuzzerConfig) -> Self {
self.config = config;
self
}
pub fn run(&self) -> Result<Vec<ConsensusViolation>, DifferentialFuzzerError> {
println!("Starting differential fuzzing with {} iterations of {} transactions each",
self.config.iterations, self.config.batch_size);
let start_time = SystemTime::now();
let mut violation_count = 0;
for i in 0..self.config.iterations {
let test_batch = self.mutation_engine.generate_test_batch(self.config.batch_size);
if self.config.parallel {
self.process_batch_parallel(&test_batch)?;
} else {
self.process_batch_sequential(&test_batch)?;
}
let current_violations = self.consensus_violations.read().unwrap().len();
let new_violations = current_violations - violation_count;
violation_count = current_violations;
if i % 10 == 0 || new_violations > 0 {
println!("Iteration {}/{}: {} transactions processed, {} violations found",
i+1, self.config.iterations, (i+1) * self.config.batch_size, violation_count);
}
if self.config.fail_fast && violation_count > 0 {
println!("Stopping early due to consensus violations (fail_fast=true)");
break;
}
}
let violations = self.consensus_violations.read().unwrap().clone();
let elapsed = start_time.elapsed().unwrap_or(Duration::from_secs(0));
println!("Differential fuzzing completed in {:.2}s", elapsed.as_secs_f64());
println!("Processed {} transactions, found {} consensus violations",
self.config.iterations * self.config.batch_size, violations.len());
Ok(violations)
}
fn process_batch_sequential(&self, batch: &[Transaction]) -> Result<(), DifferentialFuzzerError> {
for tx in batch {
self.compare_transaction(tx)?;
}
Ok(())
}
fn process_batch_parallel(&self, batch: &[Transaction]) -> Result<(), DifferentialFuzzerError> {
batch.par_iter().try_for_each(|tx| {
self.compare_transaction(tx)
})
}
fn compare_transaction(&self, tx: &Transaction) -> Result<(), DifferentialFuzzerError> {
let reference_result = self.reference_client.validate_transaction(tx);
let test_result = self.test_client.validate(tx);
let reference_ok = reference_result.is_ok();
let test_ok = test_result.is_ok();
if reference_ok != test_ok {
let violation = ConsensusViolation {
transaction: tx.clone(),
expected_result: reference_result.clone(),
actual_result: test_result.clone(),
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
context: HashMap::new(),
};
if let Ok(mut violations) = self.consensus_violations.write() {
violations.push(violation);
}
if self.config.fail_fast {
return Err(DifferentialFuzzerError::ConsensusViolation(
format!("Transaction {} validation mismatch: reference={}, test={}",
tx.txid(), reference_ok, test_ok)
));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_differential_fuzzer_basic() {
let reference_client = Arc::new(BitcoinReferenceClient::new("http://localhost:8332"));
let validator = Arc::new(TransactionValidator::new());
let fuzzer = DifferentialFuzzer::new(reference_client, validator)
.with_config(DifferentialFuzzerConfig {
iterations: 10,
batch_size: 10,
parallel: false,
fail_fast: false,
});
let violations = fuzzer.run().expect("Fuzzer should complete without errors");
assert!(violations.is_empty(), "Expected no consensus violations");
}
}