144 lines
3.7 KiB
Python
Raw Normal View History

2024-07-03 23:29:26 +09:00
from __future__ import annotations
2024-07-05 17:01:11 +09:00
import hashlib
import random
2024-07-03 23:29:26 +09:00
from dataclasses import dataclass
import dacite
import yaml
from pysphinx.sphinx import X25519PrivateKey
from mixnet.config import NodeConfig
@dataclass
class Config:
simulation: SimulationConfig
logic: LogicConfig
mixnet: MixnetConfig
@classmethod
def load(cls, yaml_path: str) -> Config:
with open(yaml_path, "r") as f:
data = yaml.safe_load(f)
2024-07-05 17:01:11 +09:00
config = dacite.from_dict(
data_class=Config,
data=data,
config=dacite.Config(type_hooks={random.Random: seed_to_random}),
)
2024-07-03 23:29:26 +09:00
# Validations
config.simulation.validate()
config.logic.validate()
config.mixnet.validate()
return config
@dataclass
class SimulationConfig:
2024-07-08 17:41:31 +09:00
# Desired duration of the simulation in seconds
# Since the simulation uses discrete time steps, the actual duration may be longer or shorter.
2024-07-03 23:29:26 +09:00
duration_sec: int
2024-07-05 17:29:20 +09:00
network: NetworkConfig
2024-07-03 23:29:26 +09:00
def validate(self):
assert self.duration_sec > 0
2024-07-05 17:29:20 +09:00
self.network.validate()
@dataclass
class NetworkConfig:
2024-07-08 17:41:31 +09:00
# Maximum network latency between nodes in seconds.
# A constant latency will be chosen randomly for each connection within the range [0, max_latency_sec].
2024-07-05 17:29:20 +09:00
max_latency_sec: float
2024-07-08 17:41:31 +09:00
# Seed for the random number generator used to determine the network latencies.
2024-07-05 17:29:20 +09:00
seed: random.Random
def validate(self):
assert self.max_latency_sec > 0
assert self.seed is not None
2024-07-05 22:57:46 +09:00
def random_latency(self) -> float:
# round to milliseconds to make analysis not too heavy
return int(self.seed.random() * self.max_latency_sec * 1000) / 1000
2024-07-03 23:29:26 +09:00
@dataclass
class LogicConfig:
2024-07-05 17:01:11 +09:00
sender_lottery: LotteryConfig
2024-07-03 23:29:26 +09:00
def validate(self):
2024-07-05 17:01:11 +09:00
self.sender_lottery.validate()
@dataclass
class LotteryConfig:
2024-07-08 17:41:31 +09:00
# Interval between lottery draws in seconds.
2024-07-05 17:01:11 +09:00
interval_sec: float
2024-07-08 17:41:31 +09:00
# Probability of a node being selected as a sender in each lottery draw.
2024-07-05 17:01:11 +09:00
probability: float
2024-07-08 17:41:31 +09:00
# Seed for the random number generator used to determine the lottery winners.
2024-07-05 17:01:11 +09:00
seed: random.Random
def validate(self):
assert self.interval_sec > 0
2024-07-08 16:22:17 +09:00
assert self.probability >= 0
2024-07-05 17:01:11 +09:00
assert self.seed is not None
2024-07-03 23:29:26 +09:00
@dataclass
class MixnetConfig:
2024-07-08 17:41:31 +09:00
# Total number of nodes in the entire network.
2024-07-03 23:29:26 +09:00
num_nodes: int
2024-07-08 17:41:31 +09:00
# Global constant transmission rate of each connection in messages per second.
2024-07-03 23:29:26 +09:00
transmission_rate_per_sec: int
2024-07-05 17:01:11 +09:00
peering: PeeringConfig
mix_path: MixPathConfig
2024-07-03 23:29:26 +09:00
def validate(self):
assert self.num_nodes > 0
assert self.transmission_rate_per_sec > 0
2024-07-05 17:01:11 +09:00
self.peering.validate()
self.mix_path.validate()
2024-07-03 23:29:26 +09:00
def node_configs(self) -> list[NodeConfig]:
return [
NodeConfig(
2024-07-05 17:01:11 +09:00
self._gen_private_key(i),
self.peering.degree,
2024-07-03 23:29:26 +09:00
self.transmission_rate_per_sec,
)
2024-07-05 17:01:11 +09:00
for i in range(self.num_nodes)
2024-07-03 23:29:26 +09:00
]
2024-07-05 17:01:11 +09:00
def _gen_private_key(self, node_idx: int) -> X25519PrivateKey:
return X25519PrivateKey.from_private_bytes(
hashlib.sha256(node_idx.to_bytes(4, "big")).digest()[:32]
)
@dataclass
class PeeringConfig:
2024-07-08 17:41:31 +09:00
# Target number of peers each node can connect to (both inbound and outbound).
2024-07-05 17:01:11 +09:00
degree: int
def validate(self):
assert self.degree > 0
@dataclass
class MixPathConfig:
2024-07-08 17:41:31 +09:00
# Maximum number of mix nodes to be chosen for a Sphinx packet.
2024-07-05 17:01:11 +09:00
max_length: int
2024-07-08 17:41:31 +09:00
# Seed for the random number generator used to determine the mix path.
2024-07-05 17:01:11 +09:00
seed: random.Random
def validate(self):
assert self.max_length > 0
assert self.seed is not None
def seed_to_random(seed: int) -> random.Random:
return random.Random(seed)