mirror of
https://github.com/logos-blockchain/logos-blockchain-specs.git
synced 2026-01-05 22:53:11 +00:00
refactor: subclasses for Config
This commit is contained in:
parent
fa4635b749
commit
66e5549fdc
@ -34,9 +34,9 @@ class Adversary:
|
||||
|
||||
def is_around_message_interval(self, time: SimTime):
|
||||
now_frac, now_int = math.modf(time)
|
||||
return now_int % self.config.message_interval == 0 and now_frac <= self.config.max_message_prep_time
|
||||
return now_int % self.config.mixnet.message_interval == 0 and now_frac <= self.config.mixnet.max_message_prep_time
|
||||
|
||||
def update_observation_window(self):
|
||||
while True:
|
||||
self.mixed_msgs_per_window.append(defaultdict(int))
|
||||
yield self.env.timeout(self.config.io_observation_window)
|
||||
yield self.env.timeout(self.config.adversary.io_observation_window)
|
||||
|
||||
@ -20,7 +20,7 @@ class Analysis:
|
||||
|
||||
def messages_emitted_around_interval(self):
|
||||
df = pd.DataFrame(
|
||||
[(node.id, cnt, node.id < len(self.sim.config.real_message_prob_weights))
|
||||
[(node.id, cnt, node.id < len(self.sim.config.mixnet.real_message_prob_weights))
|
||||
for node, cnt in self.sim.p2p.adversary.senders_around_interval.items()],
|
||||
columns=["node_id", "msg_count", "expected"]
|
||||
)
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Self
|
||||
|
||||
@ -7,7 +8,36 @@ import yaml
|
||||
|
||||
@dataclass
|
||||
class Config:
|
||||
simulation: SimulationConfig
|
||||
mixnet: MixnetConfig
|
||||
p2p: P2pConfig
|
||||
adversary: AdversaryConfig
|
||||
|
||||
@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
|
||||
config.simulation.validate()
|
||||
config.mixnet.validate()
|
||||
config.p2p.validate()
|
||||
config.adversary.validate()
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@dataclass
|
||||
class SimulationConfig:
|
||||
running_time: int
|
||||
|
||||
def validate(self):
|
||||
assert self.running_time > 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class MixnetConfig:
|
||||
num_nodes: int
|
||||
num_mix_layers: int
|
||||
# An interval of sending a new real/cover message
|
||||
@ -23,31 +53,35 @@ class Config:
|
||||
cover_message_prob: float
|
||||
# A maximum preparation time (delay) before sending the message
|
||||
max_message_prep_time: float
|
||||
# A maximum network latency between nodes directly connected with each other
|
||||
max_network_latency: float
|
||||
# A maximum delay of messages mixed in a mix node
|
||||
max_mix_delay: float
|
||||
|
||||
def validate(self):
|
||||
assert self.num_nodes > 0
|
||||
assert 0 < self.num_mix_layers <= self.num_nodes
|
||||
assert self.message_interval > 0
|
||||
assert self.real_message_prob > 0
|
||||
assert len(self.real_message_prob_weights) <= self.num_nodes
|
||||
for weight in self.real_message_prob_weights:
|
||||
assert weight >= 1
|
||||
assert self.cover_message_prob >= 0
|
||||
assert self.max_message_prep_time >= 0
|
||||
assert self.max_mix_delay >= 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class P2pConfig:
|
||||
# A maximum network latency between nodes directly connected with each other
|
||||
max_network_latency: float
|
||||
|
||||
def validate(self):
|
||||
assert self.max_network_latency >= 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class AdversaryConfig:
|
||||
# A discrete time window for the adversary to observe inputs and outputs of a certain node
|
||||
io_observation_window: int
|
||||
|
||||
@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 len(config.real_message_prob_weights) <= config.num_nodes
|
||||
for weight in config.real_message_prob_weights:
|
||||
assert weight >= 1
|
||||
assert config.cover_message_prob >= 0
|
||||
assert config.max_message_prep_time >= 0
|
||||
assert config.max_network_latency >= 0
|
||||
assert config.io_observation_window >= 0
|
||||
|
||||
return config
|
||||
def validate(self):
|
||||
assert self.io_observation_window >= 0
|
||||
|
||||
@ -1,23 +1,30 @@
|
||||
# The simulation uses a virtual time. Please see README for more details.
|
||||
running_time: 30
|
||||
num_nodes: 100
|
||||
num_mix_layers: 3
|
||||
# An interval of sending a new real/cover message
|
||||
# A probability of actually sending a message depends on the following parameters.
|
||||
message_interval: 1
|
||||
# A probability of sending a real message within a cycle
|
||||
real_message_prob: 0.1
|
||||
# A weight of real message emission probability of some nodes
|
||||
# Each weight is assigned to each node in the order of the node ID.
|
||||
# The length of the list should be <= num_nodes. i.e. some nodes won't have a weight.
|
||||
real_message_prob_weights: [10, 8, 12]
|
||||
# A probability of sending a cover message within a cycle if not sending a real message
|
||||
cover_message_prob: 0.2
|
||||
# A maximum preparation time (delay) before sending the message
|
||||
max_message_prep_time: 0.3
|
||||
# A maximum network latency between nodes directly connected with each other
|
||||
max_network_latency: 0.5
|
||||
# A maximum delay of messages mixed in a mix node
|
||||
max_mix_delay: 3
|
||||
# A discrete time window for the adversary to observe inputs and outputs of a certain node
|
||||
io_observation_window: 1
|
||||
simulation:
|
||||
# The simulation uses a virtual time. Please see README for more details.
|
||||
running_time: 30
|
||||
|
||||
mixnet:
|
||||
num_nodes: 100
|
||||
num_mix_layers: 3
|
||||
# An interval of sending a new real/cover message
|
||||
# A probability of actually sending a message depends on the following parameters.
|
||||
message_interval: 1
|
||||
# A probability of sending a real message within a cycle
|
||||
real_message_prob: 0.1
|
||||
# A weight of real message emission probability of some nodes
|
||||
# Each weight is assigned to each node in the order of the node ID.
|
||||
# The length of the list should be <= p2p.num_nodes. i.e. some nodes won't have a weight.
|
||||
real_message_prob_weights: [10, 8, 12]
|
||||
# A probability of sending a cover message within a cycle if not sending a real message
|
||||
cover_message_prob: 0.2
|
||||
# A maximum preparation time (delay) before sending the message
|
||||
max_message_prep_time: 0.3
|
||||
# A maximum delay of messages mixed in a mix node
|
||||
max_mix_delay: 3
|
||||
|
||||
p2p:
|
||||
# A maximum network latency between nodes directly connected with each other
|
||||
max_network_latency: 0.5
|
||||
|
||||
adversary:
|
||||
# A discrete time window for the adversary to observe inputs and outputs of a certain node
|
||||
io_observation_window: 1
|
||||
@ -29,13 +29,13 @@ class Node:
|
||||
Creates/encapsulate a message and send it to the network through the mixnet
|
||||
"""
|
||||
while True:
|
||||
yield self.env.timeout(self.config.message_interval)
|
||||
yield self.env.timeout(self.config.mixnet.message_interval)
|
||||
|
||||
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)
|
||||
prep_time = random.uniform(0, self.config.mixnet.max_message_prep_time)
|
||||
yield self.env.timeout(prep_time)
|
||||
|
||||
self.log("Sending a message to the mixnet")
|
||||
@ -46,22 +46,22 @@ class Node:
|
||||
rnd = random.random()
|
||||
if rnd < self.real_message_prob():
|
||||
return self.REAL_PAYLOAD
|
||||
elif rnd < self.config.cover_message_prob:
|
||||
elif rnd < self.config.mixnet.cover_message_prob:
|
||||
return self.COVER_PAYLOAD
|
||||
else:
|
||||
return None
|
||||
|
||||
def real_message_prob(self):
|
||||
weight = self.config.real_message_prob_weights[self.id] \
|
||||
if self.id < len(self.config.real_message_prob_weights) else 0
|
||||
return self.config.real_message_prob * weight
|
||||
weight = self.config.mixnet.real_message_prob_weights[self.id] \
|
||||
if self.id < len(self.config.mixnet.real_message_prob_weights) else 0
|
||||
return self.config.mixnet.real_message_prob * weight
|
||||
|
||||
def create_message(self, payload: bytes) -> SphinxPacket:
|
||||
"""
|
||||
Creates a message using the Sphinx format
|
||||
@return:
|
||||
"""
|
||||
mixes = self.p2p.get_nodes(self.config.num_mix_layers)
|
||||
mixes = self.p2p.get_nodes(self.config.mixnet.num_mix_layers)
|
||||
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]
|
||||
@ -89,7 +89,7 @@ class Node:
|
||||
self.log("Dropping a cover message: %s" % msg.payload)
|
||||
else:
|
||||
# TODO: use Poisson delay or something else, if necessary
|
||||
yield self.env.timeout(random.uniform(0, self.config.max_mix_delay))
|
||||
yield self.env.timeout(random.uniform(0, self.config.mixnet.max_mix_delay))
|
||||
self.env.process(self.p2p.broadcast(self, msg))
|
||||
else:
|
||||
self.log("Receiving SphinxPacket, but not mine")
|
||||
|
||||
@ -44,7 +44,7 @@ class P2p:
|
||||
|
||||
def send(self, msg: SphinxPacket | bytes, node):
|
||||
# simulate network latency
|
||||
yield self.env.timeout(random.uniform(0, self.config.max_network_latency))
|
||||
yield self.env.timeout(random.uniform(0, self.config.p2p.max_network_latency))
|
||||
|
||||
self.adversary.observe_incoming_message(node)
|
||||
self.env.process(node.receive_message(msg))
|
||||
|
||||
@ -13,8 +13,8 @@ class Simulation:
|
||||
self.config = config
|
||||
self.env = simpy.Environment()
|
||||
self.p2p = P2p(self.env, config)
|
||||
self.nodes = [Node(i, self.env, self.p2p, config) for i in range(config.num_nodes)]
|
||||
self.nodes = [Node(i, self.env, self.p2p, config) for i in range(config.mixnet.num_nodes)]
|
||||
self.p2p.add_node(self.nodes)
|
||||
|
||||
def run(self):
|
||||
self.env.run(until=self.config.running_time)
|
||||
self.env.run(until=self.config.simulation.running_time)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user