add seeds

This commit is contained in:
Youngjoon Lee 2024-07-05 17:01:11 +09:00
parent a260047cef
commit 3067a5d6b4
No known key found for this signature in database
GPG Key ID: B4253AFBA618BF4D
5 changed files with 87 additions and 27 deletions

View File

@ -1,7 +1,8 @@
from __future__ import annotations
import hashlib
import random
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import List
from cryptography.hazmat.primitives.asymmetric.x25519 import (
@ -26,10 +27,19 @@ class NodeConfig:
peering_degree: int
mix_path_length: int # TODO: use this when creating Sphinx packets
def id(self, short=False) -> str:
id = (
hashlib.sha256(self.private_key.public_key().public_bytes_raw())
.digest()
.hex()
)
return id[:8] if short else id
@dataclass
class MixMembership:
nodes: List[NodeInfo]
rng: random.Random = field(default_factory=random.Random)
def generate_route(self, num_hops: int, last_mix: NodeInfo) -> list[NodeInfo]:
"""
@ -43,7 +53,7 @@ class MixMembership:
"""
Choose a mix node as a mix destination that will reconstruct a message from Sphinx packets.
"""
return random.choice(self.nodes)
return self.rng.choice(self.nodes)
@dataclass

View File

@ -62,7 +62,7 @@ class Node:
if msg_with_flag is not None:
flag, msg = PacketBuilder.parse_msg_and_flag(msg_with_flag)
if flag == MessageFlag.MESSAGE_FLAG_REAL:
print(f"Broadcasting message finally: {msg}")
print(f"{self.config.id(True)}: Broadcasting message finally: {msg}")
await self.broadcast_channel.put(msg)
def connect(
@ -93,7 +93,7 @@ class Node:
)
async def send_message(self, msg: bytes):
print(f"Sending message: {msg}")
print(f"{self.config.id(True)}: Sending message: {msg}")
for packet, _ in PacketBuilder.build_real_packets(
msg, self.global_config.membership
):

View File

@ -1,5 +1,7 @@
from __future__ import annotations
import hashlib
import random
from dataclasses import dataclass
import dacite
@ -19,7 +21,11 @@ class Config:
def load(cls, yaml_path: str) -> Config:
with open(yaml_path, "r") as f:
data = yaml.safe_load(f)
config = dacite.from_dict(data_class=Config, data=data)
config = dacite.from_dict(
data_class=Config,
data=data,
config=dacite.Config(type_hooks={random.Random: seed_to_random}),
)
# Validations
config.simulation.validate()
@ -41,33 +47,72 @@ class SimulationConfig:
@dataclass
class LogicConfig:
lottery_interval_sec: float
sender_prob: float
sender_lottery: LotteryConfig
def validate(self):
assert self.lottery_interval_sec > 0
assert self.sender_prob > 0
self.sender_lottery.validate()
@dataclass
class LotteryConfig:
interval_sec: float
probability: float
seed: random.Random
def validate(self):
assert self.interval_sec > 0
assert self.probability > 0
assert self.seed is not None
@dataclass
class MixnetConfig:
num_nodes: int
transmission_rate_per_sec: int
peering_degree: int
max_mix_path_length: int
peering: PeeringConfig
mix_path: MixPathConfig
def validate(self):
assert self.num_nodes > 0
assert self.transmission_rate_per_sec > 0
assert self.peering_degree > 0
assert self.max_mix_path_length > 0
self.peering.validate()
self.mix_path.validate()
def node_configs(self) -> list[NodeConfig]:
return [
NodeConfig(
X25519PrivateKey.generate(),
self.peering_degree,
self._gen_private_key(i),
self.peering.degree,
self.transmission_rate_per_sec,
)
for _ in range(self.num_nodes)
for i in range(self.num_nodes)
]
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:
degree: int
seed: random.Random
def validate(self):
assert self.degree > 0
assert self.seed is not None
@dataclass
class MixPathConfig:
max_length: int
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)

View File

@ -3,11 +3,17 @@ simulation:
net_latency_sec: 0.01
logic:
lottery_interval_sec: 1
sender_prob: 0.01
sender_lottery:
interval_sec: 1
probability: 0.01
seed: 10
mixnet:
num_nodes: 5
transmission_rate_per_sec: 10
peering_degree: 6
max_mix_path_length: 3
peering:
degree: 6
seed: 0
mix_path:
max_length: 3
seed: 3

View File

@ -1,5 +1,3 @@
import random
import usim
import mixnet.framework.usim as usimfw
@ -16,7 +14,6 @@ class Simulation:
framework: Framework
def __init__(self, config: Config):
random.seed()
self.config = config
async def run(self):
@ -39,10 +36,11 @@ class Simulation:
[
NodeInfo(node_config.private_key.public_key())
for node_config in node_configs
]
],
self.config.mixnet.mix_path.seed,
),
self.config.mixnet.transmission_rate_per_sec,
self.config.mixnet.max_mix_path_length,
self.config.mixnet.mix_path.max_length,
)
nodes = [
Node(self.framework, node_config, global_config)
@ -66,7 +64,8 @@ class Simulation:
return MeteredRemoteSimplexConnection(self.config.simulation, self.framework)
async def run_logic(self, node: Node):
lottery_config = self.config.logic.sender_lottery
while True:
await (usim.time + self.config.logic.lottery_interval_sec)
if random.random() < self.config.logic.sender_prob:
await (usim.time + lottery_config.interval_sec)
if lottery_config.seed.random() < lottery_config.probability:
await node.send_message(b"selected block")