Fix all Rust clippy warnings that were causing CI failures when built with RUSTFLAGS=-Dwarnings. Changes include: - Replace derivable_impls with derive macros for BlockBody, Network, etc. - Use div_ceil() instead of manual implementation - Fix should_implement_trait by renaming from_str to parse - Add type aliases for type_complexity warnings - Use or_default(), is_some_and(), is_multiple_of() where appropriate - Remove needless borrows and redundant closures - Fix manual_strip with strip_prefix() - Add allow attributes for intentional patterns (too_many_arguments, needless_range_loop in cryptographic code, assertions_on_constants) - Remove unused imports, mut bindings, and dead code in tests
606 lines
17 KiB
Rust
606 lines
17 KiB
Rust
//! 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<TemplateTransaction> =
|
|
(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<BlockTemplate> = (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
|
|
);
|