use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Instant;
use std::thread;
use bitcoin::secp256k1::{PublicKey, Signature, Message, Secp256k1};
use bitcoin::hashes::{Hash, sha256};
use anyhow::{Result, anyhow};
use super::DLCOracle;
use crate::hardware_optimization::{HardwareOptimizationManager, OptimizableOperation, HardwareType};
use crate::hardware_optimization::intel::{IntelOptimizer, BatchVerificationConfig};
use crate::bitcoin::error::{BitcoinResult, BitcoinError};
#[derive(Debug, Default, Clone)]
pub struct DLCBatchVerificationStats {
pub verifications_processed: usize,
pub batches_processed: usize,
pub invalid_count: usize,
pub avg_verification_time_us: f64,
pub last_batch_size: usize,
pub last_verification_time_us: u128,
}
pub struct DLCOracleBatchVerifier {
hw_manager: Arc<HardwareOptimizationManager>,
batch_queue: Vec<(String, Signature, PublicKey)>,
max_batch_size: usize,
verification_stats: DLCBatchVerificationStats,
secp: Secp256k1<bitcoin::secp256k1::VerifyOnly>,
}
impl DLCOracleBatchVerifier {
pub fn new() -> Self {
let hw_manager = Arc::new(HardwareOptimizationManager::new());
let max_batch_size = if let Some(intel) = hw_manager.intel_optimizer() {
if intel.capabilities().kaby_lake_optimized {
384 } else if intel.capabilities().avx2_support {
256 } else {
128 }
} else {
64 };
Self {
hw_manager,
batch_queue: Vec::with_capacity(max_batch_size),
max_batch_size,
verification_stats: DLCBatchVerificationStats::default(),
secp: Secp256k1::verification_only(),
}
}
pub fn queue_verification(&mut self, outcome: String, signature: Signature, oracle_pubkey: PublicKey) -> bool {
self.batch_queue.push((outcome, signature, oracle_pubkey));
if self.batch_queue.len() >= self.max_batch_size {
self.process_batch()
} else {
true }
}
pub fn flush(&mut self) -> bool {
if self.batch_queue.is_empty() {
return true;
}
self.process_batch()
}
fn process_batch(&mut self) -> bool {
if self.batch_queue.is_empty() {
return true;
}
let start_time = Instant::now();
let batch_size = self.batch_queue.len();
self.verification_stats.last_batch_size = batch_size;
let result = if let Some(intel_opt) = self.hw_manager.intel_optimizer() {
self.process_batch_intel(intel_opt)
} else {
self.process_batch_standard()
};
let elapsed = start_time.elapsed();
let elapsed_micros = elapsed.as_micros();
let per_sig_micros = elapsed_micros as f64 / batch_size as f64;
self.verification_stats.verifications_processed += batch_size;
self.verification_stats.batches_processed += 1;
self.verification_stats.last_verification_time_us = elapsed_micros;
self.verification_stats.avg_verification_time_us =
((self.verification_stats.avg_verification_time_us *
(self.verification_stats.batches_processed - 1) as f64) + per_sig_micros) /
self.verification_stats.batches_processed as f64;
self.batch_queue.clear();
result
}
fn process_batch_intel(&mut self, intel_opt: Arc<IntelOptimizer>) -> bool {
let batch_config = BatchVerificationConfig {
batch_size: self.batch_queue.len(),
use_avx2: intel_opt.capabilities().avx2_support,
kaby_lake_optimized: intel_opt.capabilities().kaby_lake_optimized,
parallel: true, };
let (messages, signatures, pubkeys): (Vec<Message>, Vec<Signature>, Vec<PublicKey>) =
self.prepare_batch_data();
if batch_config.kaby_lake_optimized && intel_opt.capabilities().kaby_lake_optimized {
self.process_batch_kaby_lake(messages, signatures, pubkeys)
} else if batch_config.use_avx2 && intel_opt.capabilities().avx2_support {
self.process_batch_avx2(messages, signatures, pubkeys)
} else {
self.process_batch_sequential(messages, signatures, pubkeys)
}
}
fn process_batch_kaby_lake(&mut self, messages: Vec<Message>, signatures: Vec<Signature>, pubkeys: Vec<PublicKey>) -> bool {
let thread_count = std::cmp::min(
4, self.batch_queue.len().min(8) );
if thread_count <= 1 || self.batch_queue.len() < 8 {
return self.process_batch_sequential(messages, signatures, pubkeys);
}
let invalid_count = Arc::new(Mutex::new(0));
let chunk_size = (self.batch_queue.len() + thread_count - 1) / thread_count;
let mut handles = Vec::with_capacity(thread_count);
for thread_idx in 0..thread_count {
let start_idx = thread_idx * chunk_size;
let end_idx = std::cmp::min((thread_idx + 1) * chunk_size, self.batch_queue.len());
if start_idx >= end_idx {
continue;
}
let thread_messages = messages[start_idx..end_idx].to_vec();
let thread_signatures = signatures[start_idx..end_idx].to_vec();
let thread_pubkeys = pubkeys[start_idx..end_idx].to_vec();
let invalid_count = Arc::clone(&invalid_count);
let secp = Secp256k1::verification_only();
let handle = thread::spawn(move || {
let mut thread_invalid = 0;
const L2_CHUNK_SIZE: usize = 16;
for chunk_start in (0..thread_messages.len()).step_by(L2_CHUNK_SIZE) {
let chunk_end = std::cmp::min(chunk_start + L2_CHUNK_SIZE, thread_messages.len());
for i in chunk_start..chunk_end {
if secp.verify_ecdsa(&thread_messages[i], &thread_signatures[i], &thread_pubkeys[i]).is_err() {
thread_invalid += 1;
}
}
}
let mut invalid = invalid_count.lock().unwrap();
*invalid += thread_invalid;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let invalid = *invalid_count.lock().unwrap();
self.verification_stats.invalid_count += invalid;
invalid == 0
}
fn process_batch_avx2(&mut self, messages: Vec<Message>, signatures: Vec<Signature>, pubkeys: Vec<PublicKey>) -> bool {
let thread_count = std::cmp::min(
4, self.batch_queue.len().min(8) );
if thread_count <= 1 || self.batch_queue.len() < 8 {
return self.process_batch_sequential(messages, signatures, pubkeys);
}
let invalid_count = Arc::new(Mutex::new(0));
let chunk_size = (self.batch_queue.len() + thread_count - 1) / thread_count;
let mut handles = Vec::with_capacity(thread_count);
for thread_idx in 0..thread_count {
let start_idx = thread_idx * chunk_size;
let end_idx = std::cmp::min((thread_idx + 1) * chunk_size, self.batch_queue.len());
if start_idx >= end_idx {
continue;
}
let thread_messages = messages[start_idx..end_idx].to_vec();
let thread_signatures = signatures[start_idx..end_idx].to_vec();
let thread_pubkeys = pubkeys[start_idx..end_idx].to_vec();
let invalid_count = Arc::clone(&invalid_count);
let secp = Secp256k1::verification_only();
let handle = thread::spawn(move || {
let mut thread_invalid = 0;
for i in 0..thread_messages.len() {
if secp.verify_ecdsa(&thread_messages[i], &thread_signatures[i], &thread_pubkeys[i]).is_err() {
thread_invalid += 1;
}
}
let mut invalid = invalid_count.lock().unwrap();
*invalid += thread_invalid;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let invalid = *invalid_count.lock().unwrap();
self.verification_stats.invalid_count += invalid;
invalid == 0
}
fn process_batch_sequential(&mut self, messages: Vec<Message>, signatures: Vec<Signature>, pubkeys: Vec<PublicKey>) -> bool {
let mut invalid = 0;
for i in 0..messages.len() {
if self.secp.verify_ecdsa(&messages[i], &signatures[i], &pubkeys[i]).is_err() {
invalid += 1;
}
}
self.verification_stats.invalid_count += invalid;
invalid == 0
}
fn process_batch_standard(&mut self) -> bool {
let (messages, signatures, pubkeys) = self.prepare_batch_data();
self.process_batch_sequential(messages, signatures, pubkeys)
}
fn prepare_batch_data(&self) -> (Vec<Message>, Vec<Signature>, Vec<PublicKey>) {
let mut messages = Vec::with_capacity(self.batch_queue.len());
let mut signatures = Vec::with_capacity(self.batch_queue.len());
let mut pubkeys = Vec::with_capacity(self.batch_queue.len());
for (outcome, signature, pubkey) in &self.batch_queue {
let outcome_hash = sha256::Hash::hash(outcome.as_bytes());
let message = Message::from_digest_slice(&outcome_hash[..]).unwrap();
messages.push(message);
signatures.push(*signature);
pubkeys.push(*pubkey);
}
(messages, signatures, pubkeys)
}
pub fn stats(&self) -> &DLCBatchVerificationStats {
&self.verification_stats
}
}
pub fn verify_oracle_signatures_batch(
verifications: &[(String, Signature, PublicKey)],
) -> BitcoinResult<bool> {
let mut verifier = DLCOracleBatchVerifier::new();
for (outcome, signature, pubkey) in verifications {
verifier.queue_verification(outcome.clone(), *signature, *pubkey);
}
let result = verifier.flush();
Ok(result)
}
pub fn verify_oracle_signature_optimized(
outcome: &str,
oracle_signature: &Signature,
oracle_public_key: &PublicKey,
) -> BitcoinResult<bool> {
verify_oracle_signatures_batch(&[(
outcome.to_string(),
*oracle_signature,
*oracle_public_key
)])
}
#[cfg(test)]
mod tests {
use super::*;
use bitcoin::secp256k1::{SecretKey, Secp256k1};
#[test]
fn test_batch_verification() {
let secp = Secp256k1::new();
let mut verifications = Vec::new();
for i in 0..10 {
let oracle_key = SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap();
let oracle_pubkey = PublicKey::from_secret_key(&secp, &oracle_key);
let outcome = format!("outcome-{}", i);
let outcome_hash = sha256::Hash::hash(outcome.as_bytes());
let message = Message::from_digest_slice(&outcome_hash[..]).unwrap();
let signature = secp.sign_ecdsa(&message, &oracle_key);
verifications.push((outcome, signature, oracle_pubkey));
}
let result = verify_oracle_signatures_batch(&verifications).unwrap();
assert!(result);
let (outcome, signature, pubkey) = &verifications[0];
let result = verify_oracle_signature_optimized(outcome, signature, pubkey).unwrap();
assert!(result);
}
}