add cover prob

This commit is contained in:
Youngjoon Lee 2024-05-15 16:53:26 +09:00
parent 38d1738565
commit 373bab822c
No known key found for this signature in database
GPG Key ID: 09B750B5BD6F08A2
4 changed files with 43 additions and 15 deletions

View File

@ -1,4 +1,8 @@
from dataclasses import dataclass
from typing import Self
import dacite
import yaml
@dataclass
@ -7,5 +11,24 @@ class Config:
num_nodes: int
num_mix_layers: int
message_interval: int
message_prob: float
real_message_prob: float
cover_message_prob: float
max_message_prep_time: float
@classmethod
def load(cls, yaml_path: str) -> Self:
with open(yaml_path, "r") as f:
data = yaml.safe_load(f)
config = dacite.from_dict(data_class=Config, data=data)
# Validations
assert config.running_time > 0
assert config.num_nodes > 0
assert 0 < config.num_mix_layers <= config.num_nodes
assert config.message_interval > 0
assert config.real_message_prob >= 0
assert config.cover_message_prob >= 0
assert config.real_message_prob + config.cover_message_prob <= 1
assert config.max_message_prep_time >= 0
return config

View File

@ -2,5 +2,8 @@ running_time: 30
num_nodes: 5
num_mix_layers: 3
message_interval: 1
message_prob: 0.2
# (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
max_message_prep_time: 0.3

View File

@ -1,10 +1,8 @@
import argparse
import dacite
import matplotlib.pyplot as plt
import pandas as pd
import seaborn
import yaml
from config import Config
from simulation import Simulation
@ -14,10 +12,7 @@ if __name__ == "__main__":
parser.add_argument("--config", type=str, required=True, help="Configuration file path")
args = parser.parse_args()
with open(args.config, "r") as f:
config = yaml.safe_load(f)
config = dacite.from_dict(data_class=Config, data=config)
config = Config.load(args.config)
sim = Simulation(config)
sim.run()

View File

@ -29,23 +29,29 @@ class Node:
Creates/encapsulate a message and send it to the network through the mixnet
"""
while True:
# TODO: Use the realistic cover traffic emission rate
yield self.env.timeout(self.config.message_interval)
if not self.is_message_sender():
payload = self.payload_to_send()
if payload is None: # nothing to send in this turn
continue
prep_time = random.uniform(0, self.config.max_message_prep_time)
yield self.env.timeout(prep_time)
self.log("Sending a message to the mixnet")
msg = self.create_message()
msg = self.create_message(payload)
self.env.process(self.p2p.broadcast(msg))
def is_message_sender(self) -> bool:
return random.random() < self.config.message_prob
def payload_to_send(self) -> bytes | None:
rnd = random.random()
if rnd < self.config.real_message_prob:
return self.REAL_PAYLOAD
elif rnd < self.config.real_message_prob + self.config.cover_message_prob:
return self.COVER_PAYLOAD
else:
return None
def create_message(self) -> SphinxPacket:
def create_message(self, payload: bytes) -> SphinxPacket:
"""
Creates a message using the Sphinx format
@return:
@ -54,7 +60,6 @@ class Node:
public_keys = [mix.public_key for mix in mixes]
# TODO: replace with realistic tx
incentive_txs = [Node.create_incentive_tx(mix.public_key) for mix in mixes]
payload = random.choice([self.REAL_PAYLOAD, self.COVER_PAYLOAD])
return SphinxPacket(public_keys, incentive_txs, payload)
def receive_message(self, msg: SphinxPacket | bytes):
@ -78,6 +83,8 @@ class Node:
+ self.PADDING_SEPARATOR
+ bytes(len(msg) - len(msg.payload) - len(self.PADDING_SEPARATOR)))
self.env.process(self.p2p.broadcast(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))