mirror of
https://github.com/logos-co/nomos-specs.git
synced 2025-02-08 13:33:49 +00:00
add comments
This commit is contained in:
parent
adfed3a622
commit
848b3f400c
@ -13,6 +13,10 @@ from pysphinx.sphinx import Node as SphinxNode
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class GlobalConfig:
|
class GlobalConfig:
|
||||||
|
"""
|
||||||
|
Global parameters used across all nodes in the network
|
||||||
|
"""
|
||||||
|
|
||||||
membership: MixMembership
|
membership: MixMembership
|
||||||
transmission_rate_per_sec: int # Global Transmission Rate
|
transmission_rate_per_sec: int # Global Transmission Rate
|
||||||
# TODO: use this to make the size of Sphinx packet constant
|
# TODO: use this to make the size of Sphinx packet constant
|
||||||
@ -21,6 +25,10 @@ class GlobalConfig:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NodeConfig:
|
class NodeConfig:
|
||||||
|
"""
|
||||||
|
Node-specific parameters
|
||||||
|
"""
|
||||||
|
|
||||||
private_key: X25519PrivateKey
|
private_key: X25519PrivateKey
|
||||||
mix_path_length: int
|
mix_path_length: int
|
||||||
gossip: GossipConfig
|
gossip: GossipConfig
|
||||||
@ -34,6 +42,11 @@ class GossipConfig:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MixMembership:
|
class MixMembership:
|
||||||
|
"""
|
||||||
|
A list of public information of nodes in the network.
|
||||||
|
We assume that this list is eventually known to all nodes in the network (e.g. via p2p advertising).
|
||||||
|
"""
|
||||||
|
|
||||||
nodes: List[NodeInfo]
|
nodes: List[NodeInfo]
|
||||||
|
|
||||||
def generate_route(self, num_hops: int, last_mix: NodeInfo) -> list[NodeInfo]:
|
def generate_route(self, num_hops: int, last_mix: NodeInfo) -> list[NodeInfo]:
|
||||||
@ -53,6 +66,10 @@ class MixMembership:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NodeInfo:
|
class NodeInfo:
|
||||||
|
"""
|
||||||
|
Public information of a node to be shared to all nodes in the network
|
||||||
|
"""
|
||||||
|
|
||||||
public_key: X25519PublicKey
|
public_key: X25519PublicKey
|
||||||
|
|
||||||
def sphinx_node(self) -> SphinxNode:
|
def sphinx_node(self) -> SphinxNode:
|
||||||
|
@ -7,6 +7,11 @@ SimplexConnection = NetworkPacketQueue
|
|||||||
|
|
||||||
|
|
||||||
class DuplexConnection:
|
class DuplexConnection:
|
||||||
|
"""
|
||||||
|
A duplex connection in which data can be transmitted and received simultaneously in both directions.
|
||||||
|
This is to mimic duplex communication in a real network (such as TCP or QUIC).
|
||||||
|
"""
|
||||||
|
|
||||||
inbound: SimplexConnection
|
inbound: SimplexConnection
|
||||||
outbound: MixSimplexConnection
|
outbound: MixSimplexConnection
|
||||||
|
|
||||||
@ -22,6 +27,10 @@ class DuplexConnection:
|
|||||||
|
|
||||||
|
|
||||||
class MixSimplexConnection:
|
class MixSimplexConnection:
|
||||||
|
"""
|
||||||
|
Wraps a SimplexConnection to add a transmission rate and noise to the connection.
|
||||||
|
"""
|
||||||
|
|
||||||
queue: NetworkPacketQueue
|
queue: NetworkPacketQueue
|
||||||
conn: SimplexConnection
|
conn: SimplexConnection
|
||||||
transmission_rate_per_sec: int
|
transmission_rate_per_sec: int
|
||||||
@ -39,12 +48,13 @@ class MixSimplexConnection:
|
|||||||
async def __run(self):
|
async def __run(self):
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(1 / self.transmission_rate_per_sec)
|
await asyncio.sleep(1 / self.transmission_rate_per_sec)
|
||||||
# TODO: time mixing
|
# TODO: temporal mixing
|
||||||
if self.queue.empty():
|
if self.queue.empty():
|
||||||
elem = self.noise_msg
|
# To guarantee GTR, send noise if there is no message to send
|
||||||
|
msg = self.noise_msg
|
||||||
else:
|
else:
|
||||||
elem = self.queue.get_nowait()
|
msg = self.queue.get_nowait()
|
||||||
await self.conn.put(elem)
|
await self.conn.put(msg)
|
||||||
|
|
||||||
async def send(self, elem: bytes):
|
async def send(self, msg: bytes):
|
||||||
await self.queue.put(elem)
|
await self.queue.put(msg)
|
||||||
|
@ -7,9 +7,17 @@ from mixnet.connection import DuplexConnection
|
|||||||
|
|
||||||
|
|
||||||
class GossipChannel:
|
class GossipChannel:
|
||||||
|
"""
|
||||||
|
A gossip channel that broadcasts messages to all connected peers.
|
||||||
|
Peers are connected via DuplexConnection.
|
||||||
|
This class simplifies and simulates the libp2p gossipsub.
|
||||||
|
"""
|
||||||
|
|
||||||
config: GossipConfig
|
config: GossipConfig
|
||||||
conns: list[DuplexConnection]
|
conns: list[DuplexConnection]
|
||||||
|
# A handler to process inbound messages.
|
||||||
handler: Callable[[bytes], Awaitable[bytes | None]]
|
handler: Callable[[bytes], Awaitable[bytes | None]]
|
||||||
|
# A set of message hashes to prevent processing the same message twice.
|
||||||
msg_cache: set[bytes]
|
msg_cache: set[bytes]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -20,6 +20,13 @@ BroadcastChannel: TypeAlias = asyncio.Queue[bytes]
|
|||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
|
"""
|
||||||
|
This represents any node in the network, which:
|
||||||
|
- generates/gossips mix messages (Sphinx packets)
|
||||||
|
- performs cryptographic mix (unwrapping Sphinx packets)
|
||||||
|
- generates noise
|
||||||
|
"""
|
||||||
|
|
||||||
config: NodeConfig
|
config: NodeConfig
|
||||||
global_config: GlobalConfig
|
global_config: GlobalConfig
|
||||||
mixgossip_channel: GossipChannel
|
mixgossip_channel: GossipChannel
|
||||||
@ -40,6 +47,9 @@ class Node:
|
|||||||
self.packet_size = len(sample_packet.bytes())
|
self.packet_size = len(sample_packet.bytes())
|
||||||
|
|
||||||
async def __process_msg(self, msg: bytes) -> bytes | None:
|
async def __process_msg(self, msg: bytes) -> bytes | None:
|
||||||
|
"""
|
||||||
|
A handler to process messages received via gossip channel
|
||||||
|
"""
|
||||||
flag, msg = Node.__parse_msg(msg)
|
flag, msg = Node.__parse_msg(msg)
|
||||||
match flag:
|
match flag:
|
||||||
case MsgType.NOISE:
|
case MsgType.NOISE:
|
||||||
@ -56,6 +66,9 @@ class Node:
|
|||||||
async def __process_sphinx_packet(
|
async def __process_sphinx_packet(
|
||||||
self, packet: SphinxPacket
|
self, packet: SphinxPacket
|
||||||
) -> SphinxPacket | None:
|
) -> SphinxPacket | None:
|
||||||
|
"""
|
||||||
|
Unwrap the Sphinx packet and process the next Sphinx packet or the payload.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
processed = packet.process(self.config.private_key)
|
processed = packet.process(self.config.private_key)
|
||||||
match processed:
|
match processed:
|
||||||
@ -68,6 +81,9 @@ class Node:
|
|||||||
return packet
|
return packet
|
||||||
|
|
||||||
async def __process_sphinx_payload(self, payload: Payload):
|
async def __process_sphinx_payload(self, payload: Payload):
|
||||||
|
"""
|
||||||
|
Process the Sphinx payload and broadcast it if it is a real message.
|
||||||
|
"""
|
||||||
msg_with_flag = self.reconstructor.add(
|
msg_with_flag = self.reconstructor.add(
|
||||||
Fragment.from_bytes(payload.recover_plain_playload())
|
Fragment.from_bytes(payload.recover_plain_playload())
|
||||||
)
|
)
|
||||||
@ -77,8 +93,13 @@ class Node:
|
|||||||
await self.broadcast_channel.put(msg)
|
await self.broadcast_channel.put(msg)
|
||||||
|
|
||||||
def connect(self, peer: Node):
|
def connect(self, peer: Node):
|
||||||
|
"""
|
||||||
|
Establish a duplex connection with a peer node.
|
||||||
|
"""
|
||||||
noise_msg = Node.__build_msg(MsgType.NOISE, bytes(self.packet_size))
|
noise_msg = Node.__build_msg(MsgType.NOISE, bytes(self.packet_size))
|
||||||
inbound_conn, outbound_conn = asyncio.Queue(), asyncio.Queue()
|
inbound_conn, outbound_conn = asyncio.Queue(), asyncio.Queue()
|
||||||
|
|
||||||
|
# Register a duplex connection for its own use
|
||||||
self.mixgossip_channel.add_conn(
|
self.mixgossip_channel.add_conn(
|
||||||
DuplexConnection(
|
DuplexConnection(
|
||||||
inbound_conn,
|
inbound_conn,
|
||||||
@ -89,6 +110,7 @@ class Node:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# Register the same duplex connection for the peer
|
||||||
peer.mixgossip_channel.add_conn(
|
peer.mixgossip_channel.add_conn(
|
||||||
DuplexConnection(
|
DuplexConnection(
|
||||||
outbound_conn,
|
outbound_conn,
|
||||||
@ -101,6 +123,11 @@ class Node:
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def send_message(self, msg: bytes):
|
async def send_message(self, msg: bytes):
|
||||||
|
"""
|
||||||
|
Build a Sphinx packet and gossip it to all connected peers.
|
||||||
|
"""
|
||||||
|
# Here, we handle the case in which a msg is split into multiple Sphinx packets.
|
||||||
|
# But, in practice, we expect a message to be small enough to fit in a single Sphinx packet.
|
||||||
for packet, _ in PacketBuilder.build_real_packets(
|
for packet, _ in PacketBuilder.build_real_packets(
|
||||||
msg,
|
msg,
|
||||||
self.global_config.membership,
|
self.global_config.membership,
|
||||||
@ -112,10 +139,16 @@ class Node:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __build_msg(flag: MsgType, data: bytes) -> bytes:
|
def __build_msg(flag: MsgType, data: bytes) -> bytes:
|
||||||
|
"""
|
||||||
|
Prepend a flag to the message, right before sending it via network channel.
|
||||||
|
"""
|
||||||
return flag.value + data
|
return flag.value + data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __parse_msg(data: bytes) -> tuple[MsgType, bytes]:
|
def __parse_msg(data: bytes) -> tuple[MsgType, bytes]:
|
||||||
|
"""
|
||||||
|
Parse the message and extract the flag.
|
||||||
|
"""
|
||||||
if len(data) < 1:
|
if len(data) < 1:
|
||||||
raise ValueError("Invalid message format")
|
raise ValueError("Invalid message format")
|
||||||
return (MsgType(data[:1]), data[1:])
|
return (MsgType(data[:1]), data[1:])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user