synor/crates/synor-dag/benches/ghostdag_bench.rs
Gulshan Yadav 48949ebb3f Initial commit: Synor blockchain monorepo
A complete blockchain implementation featuring:
- synord: Full node with GHOSTDAG consensus
- explorer-web: Modern React blockchain explorer with 3D DAG visualization
- CLI wallet and tools
- Smart contract SDK and example contracts (DEX, NFT, token)
- WASM crypto library for browser/mobile
2026-01-08 05:22:17 +05:30

407 lines
13 KiB
Rust

//! GHOSTDAG benchmarks.
//!
//! Benchmarks the core GHOSTDAG operations:
//! - Block insertion with GHOSTDAG data calculation
//! - Blue score computation
//! - Merge set partitioning
//! - Selected chain traversal
//! - Reachability queries
//!
//! Run with: cargo bench -p synor-dag
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::sync::Arc;
use synor_dag::{
BlockDag, BlockId, GhostdagManager, ReachabilityStore, GHOSTDAG_K,
};
use synor_types::Hash256;
/// Helper to create deterministic block IDs.
fn make_block_id(n: u64) -> BlockId {
let mut bytes = [0u8; 32];
bytes[..8].copy_from_slice(&n.to_le_bytes());
Hash256::from_bytes(bytes)
}
/// Creates a test DAG setup.
fn setup_dag() -> (Arc<BlockDag>, Arc<ReachabilityStore>, GhostdagManager) {
let genesis = make_block_id(0);
let dag = Arc::new(BlockDag::new(genesis, 0));
let reachability = Arc::new(ReachabilityStore::new(genesis));
let ghostdag = GhostdagManager::new(dag.clone(), reachability.clone());
(dag, reachability, ghostdag)
}
/// Builds a linear chain of n blocks.
fn build_linear_chain(
n: usize,
dag: &Arc<BlockDag>,
reachability: &Arc<ReachabilityStore>,
ghostdag: &GhostdagManager,
) -> Vec<BlockId> {
let genesis = dag.genesis();
let mut blocks = vec![genesis];
for i in 1..=n {
let block = make_block_id(i as u64);
let parent = blocks[i - 1];
dag.insert_block(block, vec![parent], i as u64 * 100).unwrap();
reachability.add_block(block, parent, &[parent]).unwrap();
ghostdag.add_block(block, &[parent]).unwrap();
blocks.push(block);
}
blocks
}
/// Builds a DAG with specified width (parallel chains).
fn build_wide_dag(
depth: usize,
width: usize,
dag: &Arc<BlockDag>,
reachability: &Arc<ReachabilityStore>,
ghostdag: &GhostdagManager,
) -> Vec<Vec<BlockId>> {
let genesis = dag.genesis();
let mut layers: Vec<Vec<BlockId>> = vec![vec![genesis]];
for d in 1..=depth {
let mut layer = Vec::new();
for w in 0..width {
let block_num = (d * 1000 + w) as u64;
let block = make_block_id(block_num);
// Connect to all blocks in previous layer (up to MAX_BLOCK_PARENTS)
let parents: Vec<_> = layers[d - 1].iter().copied().collect();
let selected_parent = parents[0];
dag.insert_block(block, parents.clone(), block_num * 100).unwrap();
reachability.add_block(block, selected_parent, &parents).unwrap();
ghostdag.add_block(block, &parents).unwrap();
layer.push(block);
}
layers.push(layer);
}
layers
}
// ==================== Block Insertion Benchmarks ====================
fn block_insertion_linear(c: &mut Criterion) {
let mut group = c.benchmark_group("ghostdag_insert_linear");
for chain_len in [10, 50, 100, 500] {
group.throughput(Throughput::Elements(1));
group.bench_with_input(
BenchmarkId::from_parameter(chain_len),
&chain_len,
|b, &n| {
b.iter_batched(
|| {
let (dag, reachability, ghostdag) = setup_dag();
// Pre-build the chain except last block
let blocks = build_linear_chain(n - 1, &dag, &reachability, &ghostdag);
(dag, reachability, ghostdag, blocks)
},
|(dag, reachability, ghostdag, blocks)| {
// Benchmark inserting the final block
let new_block = make_block_id(n as u64);
let parent = *blocks.last().unwrap();
dag.insert_block(new_block, vec![parent], n as u64 * 100).unwrap();
reachability.add_block(new_block, parent, &[parent]).unwrap();
black_box(ghostdag.add_block(new_block, &[parent]).unwrap())
},
criterion::BatchSize::SmallInput,
)
},
);
}
group.finish();
}
fn block_insertion_wide(c: &mut Criterion) {
let mut group = c.benchmark_group("ghostdag_insert_wide");
// Test with different DAG widths
for width in [2, 4, 8, 16] {
group.throughput(Throughput::Elements(1));
group.bench_with_input(BenchmarkId::from_parameter(width), &width, |b, &w| {
b.iter_batched(
|| {
let (dag, reachability, ghostdag) = setup_dag();
// Pre-build DAG with specified width
let layers = build_wide_dag(10, w, &dag, &reachability, &ghostdag);
(dag, reachability, ghostdag, layers)
},
|(dag, reachability, ghostdag, layers)| {
// Benchmark inserting a new block on top
let new_block = make_block_id(99999);
let parents: Vec<_> = layers.last().unwrap().iter().copied().collect();
let selected_parent = parents[0];
dag.insert_block(new_block, parents.clone(), 99999 * 100).unwrap();
reachability.add_block(new_block, selected_parent, &parents).unwrap();
black_box(ghostdag.add_block(new_block, &parents).unwrap())
},
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
}
// ==================== Blue Score Benchmarks ====================
fn blue_score_calculation(c: &mut Criterion) {
let (dag, reachability, ghostdag) = setup_dag();
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
let tip = *blocks.last().unwrap();
c.bench_function("blue_score_get", |b| {
b.iter(|| black_box(ghostdag.get_blue_score(&tip).unwrap()))
});
}
fn blue_score_batch(c: &mut Criterion) {
let (dag, reachability, ghostdag) = setup_dag();
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
c.bench_function("blue_score_batch_100", |b| {
b.iter(|| {
for block in &blocks {
black_box(ghostdag.get_blue_score(block).unwrap());
}
})
});
}
// ==================== Selected Chain Benchmarks ====================
fn selected_chain_traversal(c: &mut Criterion) {
let mut group = c.benchmark_group("selected_chain");
for chain_len in [10, 50, 100, 500] {
group.bench_with_input(
BenchmarkId::from_parameter(chain_len),
&chain_len,
|b, &n| {
let (dag, reachability, ghostdag) = setup_dag();
let blocks = build_linear_chain(n, &dag, &reachability, &ghostdag);
let tip = *blocks.last().unwrap();
b.iter(|| black_box(ghostdag.get_selected_chain(&tip).unwrap()))
},
);
}
group.finish();
}
// ==================== Reachability Benchmarks ====================
fn reachability_ancestor_check(c: &mut Criterion) {
let (dag, reachability, ghostdag) = setup_dag();
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
let tip = *blocks.last().unwrap();
let genesis = dag.genesis();
c.bench_function("reachability_is_ancestor_100", |b| {
b.iter(|| black_box(reachability.is_ancestor(&genesis, &tip).unwrap()))
});
}
fn reachability_anticone_check(c: &mut Criterion) {
let (dag, reachability, ghostdag) = setup_dag();
// Build a forked DAG
let genesis = dag.genesis();
// First chain
let block1 = make_block_id(1);
let block2 = make_block_id(2);
dag.insert_block(block1, vec![genesis], 100).unwrap();
dag.insert_block(block2, vec![block1], 200).unwrap();
reachability.add_block(block1, genesis, &[genesis]).unwrap();
reachability.add_block(block2, block1, &[block1]).unwrap();
ghostdag.add_block(block1, &[genesis]).unwrap();
ghostdag.add_block(block2, &[block1]).unwrap();
// Second chain (parallel)
let block3 = make_block_id(3);
let block4 = make_block_id(4);
dag.insert_block(block3, vec![genesis], 150).unwrap();
dag.insert_block(block4, vec![block3], 250).unwrap();
reachability.add_block(block3, genesis, &[genesis]).unwrap();
reachability.add_block(block4, block3, &[block3]).unwrap();
ghostdag.add_block(block3, &[genesis]).unwrap();
ghostdag.add_block(block4, &[block3]).unwrap();
// block2 and block4 are in each other's anticone
c.bench_function("reachability_is_anticone", |b| {
b.iter(|| black_box(reachability.is_anticone(&block2, &block4).unwrap()))
});
}
// ==================== Merge Set Benchmarks ====================
fn merge_set_calculation(c: &mut Criterion) {
let mut group = c.benchmark_group("merge_set");
for width in [2, 4, 8] {
group.bench_with_input(BenchmarkId::from_parameter(width), &width, |b, &w| {
b.iter_batched(
|| {
let (dag, reachability, ghostdag) = setup_dag();
let layers = build_wide_dag(5, w, &dag, &reachability, &ghostdag);
(dag, reachability, ghostdag, layers)
},
|(dag, reachability, ghostdag, layers)| {
// Insert a new block that merges multiple parents
let new_block = make_block_id(99999);
let parents: Vec<_> = layers.last().unwrap().iter().copied().collect();
let selected_parent = parents[0];
dag.insert_block(new_block, parents.clone(), 99999 * 100).unwrap();
reachability.add_block(new_block, selected_parent, &parents).unwrap();
let data = ghostdag.add_block(new_block, &parents).unwrap();
black_box(data.merge_set_size())
},
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
}
// ==================== k-Cluster Benchmarks ====================
fn k_cluster_validation(c: &mut Criterion) {
let mut group = c.benchmark_group("k_cluster");
for k in [3, 10, 18] {
group.bench_with_input(BenchmarkId::from_parameter(k), &k, |b, &k_val| {
b.iter_batched(
|| {
let genesis = make_block_id(0);
let dag = Arc::new(BlockDag::new(genesis, 0));
let reachability = Arc::new(ReachabilityStore::new(genesis));
let ghostdag = GhostdagManager::with_k(dag.clone(), reachability.clone(), k_val);
build_wide_dag(5, k_val as usize, &dag, &reachability, &ghostdag);
(dag, reachability, ghostdag)
},
|(dag, reachability, ghostdag)| {
// Check k-cluster property
black_box(ghostdag.k())
},
criterion::BatchSize::SmallInput,
)
});
}
group.finish();
}
// ==================== GHOSTDAG Data Access ====================
fn ghostdag_data_access(c: &mut Criterion) {
let (dag, reachability, ghostdag) = setup_dag();
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
let tip = *blocks.last().unwrap();
c.bench_function("ghostdag_get_data", |b| {
b.iter(|| black_box(ghostdag.get_data(&tip).unwrap()))
});
c.bench_function("ghostdag_get_selected_parent", |b| {
b.iter(|| black_box(ghostdag.get_selected_parent(&tip).unwrap()))
});
c.bench_function("ghostdag_is_blue", |b| {
b.iter(|| black_box(ghostdag.is_blue(&tip)))
});
}
// ==================== DAG Operations ====================
fn dag_block_lookup(c: &mut Criterion) {
let (dag, reachability, ghostdag) = setup_dag();
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
let tip = *blocks.last().unwrap();
c.bench_function("dag_get_block", |b| {
b.iter(|| black_box(dag.get_block(&tip)))
});
c.bench_function("dag_get_parents", |b| {
b.iter(|| black_box(dag.get_parents(&tip)))
});
c.bench_function("dag_get_children", |b| {
b.iter(|| black_box(dag.get_children(&tip)))
});
}
fn dag_tips_query(c: &mut Criterion) {
let (dag, reachability, ghostdag) = setup_dag();
build_wide_dag(10, 4, &dag, &reachability, &ghostdag);
c.bench_function("dag_get_tips", |b| {
b.iter(|| black_box(dag.tips()))
});
}
// ==================== Criterion Groups ====================
criterion_group!(
insertion_benches,
block_insertion_linear,
block_insertion_wide,
);
criterion_group!(
score_benches,
blue_score_calculation,
blue_score_batch,
);
criterion_group!(
chain_benches,
selected_chain_traversal,
);
criterion_group!(
reachability_benches,
reachability_ancestor_check,
reachability_anticone_check,
);
criterion_group!(
merge_benches,
merge_set_calculation,
k_cluster_validation,
);
criterion_group!(
data_access_benches,
ghostdag_data_access,
dag_block_lookup,
dag_tips_query,
);
criterion_main!(
insertion_benches,
score_benches,
chain_benches,
reachability_benches,
merge_benches,
data_access_benches
);