nomos-specs/mixnet/client.py

76 lines
2.6 KiB
Python

from __future__ import annotations
import queue
import time
from datetime import datetime, timedelta
from threading import Thread
from mixnet.mixnet import Mixnet, MixnetTopology
from mixnet.node import PacketQueue
from mixnet.packet import PacketBuilder
from mixnet.poisson import poisson_interval_sec
class MixClientRunner(Thread):
"""
Emit packets at the Poisson emission_rate_per_min.
If a real packet is scheduled to be sent, this thread sends the real packet to the mixnet,
and schedules redundant real packets to be emitted in the next turns.
If no real packet is not scheduled, this thread emits a cover packet according to the emission_rate_per_min.
"""
def __init__(
self,
mixnet: Mixnet,
topology: MixnetTopology,
emission_rate_per_min: int, # Poisson rate parameter: lambda in the spec
redundancy: int, # b in the spec
real_packet_queue: PacketQueue,
outbound_socket: PacketQueue,
):
super().__init__()
self.mixnet = mixnet
self.topology = topology
self.emission_rate_per_min = emission_rate_per_min
self.redundancy = redundancy
self.real_packet_queue = real_packet_queue
self.redundant_real_packet_queue: PacketQueue = queue.Queue()
self.outbound_socket = outbound_socket
def run(self) -> None:
# Here in Python, this thread is implemented in synchronous manner.
# In the real implementation, consider implementing this in asynchronous if possible.
next_emission_ts = datetime.now() + timedelta(
seconds=poisson_interval_sec(self.emission_rate_per_min)
)
while True:
time.sleep(1 / 1000)
if datetime.now() < next_emission_ts:
continue
next_emission_ts += timedelta(
seconds=poisson_interval_sec(self.emission_rate_per_min)
)
if not self.redundant_real_packet_queue.empty():
addr, packet = self.redundant_real_packet_queue.get()
self.outbound_socket.put((addr, packet))
continue
if not self.real_packet_queue.empty():
addr, packet = self.real_packet_queue.get()
# Schedule redundant real packets
for _ in range(self.redundancy - 1):
self.redundant_real_packet_queue.put((addr, packet))
self.outbound_socket.put((addr, packet))
packet, route = PacketBuilder.drop_cover(
b"drop cover", self.mixnet, self.topology
).next()
self.outbound_socket.put((route[0].addr, packet))