From 661f40680c786f032c0b755f3057b27be13bc02d Mon Sep 17 00:00:00 2001 From: Youngjoon Lee <5462944+youngjoon-lee@users.noreply.github.com> Date: Wed, 15 May 2024 18:01:42 +0900 Subject: [PATCH] analyze nodes who emitted messages around the promised interval --- mixnet/v2/sim/config.py | 2 +- mixnet/v2/sim/config.yaml | 6 +++--- mixnet/v2/sim/main.py | 13 +++++++++---- mixnet/v2/sim/node.py | 6 +++--- mixnet/v2/sim/p2p.py | 13 +++++++++++-- mixnet/v2/sim/simulation.py | 2 +- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/mixnet/v2/sim/config.py b/mixnet/v2/sim/config.py index e3721a7..2f1647c 100644 --- a/mixnet/v2/sim/config.py +++ b/mixnet/v2/sim/config.py @@ -11,7 +11,7 @@ class Config: num_nodes: int num_mix_layers: int message_interval: int - real_message_prob: float + real_message_prob: float # TODO: should be proportional to its stake cover_message_prob: float max_message_prep_time: float diff --git a/mixnet/v2/sim/config.yaml b/mixnet/v2/sim/config.yaml index 27229bf..c2b866d 100644 --- a/mixnet/v2/sim/config.yaml +++ b/mixnet/v2/sim/config.yaml @@ -1,9 +1,9 @@ running_time: 30 -num_nodes: 5 +num_nodes: 100 num_mix_layers: 3 message_interval: 1 # (real_message_prob + cover_message_prob) should be <= 1 # If the sum is < 1, then the remaining probability is for sending nothing. -real_message_prob: 0.1 -cover_message_prob: 0.4 +real_message_prob: 0.01 +cover_message_prob: 0.2 max_message_prep_time: 0.3 \ No newline at end of file diff --git a/mixnet/v2/sim/main.py b/mixnet/v2/sim/main.py index 1bc549d..6a1037f 100644 --- a/mixnet/v2/sim/main.py +++ b/mixnet/v2/sim/main.py @@ -8,7 +8,7 @@ from config import Config from simulation import Simulation if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Run simulation', formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser = argparse.ArgumentParser(description="Run simulation", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("--config", type=str, required=True, help="Configuration file path") args = parser.parse_args() @@ -16,12 +16,17 @@ if __name__ == "__main__": sim = Simulation(config) sim.run() + # Stat the distribution of message sizes df = pd.DataFrame(sim.p2p.message_sizes, columns=["message_size"]) print(df.describe()) + + # Visualize the nodes emitted messages around the promised interval + df = pd.DataFrame([(node.id, cnt) for node, cnt in sim.p2p.nodes_emitted_msg_around_interval.items()], columns=["node", "count"]) plt.figure(figsize=(10, 6)) - seaborn.boxplot(y=df["message_size"]) - plt.title("Message size distribution") - plt.ylabel("Message Size (bytes)") + seaborn.barplot(x="node", y="count", data=df) + plt.title("Messages emitted around the promised interval") + plt.xlabel("Node ID") + plt.ylabel("Msg Count") plt.show() print("Simulation complete!") \ No newline at end of file diff --git a/mixnet/v2/sim/node.py b/mixnet/v2/sim/node.py index f821393..0f2006a 100644 --- a/mixnet/v2/sim/node.py +++ b/mixnet/v2/sim/node.py @@ -40,7 +40,7 @@ class Node: self.log("Sending a message to the mixnet") msg = self.create_message(payload) - self.env.process(self.p2p.broadcast(msg)) + self.env.process(self.p2p.broadcast(self, msg)) def payload_to_send(self) -> bytes | None: rnd = random.random() @@ -82,13 +82,13 @@ class Node: final_padded_msg = (msg.payload + self.PADDING_SEPARATOR + bytes(len(msg) - len(msg.payload) - len(self.PADDING_SEPARATOR))) - self.env.process(self.p2p.broadcast(final_padded_msg)) + self.env.process(self.p2p.broadcast(self, final_padded_msg)) else: self.log("Dropping a cover message: %s" % msg.payload) else: # TODO: use Poisson delay or something else yield self.env.timeout(random.randint(0, 5)) - self.env.process(self.p2p.broadcast(msg)) + self.env.process(self.p2p.broadcast(self, msg)) else: self.log("Receiving SphinxPacket, but not mine") else: diff --git a/mixnet/v2/sim/p2p.py b/mixnet/v2/sim/p2p.py index 22fb013..733e577 100644 --- a/mixnet/v2/sim/p2p.py +++ b/mixnet/v2/sim/p2p.py @@ -1,24 +1,33 @@ +import math import random +from collections import defaultdict import simpy +from config import Config from sphinx import SphinxPacket class P2p: - def __init__(self, env: simpy.Environment): + def __init__(self, env: simpy.Environment, config: Config): self.env = env + self.config = config self.nodes = [] self.message_sizes = [] + self.nodes_emitted_msg_around_interval = defaultdict(int) def add_node(self, nodes): self.nodes.extend(nodes) # TODO: This should accept only bytes, but SphinxPacket is also accepted until we implement the Sphinx serde - def broadcast(self, msg: SphinxPacket | bytes): + def broadcast(self, sender, msg: SphinxPacket | bytes): self.log("Broadcasting a msg: %d bytes" % len(msg)) self.message_sizes.append(len(msg)) + now_frac, now_int = math.modf(self.env.now) + if now_int % self.config.message_interval == 0 and now_frac <= self.config.max_message_prep_time: + self.nodes_emitted_msg_around_interval[sender] += 1 + # Yield 0 to ensure that the broadcast is done in the same time step. # Without this, SimPy complains that the broadcast func is not a generator. yield self.env.timeout(0) diff --git a/mixnet/v2/sim/simulation.py b/mixnet/v2/sim/simulation.py index 5ec0b92..8af2fc5 100644 --- a/mixnet/v2/sim/simulation.py +++ b/mixnet/v2/sim/simulation.py @@ -12,7 +12,7 @@ class Simulation: random.seed() self.config = config self.env = simpy.Environment() - self.p2p = P2p(self.env) + self.p2p = P2p(self.env, config) self.nodes = [Node(i, self.env, self.p2p, config) for i in range(config.num_nodes)] self.p2p.add_node(self.nodes)