mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
213 lines
6.2 KiB
Go
213 lines
6.2 KiB
Go
// Package main provides the sim CLI tool for consensus simulation
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"math/rand"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/luxfi/consensus/config"
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
nodes = flag.Int("nodes", 100, "Number of nodes in the network")
|
|
rounds = flag.Int("rounds", 10, "Number of consensus rounds to simulate")
|
|
network = flag.String("network", "mainnet", "Network configuration (mainnet, testnet, local)")
|
|
failure = flag.Float64("failure", 0.1, "Node failure rate (0.0-1.0)")
|
|
latency = flag.Duration("latency", 50*time.Millisecond, "Network latency")
|
|
verbose = flag.Bool("verbose", false, "Verbose output")
|
|
help = flag.Bool("help", false, "Show help message")
|
|
)
|
|
flag.Parse()
|
|
|
|
if *help {
|
|
printHelp()
|
|
os.Exit(0)
|
|
}
|
|
|
|
if *failure < 0 || *failure > 1 {
|
|
fmt.Fprintf(os.Stderr, "Failure rate must be between 0.0 and 1.0\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Get network configuration
|
|
params := getNetworkParams(*network)
|
|
|
|
fmt.Println("=== Consensus Simulation ===")
|
|
fmt.Printf("Network: %s\n", *network)
|
|
fmt.Printf("Nodes: %d\n", *nodes)
|
|
fmt.Printf("Rounds: %d\n", *rounds)
|
|
fmt.Printf("Failure: %.1f%%\n", *failure*100)
|
|
fmt.Printf("Latency: %s\n", *latency)
|
|
fmt.Printf("Parameters: K=%d, Alpha=%.2f, Beta=%d\n\n", params.K, params.Alpha, params.Beta)
|
|
|
|
// Run simulation
|
|
results := runSimulation(*nodes, *rounds, params, *failure, *latency, *verbose)
|
|
|
|
// Print results
|
|
printResults(results, params)
|
|
}
|
|
|
|
func printHelp() {
|
|
fmt.Println("Consensus Simulator")
|
|
fmt.Println("\nUsage: sim [options]")
|
|
fmt.Println("\nOptions:")
|
|
fmt.Println(" -nodes int Number of nodes in the network (default: 100)")
|
|
fmt.Println(" -rounds int Number of consensus rounds (default: 10)")
|
|
fmt.Println(" -network string Network configuration (default: mainnet)")
|
|
fmt.Println(" Options: mainnet, testnet, local")
|
|
fmt.Println(" -failure float Node failure rate 0.0-1.0 (default: 0.1)")
|
|
fmt.Println(" -latency duration Network latency (default: 50ms)")
|
|
fmt.Println(" -verbose Verbose output")
|
|
fmt.Println(" -help Show this help message")
|
|
fmt.Println("\nExamples:")
|
|
fmt.Println(" sim # Run default simulation")
|
|
fmt.Println(" sim -nodes 1000 -rounds 100 # Large scale simulation")
|
|
fmt.Println(" sim -failure 0.3 -latency 200ms # High failure, slow network")
|
|
fmt.Println(" sim -network testnet -verbose # Testnet config with details")
|
|
}
|
|
|
|
func getNetworkParams(network string) config.Parameters {
|
|
switch network {
|
|
case "mainnet":
|
|
return config.MainnetParams()
|
|
case "testnet":
|
|
return config.TestnetParams()
|
|
case "local":
|
|
return config.LocalParams()
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "Unknown network: %s, using mainnet\n", network)
|
|
return config.MainnetParams()
|
|
}
|
|
}
|
|
|
|
type SimulationResult struct {
|
|
Round int
|
|
VotesReceived int
|
|
Confidence float64
|
|
Decision string
|
|
TimeToConsensus time.Duration
|
|
FailedNodes int
|
|
}
|
|
|
|
func runSimulation(nodes int, rounds int, params config.Parameters, failureRate float64, latency time.Duration, verbose bool) []SimulationResult {
|
|
results := make([]SimulationResult, 0, rounds)
|
|
ctx := context.Background()
|
|
|
|
for round := 1; round <= rounds; round++ {
|
|
if verbose {
|
|
fmt.Printf("Round %d: ", round)
|
|
}
|
|
|
|
start := time.Now()
|
|
result := simulateRound(ctx, nodes, params, failureRate, latency)
|
|
result.Round = round
|
|
result.TimeToConsensus = time.Since(start)
|
|
|
|
if verbose {
|
|
fmt.Printf("%s (confidence: %.2f%%, time: %s)\n",
|
|
result.Decision, result.Confidence*100, result.TimeToConsensus)
|
|
}
|
|
|
|
results = append(results, result)
|
|
|
|
// Simulate inter-round delay
|
|
time.Sleep(latency)
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
func simulateRound(ctx context.Context, nodes int, params config.Parameters, failureRate float64, latency time.Duration) SimulationResult {
|
|
// Calculate failed nodes
|
|
failedNodes := int(float64(nodes) * failureRate)
|
|
activeNodes := nodes - failedNodes
|
|
|
|
// Sample K nodes randomly
|
|
k := params.K
|
|
if k > activeNodes {
|
|
k = activeNodes
|
|
}
|
|
|
|
// Simulate voting
|
|
votes := 0
|
|
for i := 0; i < k; i++ {
|
|
// Simulate network latency
|
|
time.Sleep(latency / time.Duration(k))
|
|
|
|
// Random vote with Byzantine behavior (simulation only, not security-critical)
|
|
if rand.Float64() > 0.2 { // 80% honest nodes //nolint:gosec // simulation randomness
|
|
votes++
|
|
}
|
|
}
|
|
|
|
// Calculate confidence
|
|
confidence := float64(votes) / float64(k)
|
|
|
|
// Determine decision based on alpha threshold
|
|
decision := "REJECT"
|
|
if confidence >= params.Alpha {
|
|
decision = "ACCEPT"
|
|
}
|
|
|
|
return SimulationResult{
|
|
VotesReceived: votes,
|
|
Confidence: confidence,
|
|
Decision: decision,
|
|
FailedNodes: failedNodes,
|
|
}
|
|
}
|
|
|
|
func printResults(results []SimulationResult, params config.Parameters) {
|
|
fmt.Println("=== Simulation Results ===")
|
|
|
|
accepts := 0
|
|
rejects := 0
|
|
totalTime := time.Duration(0)
|
|
totalConfidence := 0.0
|
|
|
|
for _, r := range results {
|
|
if r.Decision == "ACCEPT" {
|
|
accepts++
|
|
} else {
|
|
rejects++
|
|
}
|
|
totalTime += r.TimeToConsensus
|
|
totalConfidence += r.Confidence
|
|
}
|
|
|
|
fmt.Printf("\nConsensus Decisions:\n")
|
|
fmt.Printf(" Accepts: %d (%.1f%%)\n", accepts, float64(accepts)/float64(len(results))*100)
|
|
fmt.Printf(" Rejects: %d (%.1f%%)\n", rejects, float64(rejects)/float64(len(results))*100)
|
|
|
|
fmt.Printf("\nPerformance:\n")
|
|
fmt.Printf(" Avg Time: %s\n", totalTime/time.Duration(len(results)))
|
|
fmt.Printf(" Avg Confidence: %.2f%%\n", totalConfidence/float64(len(results))*100)
|
|
fmt.Printf(" Alpha Required: %.2f%%\n", params.Alpha*100)
|
|
|
|
// Calculate finality probability
|
|
finalityProb := calculateFinalityProbability(params.Alpha, params.Beta, totalConfidence/float64(len(results)))
|
|
fmt.Printf("\nFinality:\n")
|
|
fmt.Printf(" Probability: %.4f%%\n", finalityProb*100)
|
|
fmt.Printf(" Beta Rounds: %d\n", params.Beta)
|
|
}
|
|
|
|
func calculateFinalityProbability(alpha float64, beta uint32, avgConfidence float64) float64 {
|
|
// Simplified finality calculation
|
|
// P(finality) = confidence^beta
|
|
prob := 1.0
|
|
for i := uint32(0); i < beta; i++ {
|
|
prob *= avgConfidence
|
|
}
|
|
return prob
|
|
}
|
|
|
|
func init() {
|
|
// As of Go 1.20, rand.Seed is deprecated - random seeding is automatic
|
|
// No manual seeding required for better randomness
|
|
}
|