Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Mixnet: Update spec for NomMix #98

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9f58a72
mixnet v2
youngjoon-lee Jun 26, 2024
28b5055
add comment
youngjoon-lee Jun 26, 2024
3585d3c
separate global config
youngjoon-lee Jun 27, 2024
9cd601c
use public key for NodeInfo instead of private key
youngjoon-lee Jun 27, 2024
ed9b0ab
add peering_degree
youngjoon-lee Jun 28, 2024
d3476d3
add msg cache to not handle the same msg twice in gossipsub
youngjoon-lee Jun 28, 2024
235c703
fix
youngjoon-lee Jun 28, 2024
3a70343
fix
youngjoon-lee Jun 28, 2024
4aa1ee0
sphinx serde
youngjoon-lee Jun 28, 2024
d3e8b02
update comment
youngjoon-lee Jul 3, 2024
36eeab0
fix noise size
youngjoon-lee Jul 8, 2024
e995c40
refactor GossipConfig
youngjoon-lee Jul 9, 2024
0772bf1
refactor connection and gossip
youngjoon-lee Jul 10, 2024
adfed3a
use mix_path_length to make Sphinx packets
youngjoon-lee Jul 10, 2024
848b3f4
add comments
youngjoon-lee Jul 10, 2024
8712157
Merge branch 'master' into mixnet-v2
youngjoon-lee Jul 10, 2024
a25b394
fix comment
youngjoon-lee Jul 11, 2024
78d4ab9
add comment for packet_size
youngjoon-lee Jul 11, 2024
953b2d6
rename gossip to nomssip
youngjoon-lee Jul 11, 2024
ab9943d
refactor nomssip vs node encapsulation
youngjoon-lee Jul 11, 2024
ad669ab
remove message fragments
youngjoon-lee Jul 11, 2024
e1abc44
do not declare class variables
youngjoon-lee Jul 11, 2024
e558ba4
use choices
youngjoon-lee Jul 11, 2024
e964721
remove incentivization comment
youngjoon-lee Jul 11, 2024
5b41c3d
Update mixnet/nomssip.py
youngjoon-lee Jul 11, 2024
693cf63
revert: use slice because the type of enum values are bytes
youngjoon-lee Jul 11, 2024
9ed5fd5
define FlaggedPacket
youngjoon-lee Jul 11, 2024
c360a4a
add max_message_size global param
youngjoon-lee Jul 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions mixnet/README.md

This file was deleted.

13 changes: 0 additions & 13 deletions mixnet/bls.py

This file was deleted.

118 changes: 0 additions & 118 deletions mixnet/client.py

This file was deleted.

119 changes: 39 additions & 80 deletions mixnet/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,110 +2,69 @@

import random
from dataclasses import dataclass
from typing import List, TypeAlias
from typing import List

from cryptography.hazmat.primitives.asymmetric.x25519 import (
X25519PrivateKey,
X25519PublicKey,
)
from pysphinx.node import Node

from mixnet.bls import BlsPrivateKey, BlsPublicKey
from mixnet.fisheryates import FisherYates
from pysphinx.sphinx import Node as SphinxNode


@dataclass
class MixnetConfig:
topology_config: MixnetTopologyConfig
mixclient_config: MixClientConfig
mixnode_config: MixNodeConfig

class GlobalConfig:
"""
Global parameters used across all nodes in the network
"""

@dataclass
class MixnetTopologyConfig:
mixnode_candidates: List[MixNodeInfo]
size: MixnetTopologySize
entropy: bytes
membership: MixMembership
transmission_rate_per_sec: int # Global Transmission Rate
# TODO: use these two to make the size of Sphinx packet constant
max_message_size: int
max_mix_path_length: int


@dataclass
class MixClientConfig:
emission_rate_per_min: int # Poisson rate parameter: lambda
redundancy: int
topology: MixnetTopology
class NodeConfig:
youngjoon-lee marked this conversation as resolved.
Show resolved Hide resolved
"""
Node-specific parameters
"""

private_key: X25519PrivateKey
mix_path_length: int
nomssip: NomssipConfig


