//! Mining benchmarks for Synor blockchain. //! //! Benchmarks: //! - kHeavyHash hashing throughput //! - Matrix initialization //! - Nonce search rate //! - Block template creation //! - Mining coordination //! //! Run with: cargo bench -p synor-mining --bench mining_bench use criterion::{ black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, }; use synor_mining::{ kheavyhash::{KHeavyHash, ParallelMiner}, matrix::{GfTables, HeavyMatrix, OptimizedMatrix}, miner::{BlockMiner, MinerConfig, ParallelBlockMiner}, template::{BlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, CoinbaseData, TemplateTransaction}, Target, }; use synor_types::{Address, Hash256, Network}; // ==================== Test Data Helpers ==================== /// Creates a test address. fn test_address() -> Address { Address::from_ed25519_pubkey(Network::Mainnet, &[0x42; 32]) } /// Creates a deterministic hash. fn test_hash(n: u8) -> Hash256 { let mut bytes = [0u8; 32]; bytes[0] = n; bytes[31] = n; Hash256::from_bytes(bytes) } /// Creates a test coinbase. fn test_coinbase(height: u64) -> CoinbaseData { CoinbaseBuilder::new(test_address(), height) .extra_data(b"Synor Benchmark Pool".to_vec()) .reward(500_00000000) .fees(1_00000000) .build() } /// Creates a template transaction. fn test_template_tx(n: u64) -> TemplateTransaction { TemplateTransaction { txid: test_hash((n % 256) as u8), data: vec![0x42u8; 250], // ~250 byte transaction fee: 1000 + (n * 100), mass: 1000 + (n * 50), } } /// Creates a block template with the specified number of transactions. fn create_test_template(tx_count: usize) -> BlockTemplate { let mut builder = BlockTemplateBuilder::new() .version(1) .add_parent(test_hash(1)) .add_parent(test_hash(2)) .selected_parent(test_hash(1)) .timestamp(1234567890000) .bits(0x207fffff) // Easy target for benchmarking .blue_score(1000) .coinbase(test_coinbase(1000)) .reward(500_00000000); for i in 0..tx_count { builder = builder.add_transaction(test_template_tx(i as u64)); } builder.build(1).unwrap() } // ==================== kHeavyHash Throughput ==================== fn kheavyhash_throughput_single(c: &mut Criterion) { let hasher = KHeavyHash::new(); let header = [0x42u8; 80]; let pre_hash = hasher.pre_hash(&header); let mut group = c.benchmark_group("kheavyhash_throughput"); group.throughput(Throughput::Elements(1)); group.bench_function("single_hash", |b| { let mut nonce = 0u64; b.iter(|| { let result = hasher.finalize(&pre_hash, nonce); nonce = nonce.wrapping_add(1); black_box(result) }) }); // Batch throughput for batch_size in [100, 1000, 10000] { group.throughput(Throughput::Elements(batch_size)); group.bench_with_input( BenchmarkId::new("batch", batch_size), &batch_size, |b, &size| { b.iter(|| { for nonce in 0..size { black_box(hasher.finalize(&pre_hash, nonce)); } }) }, ); } group.finish(); } fn kheavyhash_sustained_rate(c: &mut Criterion) { let hasher = KHeavyHash::new(); let header = [0xAB; 80]; let pre_hash = hasher.pre_hash(&header); c.bench_function("kheavyhash_sustained_10k", |b| { b.iter(|| { let mut count = 0u64; for nonce in 0..10000 { let pow = hasher.finalize(&pre_hash, nonce); // Simulate target check if pow.hash.as_bytes()[0] == 0 { count += 1; } } black_box(count) }) }); } // ==================== Matrix Initialization ==================== fn matrix_initialization(c: &mut Criterion) { let mut group = c.benchmark_group("matrix_init"); group.bench_function("heavy_matrix_new", |b| { b.iter(|| black_box(HeavyMatrix::new())) }); group.bench_function("optimized_matrix_new", |b| { b.iter(|| black_box(OptimizedMatrix::new())) }); group.bench_function("gf_tables_new", |b| { b.iter(|| black_box(GfTables::new())) }); // Custom seed initialization for seed_idx in [0u8, 42u8, 255u8] { let seed = [seed_idx; 32]; group.bench_with_input( BenchmarkId::new("matrix_from_seed", seed_idx), &seed, |b, s| { b.iter(|| black_box(HeavyMatrix::from_seed(s))) }, ); } group.finish(); } fn matrix_memory_patterns(c: &mut Criterion) { let optimized = OptimizedMatrix::new(); let mut group = c.benchmark_group("matrix_patterns"); // Sequential access pattern group.bench_function("sequential_input", |b| { let inputs: Vec<[u8; 32]> = (0..100) .map(|i| { let mut arr = [0u8; 32]; for (j, byte) in arr.iter_mut().enumerate() { *byte = ((i + j) & 0xFF) as u8; } arr }) .collect(); b.iter(|| { for input in &inputs { black_box(optimized.multiply(input)); } }) }); // Random access pattern group.bench_function("random_input", |b| { // Pre-generated "random" inputs let inputs: Vec<[u8; 32]> = (0..100) .map(|i| { let mut arr = [0u8; 32]; let mut state = i as u8; for byte in arr.iter_mut() { state = state.wrapping_mul(17).wrapping_add(31); *byte = state; } arr }) .collect(); b.iter(|| { for input in &inputs { black_box(optimized.multiply(input)); } }) }); group.finish(); } // ==================== Nonce Search Rate ==================== fn nonce_search_rate(c: &mut Criterion) { let hasher = KHeavyHash::new(); let header = [0x42u8; 80]; let mut group = c.benchmark_group("nonce_search"); // Search with very easy target (always finds quickly) group.bench_function("easy_target_search", |b| { let target = Target::max(); b.iter(|| black_box(hasher.mine(&header, &target, 0, 1000))) }); // Search through fixed ranges for range in [1000u64, 5000, 10000] { let target = Target::from_bytes([0x00; 32]); // Impossible target group.throughput(Throughput::Elements(range)); group.bench_with_input( BenchmarkId::new("range_search", range), &range, |b, &r| { b.iter(|| black_box(hasher.mine(&header, &target, 0, r))) }, ); } // Search with progress callback group.bench_function("search_with_callback", |b| { let target = Target::max(); b.iter(|| { hasher.mine_with_callback( &header, &target, 0, 5000, |_tried, _nonce| true, ) }) }); group.finish(); } fn parallel_nonce_search(c: &mut Criterion) { let mut group = c.benchmark_group("parallel_nonce_search"); let header = [0x42u8; 80]; let target = Target::max(); for threads in [1, 2, 4] { group.bench_with_input( BenchmarkId::new("threads", threads), &threads, |b, &t| { let miner = ParallelMiner::new(t); b.iter(|| black_box(miner.mine(&header, &target, 0))) }, ); } group.finish(); } // ==================== Block Template Creation ==================== fn block_template_creation(c: &mut Criterion) { let mut group = c.benchmark_group("template_creation"); for tx_count in [0, 10, 50, 100, 500] { group.bench_with_input( BenchmarkId::new("tx_count", tx_count), &tx_count, |b, &count| { b.iter_batched( || { // Setup: create transactions let txs: Vec = (0..count) .map(|i| test_template_tx(i as u64)) .collect(); txs }, |txs| { // Build template let mut builder = BlockTemplateBuilder::new() .version(1) .add_parent(test_hash(1)) .timestamp(1234567890000) .bits(0x1d00ffff) .blue_score(1000) .coinbase(test_coinbase(1000)) .reward(500_00000000); for tx in txs { builder = builder.add_transaction(tx); } black_box(builder.build(1)) }, criterion::BatchSize::SmallInput, ) }, ); } group.finish(); } fn template_header_creation(c: &mut Criterion) { let mut group = c.benchmark_group("template_header"); for tx_count in [0, 50, 200] { let template = create_test_template(tx_count); group.bench_with_input( BenchmarkId::new("header_for_mining", tx_count), &template, |b, tmpl| { b.iter(|| black_box(tmpl.header_for_mining())) }, ); group.bench_with_input( BenchmarkId::new("header_with_extra_nonce", tx_count), &template, |b, tmpl| { b.iter(|| black_box(tmpl.header_with_extra_nonce(12345))) }, ); } group.finish(); } fn template_validation(c: &mut Criterion) { let mut group = c.benchmark_group("template_validation"); for tx_count in [0, 50, 200] { let template = create_test_template(tx_count); group.bench_with_input( BenchmarkId::new("validate", tx_count), &template, |b, tmpl| { b.iter(|| black_box(tmpl.validate())) }, ); group.bench_with_input( BenchmarkId::new("get_target", tx_count), &template, |b, tmpl| { b.iter(|| black_box(tmpl.get_target())) }, ); } group.finish(); } fn coinbase_building(c: &mut Criterion) { let mut group = c.benchmark_group("coinbase_building"); group.bench_function("coinbase_simple", |b| { b.iter(|| { black_box(CoinbaseBuilder::new(test_address(), 1000).build()) }) }); group.bench_function("coinbase_with_data", |b| { b.iter(|| { black_box( CoinbaseBuilder::new(test_address(), 1000) .extra_data(b"Synor Mining Pool v1.0.0".to_vec()) .reward(500_00000000) .fees(1_00000000) .build() ) }) }); // Varying extra data sizes for data_size in [0, 32, 100, 256] { let extra_data = vec![0x42u8; data_size]; group.bench_with_input( BenchmarkId::new("extra_data_size", data_size), &extra_data, |b, data| { b.iter(|| { black_box( CoinbaseBuilder::new(test_address(), 1000) .extra_data(data.clone()) .reward(500_00000000) .build() ) }) }, ); } group.finish(); } // ==================== Miner Configuration ==================== fn miner_config_creation(c: &mut Criterion) { c.bench_function("miner_config_default", |b| { b.iter(|| black_box(MinerConfig::default())) }); c.bench_function("miner_config_solo", |b| { b.iter(|| black_box(MinerConfig::solo(test_address(), 4))) }); c.bench_function("miner_config_pool", |b| { b.iter(|| black_box(MinerConfig::pool(test_address(), 4, 12345))) }); } fn block_miner_creation(c: &mut Criterion) { let mut group = c.benchmark_group("miner_creation"); for threads in [1, 2, 4, 8] { group.bench_with_input( BenchmarkId::new("block_miner", threads), &threads, |b, &t| { let config = MinerConfig::solo(test_address(), t); b.iter(|| black_box(BlockMiner::new(config.clone()))) }, ); } group.finish(); } fn parallel_block_miner_creation(c: &mut Criterion) { let mut group = c.benchmark_group("parallel_miner_creation"); for threads in [1, 2, 4, 8] { group.bench_with_input( BenchmarkId::new("parallel_block_miner", threads), &threads, |b, &t| { let config = MinerConfig::solo(test_address(), t); b.iter(|| black_box(ParallelBlockMiner::new(config.clone()))) }, ); } group.finish(); } // ==================== Target Operations ==================== fn target_operations(c: &mut Criterion) { let mut group = c.benchmark_group("target_ops"); // Target from bits for bits in [0x1d00ffff, 0x1a0fffff, 0x170fffff] { group.bench_with_input( BenchmarkId::new("from_bits", format!("{:08x}", bits)), &bits, |b, &bit| { b.iter(|| black_box(Target::from_bits(bit))) }, ); } // Target comparison let target = Target::from_bits(0x1d00ffff); let easy_hash = Hash256::from_bytes([0x00; 32]); let hard_hash = Hash256::from_bytes([0xFF; 32]); group.bench_function("is_met_by_pass", |b| { b.iter(|| black_box(target.is_met_by(&easy_hash))) }); group.bench_function("is_met_by_fail", |b| { b.iter(|| black_box(target.is_met_by(&hard_hash))) }); // Difficulty calculation group.bench_function("to_difficulty", |b| { b.iter(|| black_box(target.to_difficulty())) }); group.finish(); } // ==================== Mining Statistics ==================== fn mining_stats_operations(c: &mut Criterion) { use synor_mining::MiningStats; let mut group = c.benchmark_group("mining_stats"); group.bench_function("stats_default", |b| { b.iter(|| black_box(MiningStats::default())) }); group.bench_function("update_hashrate", |b| { b.iter_batched( || MiningStats::default(), |mut stats| { stats.update_hashrate(1000000, 1000); black_box(stats) }, criterion::BatchSize::SmallInput, ) }); group.bench_function("record_block", |b| { b.iter_batched( || (MiningStats::default(), 1000u64), |(mut stats, timestamp)| { stats.record_block(timestamp); black_box(stats) }, criterion::BatchSize::SmallInput, ) }); group.bench_function("formatted_hashrate", |b| { let mut stats = MiningStats::default(); stats.update_hashrate(1_000_000_000, 1000); // 1 GH/s b.iter(|| black_box(stats.formatted_hashrate())) }); group.finish(); } // ==================== End-to-End Mining ==================== fn end_to_end_mining(c: &mut Criterion) { let mut group = c.benchmark_group("e2e_mining"); // Full mining cycle: template -> hash -> check target group.bench_function("full_cycle", |b| { let template = create_test_template(50); let hasher = KHeavyHash::new(); let target = template.get_target(); b.iter(|| { let header = template.header_for_mining(); let pre_hash = hasher.pre_hash(&header); let pow = hasher.finalize(&pre_hash, 12345); black_box(target.is_met_by(&pow.hash)) }) }); // Mining with template rotation (simulating new templates arriving) group.bench_function("template_rotation_100_hashes", |b| { let templates: Vec = (0..10) .map(|i| { let mut t = create_test_template(10); t.id = i; t }) .collect(); let hasher = KHeavyHash::new(); b.iter(|| { let mut total_hashes = 0u64; for template in &templates { let header = template.header_for_mining(); let pre_hash = hasher.pre_hash(&header); for nonce in 0..10 { let _ = hasher.finalize(&pre_hash, nonce); total_hashes += 1; } } black_box(total_hashes) }) }); group.finish(); } // ==================== Criterion Groups ==================== criterion_group!( throughput_benches, kheavyhash_throughput_single, kheavyhash_sustained_rate, ); criterion_group!( matrix_benches, matrix_initialization, matrix_memory_patterns, ); criterion_group!( nonce_benches, nonce_search_rate, parallel_nonce_search, ); criterion_group!( template_benches, block_template_creation, template_header_creation, template_validation, coinbase_building, ); criterion_group!( miner_benches, miner_config_creation, block_miner_creation, parallel_block_miner_creation, ); criterion_group!( misc_benches, target_operations, mining_stats_operations, ); criterion_group!( e2e_benches, end_to_end_mining, ); criterion_main!( throughput_benches, matrix_benches, nonce_benches, template_benches, miner_benches, misc_benches, e2e_benches );