use koru_lambda_core::{
Distinction, DistinctionEngine, LocalCausalAgent, NetworkAgent, PeerIdentity,
StructuralCompactor, TransactionAction, TransactionBatch,
};
use std::sync::Arc;
fn propose_and_finalize_batch(
agent: &mut NetworkAgent,
batch: TransactionBatch,
engine: &Arc<DistinctionEngine>,
) -> Result<Distinction, String> {
let commitment = agent.propose_commitment(batch.clone(), engine)?;
agent.finalize_batch(batch, commitment.commitment_hash, engine)
}
#[test]
fn test_e2e_multi_node_consensus() {
println!("\n=== End-to-End: Multi-Node Consensus ===\n");
println!("Setting up distributed system...");
let engine = Arc::new(DistinctionEngine::new());
const NUM_VALIDATORS: usize = 7;
let mut nodes: Vec<NetworkAgent> =
(0..NUM_VALIDATORS).map(|_| NetworkAgent::new(&engine)).collect();
let validators: Vec<PeerIdentity> = (0..NUM_VALIDATORS)
.map(|i| PeerIdentity::new(format!("validator_{}", i), &engine))
.collect();
println!(" Bootstrapping validator set...");
for node in nodes.iter_mut() {
for validator in validators.iter() {
node.join_peer(validator.clone(), &engine);
}
}
println!(" {} validators active\n", NUM_VALIDATORS);
println!("Phase 1: Processing 100 transactions across 10 batches...");
const NUM_BATCHES: usize = 10;
const TXS_PER_BATCH: usize = 10;
let mut successful_batches = 0;
let mut total_txs = 0;
for batch_idx in 0..NUM_BATCHES {
let leaders: Vec<String> =
nodes.iter().map(|n| n.get_current_leader().unwrap().id.clone()).collect();
let leader = &leaders[0];
assert!(
leaders.iter().all(|l| l == leader),
"Nodes disagree on leader at batch {}: {:?}",
batch_idx,
leaders
);
println!(" Batch {}: Leader = {}", batch_idx, leader);
let transactions: Vec<TransactionAction> = (0..TXS_PER_BATCH)
.map(|i| TransactionAction {
nonce: (batch_idx * TXS_PER_BATCH + i) as u64,
data: vec![batch_idx as u8, i as u8],
})
.collect();
let batch = TransactionBatch {
transactions,
previous_root: nodes[0].consensus_state_root().to_string(),
};
let results: Vec<Result<_, _>> = nodes
.iter_mut()
.map(|node| propose_and_finalize_batch(node, batch.clone(), &engine))
.collect();
assert!(
results.iter().all(|r| r.is_ok()),
"Some nodes rejected batch {}: {:?}",
batch_idx,
results
);
successful_batches += 1;
total_txs += TXS_PER_BATCH;
let network_roots: Vec<String> =
nodes.iter().map(|n| n.get_current_root().id().to_string()).collect();
assert!(
network_roots.iter().all(|r| r == &network_roots[0]),
"Nodes diverged after batch {}: {:?}",
batch_idx,
network_roots
);
for node in nodes.iter_mut() {
node.advance_epoch(&engine);
}
}
println!(" ✓ {} batches processed", successful_batches);
println!(" ✓ {} transactions committed", total_txs);
println!("\nVerifying consensus...");
let final_roots: Vec<String> =
nodes.iter().map(|n| n.get_current_root().id().to_string()).collect();
let consensus_root = &final_roots[0];
assert!(
final_roots.iter().all(|r| r == consensus_root),
"FAILED: Nodes did not reach consensus. Roots: {:?}",
final_roots
);
println!(" ✓ All nodes converged to: {}...", &consensus_root[..16]);
let consensus_states: Vec<String> =
nodes.iter().map(|n| n.consensus_state_root().to_string()).collect();
assert!(
consensus_states.iter().all(|s| s == &consensus_states[0]),
"FAILED: Nodes have different consensus states"
);
println!(" ✓ Consensus state: {}...", &consensus_states[0][..16]);
let epochs: Vec<u64> = nodes.iter().map(|n| n.current_epoch()).collect();
assert!(
epochs.iter().all(|e| *e == epochs[0]),
"FAILED: Nodes have different epochs: {:?}",
epochs
);
println!(" ✓ All nodes at epoch {}", epochs[0]);
println!("\n=== Multi-Node Consensus: SUCCESS ===\n");
}
#[test]
#[allow(deprecated)]
fn test_e2e_system_under_load_with_compaction() {
println!("\n=== End-to-End: System Under Load + Compaction ===\n");
println!("Setting up high-load distributed system...");
let engine = Arc::new(DistinctionEngine::new());
const NUM_VALIDATORS: usize = 5;
let mut nodes: Vec<NetworkAgent> =
(0..NUM_VALIDATORS).map(|_| NetworkAgent::new(&engine)).collect();
let mut compactors: Vec<StructuralCompactor> =
(0..NUM_VALIDATORS).map(|_| StructuralCompactor::new(&engine)).collect();
let validators: Vec<PeerIdentity> =
(0..NUM_VALIDATORS).map(|i| PeerIdentity::new(format!("node_{}", i), &engine)).collect();
for node in nodes.iter_mut() {
for validator in validators.iter() {
node.join_peer(validator.clone(), &engine);
}
}
println!(" {} validators active", NUM_VALIDATORS);
println!(" {} compactors initialized\n", NUM_VALIDATORS);
const NUM_BATCHES: usize = 100;
const TXS_PER_BATCH: usize = 10;
const TOTAL_TXS: usize = NUM_BATCHES * TXS_PER_BATCH;
println!("Processing {} transactions...", TOTAL_TXS);
let start = std::time::Instant::now();
let mut txs_processed = 0;
for batch_idx in 0..NUM_BATCHES {
let transactions: Vec<TransactionAction> = (0..TXS_PER_BATCH)
.map(|i| TransactionAction {
nonce: (batch_idx * TXS_PER_BATCH + i) as u64,
data: vec![(batch_idx as u8), (i as u8), ((batch_idx + i) % 256) as u8],
})
.collect();
let batch = TransactionBatch {
transactions,
previous_root: nodes[0].consensus_state_root().to_string(),
};
for node in nodes.iter_mut() {
let result = propose_and_finalize_batch(node, batch.clone(), &engine);
assert!(result.is_ok(), "Batch {} failed: {:?}", batch_idx, result);
}
txs_processed += TXS_PER_BATCH;
if txs_processed % 500 == 0 {
println!(" {} txs processed - running compaction...", txs_processed);
for compactor in compactors.iter_mut() {
compactor.compact(&engine);
}
let stats: Vec<_> = compactors.iter().map(|c| c.get_stats()).collect();
println!(
" Compactor 0: {} HOT, {} WARM, {} COLD",
stats[0].hot_count, stats[0].warm_count, stats[0].cold_count
);
for i in 1..stats.len() {
assert_eq!(
stats[i].hot_count, stats[0].hot_count,
"Compactors diverged on HOT count"
);
assert_eq!(
stats[i].cold_count, stats[0].cold_count,
"Compactors diverged on COLD count"
);
}
}
if batch_idx % 10 == 9 {
for node in nodes.iter_mut() {
node.advance_epoch(&engine);
}
}
}
let duration = start.elapsed();
let throughput = (TOTAL_TXS as f64) / duration.as_secs_f64();
println!("\nPerformance metrics:");
println!(" Total transactions: {}", TOTAL_TXS);
println!(" Duration: {:.2}s", duration.as_secs_f64());
println!(" Throughput: {:.0} tx/s", throughput);
println!("\nFinal compaction state:");
let final_stats = compactors[0].get_stats();
let total_distinctions = engine.distinction_count();
let active_set = final_stats.hot_count + final_stats.warm_count;
let compression_ratio = total_distinctions as f64 / active_set.max(1) as f64;
println!(" Total distinctions: {}", total_distinctions);
println!(" HOT: {}", final_stats.hot_count);
println!(" WARM: {}", final_stats.warm_count);
println!(" COLD: {}", final_stats.cold_count);
println!(" Compression ratio: {:.2}x", compression_ratio);
println!("\nVerifying final consensus...");
let consensus_states: Vec<String> =
nodes.iter().map(|n| n.consensus_state_root().to_string()).collect();
assert!(
consensus_states.iter().all(|s| s == &consensus_states[0]),
"FAILED: Nodes diverged under load"
);
println!(" ✓ All nodes converged");
println!(" ✓ Consensus maintained throughout compaction");
println!(" ✓ {} total epochs", nodes[0].current_epoch());
println!("\n=== System Under Load: SUCCESS ===\n");
}
#[test]
#[allow(deprecated)]
fn test_e2e_byzantine_fault_tolerance() {
println!("\n=== End-to-End: Byzantine Fault Tolerance ===\n");
println!("Setting up network with byzantine nodes...");
let engine = Arc::new(DistinctionEngine::new());
const NUM_VALIDATORS: usize = 7;
const HONEST_NODES: usize = 5;
let mut honest_nodes: Vec<NetworkAgent> =
(0..HONEST_NODES).map(|_| NetworkAgent::new(&engine)).collect();
let mut byzantine_nodes: Vec<NetworkAgent> =
(0..2).map(|_| NetworkAgent::new(&engine)).collect();
let validators: Vec<PeerIdentity> = (0..NUM_VALIDATORS)
.map(|i| PeerIdentity::new(format!("validator_{}", i), &engine))
.collect();
for node in honest_nodes.iter_mut().chain(byzantine_nodes.iter_mut()) {
for validator in validators.iter() {
node.join_peer(validator.clone(), &engine);
}
}
println!(" {} honest nodes", HONEST_NODES);
println!(" {} byzantine nodes\n", 2);
println!("Phase 1: Normal operation (10 batches)...");
for batch_idx in 0..10 {
let transactions: Vec<TransactionAction> = (0..5)
.map(|i| TransactionAction {
nonce: (batch_idx * 5 + i) as u64,
data: vec![batch_idx as u8, i as u8],
})
.collect();
let batch = TransactionBatch {
transactions,
previous_root: honest_nodes[0].consensus_state_root().to_string(),
};
for node in honest_nodes.iter_mut().chain(byzantine_nodes.iter_mut()) {
let result = propose_and_finalize_batch(node, batch.clone(), &engine);
assert!(result.is_ok(), "Normal batch {} failed", batch_idx);
}
for node in honest_nodes.iter_mut().chain(byzantine_nodes.iter_mut()) {
node.advance_epoch(&engine);
}
}
println!(" ✓ 10 normal batches processed\n");
println!("Phase 2: Byzantine attack (invalid nonce)...");
let byzantine_batch = TransactionBatch {
transactions: vec![TransactionAction {
nonce: 9999, data: vec![0xff],
}],
previous_root: honest_nodes[0].consensus_state_root().to_string(),
};
let byzantine_results: Vec<_> = byzantine_nodes
.iter_mut()
.map(|node| propose_and_finalize_batch(node, byzantine_batch.clone(), &engine))
.collect();
assert!(byzantine_results.iter().all(|r| r.is_err()), "Byzantine batch should be rejected");
println!(" ✓ Byzantine batch rejected by structural validator");
let honest_results: Vec<_> = honest_nodes
.iter_mut()
.map(|node| propose_and_finalize_batch(node, byzantine_batch.clone(), &engine))
.collect();
assert!(
honest_results.iter().all(|r| r.is_err()),
"Honest nodes should also reject byzantine batch"
);
println!(" ✓ Honest nodes rejected byzantine batch\n");
println!("Phase 3: Recovery (honest nodes continue)...");
let recovery_batch = TransactionBatch {
transactions: vec![TransactionAction {
nonce: 50, data: vec![0xaa],
}],
previous_root: honest_nodes[0].consensus_state_root().to_string(),
};
for node in honest_nodes.iter_mut() {
let result = propose_and_finalize_batch(node, recovery_batch.clone(), &engine);
assert!(result.is_ok(), "Recovery batch failed");
}
println!(" ✓ Honest nodes recovered and processed valid batch");
println!("\nVerifying honest consensus...");
let honest_states: Vec<String> =
honest_nodes.iter().map(|n| n.consensus_state_root().to_string()).collect();
assert!(honest_states.iter().all(|s| s == &honest_states[0]), "Honest nodes diverged");
let byzantine_states: Vec<String> =
byzantine_nodes.iter().map(|n| n.consensus_state_root().to_string()).collect();
assert!(
byzantine_states.iter().all(|s| s != &honest_states[0]),
"Byzantine nodes should not match honest consensus"
);
println!(" ✓ Honest majority maintained consensus");
println!(" ✓ Byzantine nodes excluded from consensus");
println!("\n=== Byzantine Fault Tolerance: SUCCESS ===\n");
}
#[test]
#[allow(deprecated)]
fn test_e2e_network_partition_recovery() {
println!("\n=== End-to-End: Network Partition Recovery ===\n");
println!("Setting up network...");
let engine = Arc::new(DistinctionEngine::new());
const NUM_VALIDATORS: usize = 5;
let mut all_nodes: Vec<NetworkAgent> =
(0..NUM_VALIDATORS).map(|_| NetworkAgent::new(&engine)).collect();
let validators: Vec<PeerIdentity> =
(0..NUM_VALIDATORS).map(|i| PeerIdentity::new(format!("node_{}", i), &engine)).collect();
for node in all_nodes.iter_mut() {
for validator in validators.iter() {
node.join_peer(validator.clone(), &engine);
}
}
println!(" {} validators initialized\n", NUM_VALIDATORS);
println!("Phase 1: All nodes processing (10 batches)...");
for batch_idx in 0..10 {
let batch = TransactionBatch {
transactions: vec![TransactionAction {
nonce: batch_idx as u64,
data: vec![batch_idx as u8],
}],
previous_root: all_nodes[0].consensus_state_root().to_string(),
};
for node in all_nodes.iter_mut() {
propose_and_finalize_batch(node, batch.clone(), &engine).unwrap();
}
for node in all_nodes.iter_mut() {
node.advance_epoch(&engine);
}
}
println!(" ✓ All nodes at nonce 10\n");
println!("Phase 2: Network partition (nodes 3-4 isolated)...");
let mut partitioned_nodes = all_nodes.split_off(3);
let mut active_nodes = all_nodes;
println!(" Active nodes: 3");
println!(" Partitioned nodes: 2");
println!(" Active nodes processing 20 batches...");
for batch_idx in 10..30 {
let batch = TransactionBatch {
transactions: vec![TransactionAction {
nonce: batch_idx as u64,
data: vec![batch_idx as u8],
}],
previous_root: active_nodes[0].consensus_state_root().to_string(),
};
for node in active_nodes.iter_mut() {
propose_and_finalize_batch(node, batch.clone(), &engine).unwrap();
}
for node in active_nodes.iter_mut() {
node.advance_epoch(&engine);
}
}
println!(" ✓ Active nodes at nonce 30");
println!(" ✓ Partitioned nodes still at nonce 10\n");
let active_state = active_nodes[0].consensus_state_root().to_string();
let partitioned_state = partitioned_nodes[0].consensus_state_root().to_string();
assert_ne!(active_state, partitioned_state, "States should diverge during partition");
println!("Phase 3: Partition heals - catching up partitioned nodes...");
for batch_idx in 10..30 {
let batch = TransactionBatch {
transactions: vec![TransactionAction {
nonce: batch_idx as u64,
data: vec![batch_idx as u8],
}],
previous_root: partitioned_nodes[0].consensus_state_root().to_string(),
};
for node in partitioned_nodes.iter_mut() {
let result = propose_and_finalize_batch(node, batch.clone(), &engine);
assert!(result.is_ok(), "Catch-up batch {} failed", batch_idx);
}
for node in partitioned_nodes.iter_mut() {
node.advance_epoch(&engine);
}
}
println!(" ✓ Partitioned nodes caught up to nonce 30\n");
println!("Verifying convergence...");
active_nodes.append(&mut partitioned_nodes);
let all_nodes = active_nodes;
let final_states: Vec<String> =
all_nodes.iter().map(|n| n.consensus_state_root().to_string()).collect();
assert!(
final_states.iter().all(|s| s == &final_states[0]),
"Nodes failed to converge after partition recovery"
);
println!(" ✓ All 5 nodes converged to same state");
println!(" ✓ Consensus state: {}...", &final_states[0][..16]);
println!("\n=== Network Partition Recovery: SUCCESS ===\n");
}