diff --git a/Makefile b/Makefile index b0ebe1b..2afdae0 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,8 @@ TOPOLOGY_GEN_DIR = topology/gen GOSSIPSUB_DIR = gossipsub TOPOLOGY_FILE = topology.json SIMCONFIG_FILE = simconfig.yaml +PLOT_DIR = updated-plots +PLOT_OUTPUT_FILE = output.png # Check dependencies check-deps: @@ -49,7 +51,7 @@ build: check-deps @echo "Build successful" # Generate network graph and Shadow configuration -generate-config: generate-topology +generate-config: build generate-topology @echo "Generating Shadow network configuration for $(NODE_COUNT) nodes with $(MESH_NODE_COUNT) mesh nodes, $(MESH_ATTESTER_COUNT) mesh attesters nodes, $(NON_MESH_ATTESTER_COUNT) non mesh attesters nodes..." uv run network_graph.py $(NODE_COUNT) $(TOPOLOGY_FILE) $(SIMCONFIG_FILE) @test -f shadow-gossipsub.yaml && test -f graph.gml || (echo "Config generation failed" && exit 1) @@ -62,8 +64,8 @@ generate-config-only: @echo "Configuration generated" # Run the complete Shadow simulation -run-sim: build generate-config - @echo "Starting GossipSub Shadow simulation ($(NODE_COUNT) nodes, $(MESH_NODE_COUNT) mesh nodes, $(MESH_ATTESTER_COUNT) mesh attesters nodes, $(NON_MESH_ATTESTER_COUNT) non mesh attesters nodes, $(MSG_SIZE) byte message)..." +run-shadow: build + @echo "Starting GossipSub Shadow simulation" @rm -rf shadow.data/ shadow --progress $(PROGRESS) shadow-gossipsub.yaml @echo "GossipSub simulation completed" @@ -76,8 +78,11 @@ test: # Plot message propagation plot: @echo "Plotting message propagation..." - uv run plot_propagation.py $(NODE_COUNT) --topology-file $(TOPOLOGY_FILE) --peer-count $(PEER_COUNT) --non-mesh-node-peer-count $(NON_MESH_NODE_PEER_COUNT) --simconfig-file $(SIMCONFIG_FILE) -o some-plots/30slots_batch_5ms_node$(NODE_COUNT)_mesh_nodes$(MESH_NODE_COUNT)_mesh_attesters$(MESH_ATTESTER_COUNT)_non_mesh_attesters$(NON_MESH_ATTESTER_COUNT).png - @test -f some-plots/30slots_batch_5ms_node$(NODE_COUNT)_mesh_nodes$(MESH_NODE_COUNT)_mesh_attesters$(MESH_ATTESTER_COUNT)_non_mesh_attesters$(NON_MESH_ATTESTER_COUNT).png && echo "Plot generated: some-plots/30slots_batch_5ms_node$(NODE_COUNT)_mesh_nodes$(MESH_NODE_COUNT)_mesh_attesters$(MESH_ATTESTER_COUNT)_non_mesh_attesters$(NON_MESH_ATTESTER_COUNT).png" || echo "Plot generation failed" + @mkdir -p $(PLOT_DIR) + uv run plot_propagation.py $(NODE_COUNT) --topology-file $(TOPOLOGY_FILE) --peer-count $(PEER_COUNT) --non-mesh-node-peer-count $(NON_MESH_NODE_PEER_COUNT) --simconfig-file $(SIMCONFIG_FILE) -o $(PLOT_DIR)/$(PLOT_OUTPUT_FILE) + @test -f $(PLOT_DIR)/$(PLOT_OUTPUT_FILE) && echo "Plot generated: $(PLOT_DIR)/$(PLOT_OUTPUT_FILE)" || echo "Plot generation failed" + +run-sim: run-shadow test plot # Clean build artifacts and simulation results clean: diff --git a/gossipsub/main.go b/gossipsub/main.go index 709e9a5..4bdae0f 100644 --- a/gossipsub/main.go +++ b/gossipsub/main.go @@ -115,11 +115,11 @@ const ( // BLSBatchVerifier simulates a BLS signature batch verifier queue type BLSBatchVerifier struct { - queue chan *ValidationRequest - ctx context.Context - cancel context.CancelFunc - batchVerifierTime time.Duration - batchIntervalTime time.Duration + queue chan *ValidationRequest + ctx context.Context + cancel context.CancelFunc + batchIntervalTime time.Duration + prysmValidatorInfo *simconfig.PrysmValidatorInfo } // ValidationRequest represents a message waiting for batch verification @@ -129,15 +129,15 @@ type ValidationRequest struct { } // NewBLSBatchVerifier creates a new batch verifier -func NewBLSBatchVerifier(batchVerifierTime time.Duration, batchIntervalTime time.Duration) *BLSBatchVerifier { +func NewBLSBatchVerifier(prysmValidatorInfo *simconfig.PrysmValidatorInfo, batchIntervalTime time.Duration) *BLSBatchVerifier { ctx, cancel := context.WithCancel(context.Background()) bv := &BLSBatchVerifier{ - queue: make(chan *ValidationRequest, 1000), // Buffer for high throughput - ctx: ctx, - cancel: cancel, - batchVerifierTime: batchVerifierTime, - batchIntervalTime: batchIntervalTime, + queue: make(chan *ValidationRequest, 1000), // Buffer for high throughput + ctx: ctx, + cancel: cancel, + batchIntervalTime: batchIntervalTime, + prysmValidatorInfo: prysmValidatorInfo, } // Start the batch processing goroutine @@ -168,13 +168,37 @@ func (bv *BLSBatchVerifier) processBatches() { } } -// processBatch simulates batch verification taking 4.5ms +// processBatch simulates batch verification with percentile-based timing func (bv *BLSBatchVerifier) processBatch(batch []*ValidationRequest) { if len(batch) == 0 { return } - time.Sleep(bv.batchVerifierTime) + // Generate random number to determine which percentile batch verifier time to use + randNum := rand.Intn(101) // 0-100 inclusive + var batchVerifierTime time.Duration + + if randNum >= 99 { + // Top 1%: values 99-100 → P99 + batchVerifierTime = time.Duration(bv.prysmValidatorInfo.P99BatchVerifierTime) * time.Microsecond + } else if randNum >= 95 { + // 95-98: 4% of values → P95 + batchVerifierTime = time.Duration(bv.prysmValidatorInfo.P95BatchVerifierTime) * time.Microsecond + } else if randNum >= 90 { + // 90-94: 5% of values → P90 + batchVerifierTime = time.Duration(bv.prysmValidatorInfo.P90BatchVerifierTime) * time.Microsecond + } else if randNum >= 75 { + // 75-89: 15% of values → P75 + batchVerifierTime = time.Duration(bv.prysmValidatorInfo.P75BatchVerifierTime) * time.Microsecond + } else if randNum >= 25 { + // 25-74: 50% of values → P50 (median range) + batchVerifierTime = time.Duration(bv.prysmValidatorInfo.P50BatchVerifierTime) * time.Microsecond + } else { + // 0-24: 25% of values → P25 + batchVerifierTime = time.Duration(bv.prysmValidatorInfo.P25BatchVerifierTime) * time.Microsecond + } + + time.Sleep(batchVerifierTime) // Send results back to all requests in the batch for _, req := range batch { @@ -261,11 +285,11 @@ func main() { time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) var ( - nodeID = flag.Int("node-id", 0, "Node ID for this simulation instance") - nodeCount = flag.Int("node-count", 10, "Total number of nodes in simulation") - simConfigFile = flag.String("simconfig-file", "", "Path to simulation config file") - topologyFile = flag.String("topology-file", "", "Path to topology JSON file (required)") - logLevel = flag.String("log-level", "info", "Log level (debug, info, warn, error)") + nodeID = flag.Int("node-id", 0, "Node ID for this simulation instance") + nodeCount = flag.Int("node-count", 10, "Total number of nodes in simulation") + simConfigFile = flag.String("simconfig-file", "", "Path to simulation config file") + topologyFile = flag.String("topology-file", "", "Path to topology JSON file (required)") + logLevel = flag.String("log-level", "info", "Log level (debug, info, warn, error)") ) flag.Parse() @@ -388,24 +412,47 @@ func main() { } batchIntervalTimeDuration := time.Duration(simConfig.PrysmValidator.BatchInterval) * time.Millisecond - batchVerificationTimeDuration := time.Duration(simConfig.PrysmValidator.BatchVerifierTime) * time.Microsecond // Create BLS batch verifier for Prysm clients var batchVerifier *BLSBatchVerifier if clientType == PrysmClient { - log.Printf("Creating BLS batch verifier for Prysm client with batch verification time %s and batch interval time %s", batchVerificationTimeDuration.String(), batchIntervalTimeDuration.String()) - batchVerifier = NewBLSBatchVerifier(batchVerificationTimeDuration, batchIntervalTimeDuration) + log.Printf("Creating BLS batch verifier for Prysm client with percentile-based batch verification times and batch interval time %s", batchIntervalTimeDuration.String()) + batchVerifier = NewBLSBatchVerifier(&simConfig.PrysmValidator, batchIntervalTimeDuration) defer batchVerifier.Close() } else { - log.Printf("Creating Lighthouse validator for Lighthouse client with validator time %s", time.Duration(simConfig.LighthouseValidator.ValidatorTime).String()) + log.Printf("Creating Lighthouse validator for Lighthouse client with percentile-based validator times") } - // Register topic validator with BLS batch verification + // Register topic validator with BLS batch verification. 47 byte topic topicName := "gossipsub-simabcdefghijklmnopqrstuvwxyzabcdefgh" err = ps.RegisterTopicValidator(topicName, func(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult { if clientType == PrysmClient { return batchVerifier.ValidateMessage(ctx, msg) } else if clientType == LighthouseClient { - time.Sleep(time.Duration(simConfig.LighthouseValidator.ValidatorTime) * time.Microsecond) + // Generate random number to determine which percentile validation time to use + randNum := rand.Intn(101) // 0-100 inclusive + var validatorTime uint64 + + if randNum >= 99 { + // Top 1%: values 99-100 → P99 + validatorTime = simConfig.LighthouseValidator.P99ValidatorTime + } else if randNum >= 95 { + // 95-98: 4% of values → P95 + validatorTime = simConfig.LighthouseValidator.P95ValidatorTime + } else if randNum >= 90 { + // 90-94: 5% of values → P90 + validatorTime = simConfig.LighthouseValidator.P90ValidatorTime + } else if randNum >= 75 { + // 75-89: 15% of values → P75 + validatorTime = simConfig.LighthouseValidator.P75ValidatorTime + } else if randNum >= 25 { + // 25-74: 50% of values → P50 (median range) + validatorTime = simConfig.LighthouseValidator.P50ValidatorTime + } else { + // 0-24: 25% of values → P25 + validatorTime = simConfig.LighthouseValidator.P25ValidatorTime + } + + time.Sleep(time.Duration(validatorTime) * time.Microsecond) return pubsub.ValidationAccept } @@ -414,7 +461,7 @@ func main() { if err != nil { log.Fatalf("Failed to register topic validator: %v", err) } - log.Printf("Registered BLS batch verifier for %s (%s batching + %s verification)", topicName, batchIntervalTimeDuration.String(), batchVerificationTimeDuration.String()) + log.Printf("Registered BLS batch verifier for %s (%s batching with percentile-based verification times)", topicName, batchIntervalTimeDuration.String()) // Join topic and conditionally subscribe var topic *pubsub.Topic @@ -502,7 +549,7 @@ func main() { // Wait for network setup and peer discovery (important in Shadow) // Synchronize all nodes to start at exactly 2000/01/01 00:02:00 targetTime := time.Date(2000, 1, 1, 0, 2, 0, 0, time.UTC) - waitDuration := targetTime.Sub(time.Now()) + waitDuration := time.Until(targetTime) log.Printf("Waiting for peer discovery and network stabilization until 2000/01/01 00:02:00 (%v)...", waitDuration) if waitDuration > 0 { time.Sleep(waitDuration) @@ -581,6 +628,11 @@ func main() { } } + // Sleep for a random time between 10 and 20ms before publishing to account for publish delays. + // number is not scientifically chosen. + sleepDuration := time.Duration(10+rand.Intn(11)) * time.Millisecond + time.Sleep(sleepDuration) + err = topic.Publish(ctx, msg) if err != nil { log.Printf("Failed to publish message: %v", err) diff --git a/network_graph.py b/network_graph.py index f5605b7..726a892 100755 --- a/network_graph.py +++ b/network_graph.py @@ -111,15 +111,15 @@ class NodeType: """ edges = [ - Edge(united_states, united_states, 2), - Edge(united_states, germany, 84), - Edge(united_states, france, 79), - Edge(united_states, finland, 100), - Edge(united_states, canada, 15), - Edge(united_states, singapore, 235), - Edge(united_states, united_kingdom, 70), - Edge(united_states, japan, 185), - Edge(united_states, netherlands, 79), + Edge(united_states, united_states, 70), + Edge(united_states, germany, 149), + Edge(united_states, france, 135), + Edge(united_states, finland, 177), + Edge(united_states, canada, 65), + Edge(united_states, singapore, 168), + Edge(united_states, united_kingdom, 132), + Edge(united_states, japan, 108), + Edge(united_states, netherlands, 137), Edge(united_states, south_korea, 199), Edge(united_states, austria, 97), Edge(united_states, australia, 198), @@ -165,15 +165,15 @@ class NodeType: Edge(united_states, indonesia, 235), Edge(united_states, chile, 133), Edge(united_states, estonia, 99), - Edge(germany, united_states, 84), - Edge(germany, germany, 2), - Edge(germany, france, 11), - Edge(germany, finland, 41), - Edge(germany, canada, 96), - Edge(germany, singapore, 150), - Edge(germany, united_kingdom, 14), - Edge(germany, japan, 222), - Edge(germany, netherlands, 11), + Edge(germany, united_states, 151), + Edge(germany, germany, 12), + Edge(germany, france, 29), + Edge(germany, finland, 25), + Edge(germany, canada, 100), + Edge(germany, singapore, 154), + Edge(germany, united_kingdom, 17), + Edge(germany, japan, 240), + Edge(germany, netherlands, 15), Edge(germany, south_korea, 285), Edge(germany, austria, 14), Edge(germany, australia, 278), @@ -219,16 +219,16 @@ class NodeType: Edge(germany, indonesia, 182), Edge(germany, chile, 213), Edge(germany, estonia, 29), - Edge(france, united_states, 79), - Edge(france, germany, 11), - Edge(france, france, 2), - Edge(france, finland, 33), - Edge(france, canada, 93), + Edge(france, united_states, 135), + Edge(france, germany, 30), + Edge(france, france, 12), + Edge(france, finland, 36), + Edge(france, canada, 92), Edge(france, singapore, 264), - Edge(france, united_kingdom, 9), - Edge(france, japan, 235), - Edge(france, netherlands, 10), - Edge(france, south_korea, 274), + Edge(france, united_kingdom, 14), + Edge(france, japan, 242), + Edge(france, netherlands, 23), + Edge(france, south_korea, 280), Edge(france, austria, 22), Edge(france, australia, 271), Edge(france, switzerland, 20), @@ -273,16 +273,16 @@ class NodeType: Edge(france, indonesia, 272), Edge(france, chile, 204), Edge(france, estonia, 39), - Edge(finland, united_states, 100), + Edge(finland, united_states, 175), Edge(finland, germany, 41), - Edge(finland, france, 33), - Edge(finland, finland, 2), + Edge(finland, france, 35), + Edge(finland, finland, 9), Edge(finland, canada, 112), Edge(finland, singapore, 186), Edge(finland, united_kingdom, 35), Edge(finland, japan, 243), - Edge(finland, netherlands, 27), - Edge(finland, south_korea, 266), + Edge(finland, netherlands, 33), + Edge(finland, south_korea, 293), Edge(finland, austria, 33), Edge(finland, australia, 279), Edge(finland, switzerland, 40), @@ -331,12 +331,12 @@ class NodeType: Edge(canada, germany, 95), Edge(canada, france, 93), Edge(canada, finland, 112), - Edge(canada, canada, 2), + Edge(canada, canada, 52), Edge(canada, singapore, 252), - Edge(canada, united_kingdom, 91), + Edge(canada, united_kingdom, 95), Edge(canada, japan, 179), Edge(canada, netherlands, 95), - Edge(canada, south_korea, 189), + Edge(canada, south_korea, 194), Edge(canada, austria, 110), Edge(canada, australia, 206), Edge(canada, switzerland, 111), @@ -382,10 +382,10 @@ class NodeType: Edge(canada, chile, 153), Edge(canada, estonia, 110), Edge(singapore, united_states, 235), - Edge(singapore, germany, 150), + Edge(singapore, germany, 155), Edge(singapore, france, 263), Edge(singapore, finland, 185), - Edge(singapore, canada, 251), + Edge(singapore, canada, 236), Edge(singapore, singapore, 2), Edge(singapore, united_kingdom, 170), Edge(singapore, japan, 79), @@ -435,15 +435,15 @@ class NodeType: Edge(singapore, indonesia, 14), Edge(singapore, chile, 358), Edge(singapore, estonia, 176), - Edge(united_kingdom, united_states, 69), - Edge(united_kingdom, germany, 14), - Edge(united_kingdom, france, 9), + Edge(united_kingdom, united_states, 132), + Edge(united_kingdom, germany, 17), + Edge(united_kingdom, france, 15), Edge(united_kingdom, finland, 35), - Edge(united_kingdom, canada, 90), + Edge(united_kingdom, canada, 98), Edge(united_kingdom, singapore, 170), - Edge(united_kingdom, united_kingdom, 2), - Edge(united_kingdom, japan, 217), - Edge(united_kingdom, netherlands, 8), + Edge(united_kingdom, united_kingdom, 13), + Edge(united_kingdom, japan, 233), + Edge(united_kingdom, netherlands, 10), Edge(united_kingdom, south_korea, 270), Edge(united_kingdom, austria, 28), Edge(united_kingdom, australia, 280), @@ -490,14 +490,14 @@ class NodeType: Edge(united_kingdom, chile, 201), Edge(united_kingdom, estonia, 34), Edge(japan, united_states, 184), - Edge(japan, germany, 222), + Edge(japan, germany, 240), Edge(japan, france, 235), Edge(japan, finland, 243), Edge(japan, canada, 179), Edge(japan, singapore, 79), - Edge(japan, united_kingdom, 217), - Edge(japan, japan, 2), - Edge(japan, netherlands, 240), + Edge(japan, united_kingdom, 234), + Edge(japan, japan, 8), + Edge(japan, netherlands, 333), Edge(japan, south_korea, 37), Edge(japan, austria, 234), Edge(japan, australia, 114), @@ -543,15 +543,15 @@ class NodeType: Edge(japan, indonesia, 80), Edge(japan, chile, 356), Edge(japan, estonia, 241), - Edge(netherlands, united_states, 79), - Edge(netherlands, germany, 12), - Edge(netherlands, france, 10), - Edge(netherlands, finland, 27), - Edge(netherlands, canada, 95), + Edge(netherlands, united_states, 137), + Edge(netherlands, germany, 15), + Edge(netherlands, france, 24), + Edge(netherlands, finland, 33), + Edge(netherlands, canada, 142), Edge(netherlands, singapore, 163), - Edge(netherlands, united_kingdom, 7), - Edge(netherlands, japan, 240), - Edge(netherlands, netherlands, 2), + Edge(netherlands, united_kingdom, 10), + Edge(netherlands, japan, 333), + Edge(netherlands, netherlands, 3), Edge(netherlands, south_korea, 273), Edge(netherlands, austria, 19), Edge(netherlands, australia, 291), @@ -3072,6 +3072,10 @@ class NodeType: for t1 in node_types: for t2 in node_types: for edge in edges: + edge_latency = edge.latency + if edge_latency > 1: + edge_latency = edge.latency // 2 # the ping latencies is RTT and we need RTT // 2 + G.add_edge(f"{edge.src.name}-{t1.name}", f"{edge.dst.name}-{t2.name}", label=f"{edge.src.name}-{t1.name} to {edge.dst.name}-{t2.name}", latency=f"{edge.latency} ms", diff --git a/simconfig.yaml b/simconfig.yaml index f6e6ad5..0350286 100644 --- a/simconfig.yaml +++ b/simconfig.yaml @@ -5,8 +5,18 @@ client_split: lighthouse: 56 prysm_validator: batch_interval: 5 - batch_verifier_time: 4500 + p99_batch_verifier_time: 32500 + p95_batch_verifier_time: 8500 + p90_batch_verifier_time: 5000 + p75_batch_verifier_time: 4350 + p50_batch_verifier_time: 3900 + p25_batch_verifier_time: 3900 lighthouse_validator: - validator_time: 2500 + p99_validator_time: 9500 + p95_validator_time: 4900 + p90_validator_time: 4650 + p75_validator_time: 3700 + p50_validator_time: 2600 + p25_validator_time: 1300 slots_to_run: 30 msg_size: 192 diff --git a/simconfig/simconfig.go b/simconfig/simconfig.go index ccde2fe..716cce0 100644 --- a/simconfig/simconfig.go +++ b/simconfig/simconfig.go @@ -10,11 +10,21 @@ import ( type PrysmValidatorInfo struct { BatchInterval uint64 `yaml:"batch_interval"` - BatchVerifierTime uint64 `yaml:"batch_verifier_time"` + P99BatchVerifierTime uint64 `yaml:"p99_batch_verifier_time"` + P95BatchVerifierTime uint64 `yaml:"p95_batch_verifier_time" ` + P90BatchVerifierTime uint64 `yaml:"p90_batch_verifier_time"` + P75BatchVerifierTime uint64 `yaml:"p75_batch_verifier_time"` + P50BatchVerifierTime uint64 `yaml:"p50_batch_verifier_time"` + P25BatchVerifierTime uint64 `yaml:"p25_batch_verifier_time"` } type LighthouseValidatorInfo struct { - ValidatorTime uint64 `yaml:"validator_time"` + P99ValidatorTime uint64 `yaml:"p99_validator_time"` + P95ValidatorTime uint64 `yaml:"p95_validator_time"` + P90ValidatorTime uint64 `yaml:"p90_validator_time"` + P75ValidatorTime uint64 `yaml:"p75_validator_time"` + P50ValidatorTime uint64 `yaml:"p50_validator_time"` + P25ValidatorTime uint64 `yaml:"p25_validator_time"` } type ClientSplit struct { diff --git a/updated-plots/50ms_batch_interval_plot.png b/updated-plots/50ms_batch_interval_plot.png new file mode 100644 index 0000000..b2e8c54 Binary files /dev/null and b/updated-plots/50ms_batch_interval_plot.png differ diff --git a/updated-plots/5ms_batch_interval_plot.png b/updated-plots/5ms_batch_interval_plot.png new file mode 100644 index 0000000..6f8765e Binary files /dev/null and b/updated-plots/5ms_batch_interval_plot.png differ