@dataclass
class MixNodeConfig:
encryption_private_key: X25519PrivateKey
delay_rate_per_min: int # Poisson rate parameter: mu
class NomssipConfig:
# The target number of peers a node should maintain in its p2p network
peering_degree: int


@dataclass
class MixnetTopology:
# In production, this can be a 1-D array, which is accessible by indexes.
# Here, we use a 2-D array for readability.
layers: List[List[MixNodeInfo]]

def __init__(
self,
config: MixnetTopologyConfig,
) -> None:
"""
Build a new topology deterministically using an entropy and a given set of candidates.
"""
shuffled = FisherYates.shuffle(config.mixnode_candidates, config.entropy)
sampled = shuffled[: config.size.num_total_mixnodes()]
class MixMembership:
"""
A list of public information of nodes in the network.
We assume that this list is known to all nodes in the network.
"""

layers = []
for layer_id in range(config.size.num_layers):
start = layer_id * config.size.num_mixnodes_per_layer
layer = sampled[start : start + config.size.num_mixnodes_per_layer]
layers.append(layer)
self.layers = layers

def generate_route(self, mix_destination: MixNodeInfo) -> list[MixNodeInfo]:
"""
Generate a mix route for a Sphinx packet.
The pre-selected mix_destination is used as a last mix node in the route,
so that associated packets can be merged together into a original message.
"""
route = [random.choice(layer) for layer in self.layers[:-1]]
route.append(mix_destination)
return route
nodes: List[NodeInfo]

def choose_mix_destination(self) -> MixNodeInfo:
def generate_route(self, length: int) -> list[NodeInfo]:
"""
Choose a mix node from the last mix layer as a mix destination
that will reconstruct a message from Sphinx packets.
Choose `length` nodes with replacement as a mix route.
"""
return random.choice(self.layers[-1])


@dataclass
class MixnetTopologySize:
num_layers: int
num_mixnodes_per_layer: int

def num_total_mixnodes(self) -> int:
return self.num_layers * self.num_mixnodes_per_layer


# 32-byte that represents an IP address and a port of a mix node.
NodeAddress: TypeAlias = bytes
return random.choices(self.nodes, k=length)


@dataclass
class MixNodeInfo:
identity_private_key: BlsPrivateKey
encryption_private_key: X25519PrivateKey
addr: NodeAddress

def identity_public_key(self) -> BlsPublicKey:
return self.identity_private_key.get_g1()
class NodeInfo:
"""
Public information of a node to be shared to all nodes in the network
"""

def encryption_public_key(self) -> X25519PublicKey:
return self.encryption_private_key.public_key()
public_key: X25519PublicKey

def sphinx_node(self) -> Node:
return Node(self.encryption_private_key, self.addr)
def sphinx_node(self) -> SphinxNode:
dummy_node_addr = bytes(32)
return SphinxNode(self.public_key, dummy_node_addr)
52 changes: 52 additions & 0 deletions mixnet/connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from __future__ import annotations

import asyncio

NetworkPacketQueue = asyncio.Queue[bytes]
SimplexConnection = NetworkPacketQueue


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).
"""

def __init__(self, inbound: SimplexConnection, outbound: MixSimplexConnection):
self.inbound = inbound
self.outbound = outbound

async def recv(self) -> bytes:
return await self.inbound.get()

async def send(self, packet: bytes):
await self.outbound.send(packet)


class MixSimplexConnection:
"""
Wraps a SimplexConnection to add a transmission rate and noise to the connection.
"""

def __init__(
self, conn: SimplexConnection, transmission_rate_per_sec: int, noise_msg: bytes
):
self.queue = asyncio.Queue()
self.conn = conn
self.transmission_rate_per_sec = transmission_rate_per_sec
self.noise_msg = noise_msg
self.task = asyncio.create_task(self.__run())

async def __run(self):
while True:
await asyncio.sleep(1 / self.transmission_rate_per_sec)
# TODO: temporal mixing
if self.queue.empty():
# To guarantee GTR, send noise if there is no message to send
msg = self.noise_msg
else:
msg = self.queue.get_nowait()
await self.conn.put(msg)

async def send(self, msg: bytes):
await self.queue.put(msg)
21 changes: 0 additions & 21 deletions mixnet/fisheryates.py

This file was deleted.

Loading