nomos-specs/mixnet/sim/simulation.py

89 lines
3.2 KiB
Python
Raw Normal View History

import usim
import mixnet.framework.usim as usimfw
2024-07-03 23:29:26 +09:00
from mixnet.config import GlobalConfig, MixMembership, NodeInfo
from mixnet.framework.framework import Framework
2024-07-05 17:19:02 +09:00
from mixnet.node import Node, PeeringDegreeReached
2024-07-03 23:29:26 +09:00
from mixnet.sim.config import Config
from mixnet.sim.connection import MeteredRemoteSimplexConnection
2024-07-05 22:57:46 +09:00
from mixnet.sim.state import AllNodeStates, NodeState
2024-07-03 23:29:26 +09:00
from mixnet.sim.stats import ConnectionStats
class Simulation:
config: Config
framework: Framework
2024-07-03 23:29:26 +09:00
def __init__(self, config: Config):
self.config = config
async def run(self):
2024-07-05 22:57:46 +09:00
conn_stats, all_node_states = await self._run()
conn_stats.bandwidths()
2024-07-05 22:57:46 +09:00
all_node_states.analyze()
2024-07-03 23:29:26 +09:00
2024-07-05 22:57:46 +09:00
async def _run(self) -> tuple[ConnectionStats, AllNodeStates]:
async with usim.until(usim.time + self.config.simulation.duration_sec) as scope:
self.framework = usimfw.Framework(scope)
2024-07-05 22:57:46 +09:00
nodes, conn_stats, all_node_states = self.init_nodes()
for node in nodes:
self.framework.spawn(self.run_logic(node))
2024-07-05 22:57:46 +09:00
return conn_stats, all_node_states
assert False # unreachable
2024-07-03 23:29:26 +09:00
2024-07-05 22:57:46 +09:00
def init_nodes(self) -> tuple[list[Node], ConnectionStats, AllNodeStates]:
2024-07-03 23:29:26 +09:00
node_configs = self.config.mixnet.node_configs()
global_config = GlobalConfig(
MixMembership(
[
NodeInfo(node_config.private_key.public_key())
for node_config in node_configs
2024-07-05 17:01:11 +09:00
],
self.config.mixnet.mix_path.seed,
2024-07-03 23:29:26 +09:00
),
self.config.mixnet.transmission_rate_per_sec,
2024-07-05 17:01:11 +09:00
self.config.mixnet.mix_path.max_length,
2024-07-03 23:29:26 +09:00
)
nodes = [
Node(self.framework, node_config, global_config)
for node_config in node_configs
]
2024-07-03 23:29:26 +09:00
2024-07-05 22:57:46 +09:00
all_node_states = AllNodeStates(len(nodes), self.config.simulation.duration_sec)
2024-07-03 23:29:26 +09:00
conn_stats = ConnectionStats()
for i, node in enumerate(nodes):
2024-07-05 22:57:46 +09:00
peer_idx = (i + 1) % len(nodes)
peer = nodes[peer_idx]
node_states = all_node_states[i]
peer_states = all_node_states[peer_idx]
inbound_conn, outbound_conn = (
2024-07-05 22:57:46 +09:00
self.create_conn(peer_states, node_states),
self.create_conn(node_states, peer_states),
)
2024-07-05 17:19:02 +09:00
try:
node.connect(peer, inbound_conn, outbound_conn)
except PeeringDegreeReached:
continue
2024-07-03 23:29:26 +09:00
conn_stats.register(node, inbound_conn, outbound_conn)
conn_stats.register(peer, outbound_conn, inbound_conn)
2024-07-03 23:29:26 +09:00
2024-07-05 22:57:46 +09:00
return nodes, conn_stats, all_node_states
2024-07-03 23:29:26 +09:00
2024-07-05 22:57:46 +09:00
def create_conn(
self, sender_states: list[NodeState], receiver_states: list[NodeState]
) -> MeteredRemoteSimplexConnection:
2024-07-05 17:29:20 +09:00
return MeteredRemoteSimplexConnection(
2024-07-05 22:57:46 +09:00
self.config.simulation.network,
self.framework,
sender_states,
receiver_states,
2024-07-05 17:29:20 +09:00
)
2024-07-03 23:29:26 +09:00
async def run_logic(self, node: Node):
2024-07-05 17:01:11 +09:00
lottery_config = self.config.logic.sender_lottery
while True:
2024-07-05 17:01:11 +09:00
await (usim.time + lottery_config.interval_sec)
if lottery_config.seed.random() < lottery_config.probability:
2024-07-03 23:29:26 +09:00
await node.send_message(b"selected block")