From 60a5dbfe26f2ec3dd59cef0e5b794d3d9df994c5 Mon Sep 17 00:00:00 2001 From: danielsanchezq Date: Fri, 28 Jun 2024 17:54:56 +0200 Subject: [PATCH] First experiments --- da/subnetwork_assignment.py | 31 +++++++++++++++++ da/test_subnetwork_assignment.py | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 da/subnetwork_assignment.py create mode 100644 da/test_subnetwork_assignment.py diff --git a/da/subnetwork_assignment.py b/da/subnetwork_assignment.py new file mode 100644 index 0000000..c58326d --- /dev/null +++ b/da/subnetwork_assignment.py @@ -0,0 +1,31 @@ +from itertools import chain +from math import ceil +from typing import Generator, Set, TypeAlias +from hashlib import blake2b +import random +from scipy.stats.qmc import Halton +Id: TypeAlias = bytes + + +def _lazy_recursive_hash(_id: Id, hasher=lambda x: blake2b(x).digest()) -> Generator[Id, None, None]: + while True: + _id = hasher(_id) + yield _id + + +def generate_distribution_set(_id: Id, set_size: int, modulus=4096, hasher=lambda x: blake2b(x).digest()) -> Set[int]: + result_set = set() + ids = _lazy_recursive_hash(_id) + while len(result_set) < set_size: + result_set.add(int.from_bytes(next(ids)) % modulus) + return result_set + + +def generate_distribution_set_v3(_id: Id, set_size: int, replication_factor: int, modulus=4096) -> Set[int]: + halton = Halton(set_size*replication_factor, seed=int.from_bytes(_id)) + return set(chain.from_iterable(halton.integers(l_bounds=0, u_bounds=modulus, n=set_size*replication_factor))) + + +def calculate_minimum_membership(network_size: int, number_of_nodes: int, replication_factor: int) -> int: + return ceil(network_size / number_of_nodes) * replication_factor + diff --git a/da/test_subnetwork_assignment.py b/da/test_subnetwork_assignment.py new file mode 100644 index 0000000..c8b3252 --- /dev/null +++ b/da/test_subnetwork_assignment.py @@ -0,0 +1,59 @@ +from math import ceil, floor +from typing import List +from unittest import TestCase +from .subnetwork_assignment import ( + generate_distribution_set, + generate_distribution_set_v3, + calculate_minimum_membership +) +from hashlib import blake2b, sha512 +from uuid import uuid4 +from itertools import chain + + +SUBNETWORK_SIZE: int = 4096 +NODES_OVER_NETWORK_SIZE: List[float] = [0.1, 0.25, 0.5, 0.75, 1.0, 2.0, 10.0] +HASHERS = [lambda x: sha512(x).digest()] + + +class TestSubnetworkAssignment(TestCase): + def test_no_subnetwork_empty(self): + for hasher in HASHERS: + for nodes_factor in reversed(NODES_OVER_NETWORK_SIZE): + total_nodes = int(SUBNETWORK_SIZE * nodes_factor) + nodes = [uuid4() for _ in range(total_nodes)] + replication_factor = ceil(5/nodes_factor) + minimum_membership = calculate_minimum_membership(SUBNETWORK_SIZE, total_nodes, replication_factor) + subsets = set( + chain.from_iterable( + generate_distribution_set( + blake2b(_id.bytes).digest(), + minimum_membership, + SUBNETWORK_SIZE, + hasher=hasher + ) + for _id in nodes + ) + ) + print(f"Total nodes: {total_nodes}") + self.assertGreater(len(subsets), floor(SUBNETWORK_SIZE*0.99)) + + def test_no_subnetwork_v3_empty(self): + for nodes_factor in reversed(NODES_OVER_NETWORK_SIZE): + total_nodes = int(SUBNETWORK_SIZE * nodes_factor) + nodes = [uuid4() for _ in range(total_nodes)] + replication_factor = ceil(3/nodes_factor) + minimum_membership = calculate_minimum_membership(SUBNETWORK_SIZE, total_nodes, 1) + subsets = set( + chain.from_iterable( + generate_distribution_set_v3( + blake2b(_id.bytes).digest(), + minimum_membership, + replication_factor, + SUBNETWORK_SIZE + ) + for _id in nodes + ) + ) + print(f"Total nodes: {total_nodes}") + self.assertGreater(len(subsets), floor(SUBNETWORK_SIZE*0.99))