2024-02-05 06:47:36 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2024-02-05 08:04:02 +00:00
|
|
|
from dataclasses import dataclass
|
2024-02-05 06:47:36 +00:00
|
|
|
from typing import List
|
|
|
|
|
2024-02-05 08:04:02 +00:00
|
|
|
from mixnet.config import MixnetConfig, MixnetTopology, MixNodeInfo
|
2024-02-05 06:47:36 +00:00
|
|
|
from mixnet.fisheryates import FisherYates
|
2024-02-05 08:04:02 +00:00
|
|
|
from mixnet.mixnet import Mixnet
|
2024-02-05 06:47:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Robustness:
|
|
|
|
"""
|
|
|
|
A robustness layer is placed on top of a mixnet layer and a consensus layer,
|
|
|
|
to separate their responsibilities and minimize dependencies between them.
|
|
|
|
|
|
|
|
For v1, the role of robustness layer is building a new mixnet topology
|
|
|
|
and injecting it to the mixnet layer,
|
|
|
|
whenever a new entropy is received from the consensus layer.
|
|
|
|
A static list of nodes is used for building topologies deterministically.
|
|
|
|
This can be changed in later versions.
|
|
|
|
|
|
|
|
In later versions, the robustness layer will have more responsibilities.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
2024-02-05 08:04:02 +00:00
|
|
|
config: RobustnessConfig,
|
2024-02-05 06:47:36 +00:00
|
|
|
mixnet: Mixnet,
|
|
|
|
) -> None:
|
2024-02-05 08:04:02 +00:00
|
|
|
self.__config = config
|
|
|
|
self.__mixnet = mixnet
|
2024-02-05 06:47:36 +00:00
|
|
|
|
|
|
|
def set_entropy(self, entropy: bytes) -> None:
|
|
|
|
"""
|
|
|
|
Given a entropy received, build a new topology and send it to mixnet.
|
2024-02-05 08:04:02 +00:00
|
|
|
In v1, this doesn't change any mixnet config except topology.
|
2024-02-05 06:47:36 +00:00
|
|
|
|
|
|
|
In real implementations, this method should be a long-running task, consuming entropy periodically.
|
|
|
|
Here in the spec, this method has been simplified as a setter, assuming the single-thread test environment.
|
|
|
|
"""
|
2024-02-05 08:04:02 +00:00
|
|
|
self.__config.mixnet.mixnet_layer_config.topology = self.build_topology(
|
|
|
|
self.__config.mixnet.mixnode_candidates,
|
|
|
|
self.__config.mixnet.topology_size,
|
|
|
|
entropy,
|
|
|
|
)
|
|
|
|
self.__mixnet.set_config(self.__config.mixnet.mixnet_layer_config)
|
2024-02-05 06:47:36 +00:00
|
|
|
|
2024-02-05 08:04:02 +00:00
|
|
|
@staticmethod
|
|
|
|
def build_topology(
|
|
|
|
mixnode_candidates: List[MixNodeInfo],
|
|
|
|
mixnet_topology_size: MixnetTopologySize,
|
|
|
|
entropy: bytes,
|
|
|
|
) -> MixnetTopology:
|
2024-02-05 06:47:36 +00:00
|
|
|
"""
|
|
|
|
Build a new topology deterministically using an entropy and a given set of candidates.
|
|
|
|
"""
|
2024-02-05 08:04:02 +00:00
|
|
|
shuffled = FisherYates.shuffle(mixnode_candidates, entropy)
|
|
|
|
sampled = shuffled[: mixnet_topology_size.num_total_mixnodes()]
|
2024-02-05 06:47:36 +00:00
|
|
|
|
|
|
|
layers = []
|
2024-02-05 08:04:02 +00:00
|
|
|
for layer_id in range(mixnet_topology_size.num_layers):
|
|
|
|
start = layer_id * mixnet_topology_size.num_mixnodes_per_layer
|
|
|
|
layer = sampled[start : start + mixnet_topology_size.num_mixnodes_per_layer]
|
2024-02-05 06:47:36 +00:00
|
|
|
layers.append(layer)
|
|
|
|
return MixnetTopology(layers)
|
2024-02-05 08:04:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class RobustnessConfig:
|
|
|
|
"""In v1, the robustness layer manages configs only for the mixnet layer."""
|
|
|
|
|
|
|
|
mixnet: RobustnessMixnetConfig
|
|
|
|
|
|
|
|
|
|
|
|
class RobustnessMixnetConfig:
|
|
|
|
"""
|
|
|
|
Configurations for the mixnet layer
|
|
|
|
These configurations are meant to be changed over time according to other parameters from other layers (e.g. consensus).
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
mixnode_candidates: List[MixNodeInfo],
|
|
|
|
mixnet_topology_size: MixnetTopologySize,
|
|
|
|
mixnet_layer_config: MixnetConfig,
|
|
|
|
) -> None:
|
|
|
|
assert mixnet_topology_size.num_total_mixnodes() <= len(mixnode_candidates)
|
|
|
|
self.mixnode_candidates = mixnode_candidates
|
|
|
|
self.topology_size = mixnet_topology_size
|
|
|
|
# A config to be injected to the mixnet layer whenever it is updated
|
|
|
|
self.mixnet_layer_config = mixnet_layer_config
|
|
|
|
|
|
|
|
|
|
|
|
@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
|