mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
300667e5f2
Previously we supported N=7 through N=10 and N=13 through N=20; with pulse we need N=11 and N=12, so fill in the gaps so we now have a matrix for everything from N=7 to N=20. Connections are generated from utils/generate-quorum-matrix.py using that same criteria as the existing connection matrices (that is, finding a set of connections to ensure that every node is connected to every other node twice with direct or two-hop connections). The missing added connection details are as follows: ┌────┬───────────────────────────────┬─────────────┬────────────────────┐ │ i │ Connections (1 = out, x = in) │ ≤2 paths │ Connectivity: │ ├────┼───────────────────────────────┼─────────────┼────────────────────┤ │ 0 │ -100xx1x001 │ -3433343332 │ 6 (= 3 out + 3 in) │ │ 1 │ x-10x0011x0 │ 3-333344322 │ 6 (= 3 out + 3 in) │ │ 2 │ 0x-11x01000 │ 43-33232322 │ 5 (= 3 out + 2 in) │ │ 3 │ 00x-110010x │ 333-2322223 │ 5 (= 3 out + 2 in) │ │ 4 │ 11xx-010000 │ 3332-424222 │ 5 (= 3 out + 2 in) │ │ 5 │ 101x0-100xx │ 33234-33323 │ 6 (= 3 out + 3 in) │ │ 6 │ x000xx-1010 │ 443223-2222 │ 5 (= 2 out + 3 in) │ │ 7 │ 1xx000x-100 │ 3422432-232 │ 5 (= 2 out + 3 in) │ │ 8 │ 0x0x000x-11 │ 33322322-22 │ 5 (= 2 out + 3 in) │ │ 9 │ 010001x0x-0 │ 322222232-2 │ 4 (= 2 out + 2 in) │ │ 10 │ x0010100x0- │ 2223232222- │ 4 (= 2 out + 2 in) │ └────┴───────────────────────────────┴─────────────┴────────────────────┘ ┌────┬───────────────────────────────┬──────────────┬────────────────────┐ │ i │ Connections (1 = out, x = in) │ ≤2 paths │ Connectivity: │ ├────┼───────────────────────────────┼──────────────┼────────────────────┤ │ 0 │ -10xx0x00010 │ -23332332222 │ 5 (= 2 out + 3 in) │ │ 1 │ x-110x001000 │ 2-3422232222 │ 5 (= 3 out + 2 in) │ │ 2 │ 0x-10100001x │ 33-222233222 │ 5 (= 3 out + 2 in) │ │ 3 │ 1xx-1001x000 │ 342-23323233 │ 6 (= 3 out + 3 in) │ │ 4 │ 100x-010010x │ 3222-2323332 │ 5 (= 3 out + 2 in) │ │ 5 │ 01x00-110x00 │ 22232-323232 │ 5 (= 3 out + 2 in) │ │ 6 │ 1000xx-101x0 │ 322333-32333 │ 6 (= 3 out + 3 in) │ │ 7 │ 000x0xx-1010 │ 3332223-2322 │ 5 (= 2 out + 3 in) │ │ 8 │ 0x01000x-101 │ 22333322-222 │ 5 (= 3 out + 2 in) │ │ 9 │ 0000x1x0x-01 │ 222232332-23 │ 5 (= 2 out + 3 in) │ │ 10 │ x0x0001x00-1 │ 2223333222-2 │ 5 (= 2 out + 3 in) │ │ 11 │ 00101000xxx- │ 22232232232- │ 5 (= 2 out + 3 in) │ └────┴───────────────────────────────┴──────────────┴────────────────────┘
173 lines
6.1 KiB
Python
Executable file
173 lines
6.1 KiB
Python
Executable file
#!/usr/bin/python3
|
|
|
|
# This script generates the quorum interconnection matrices for different quorum sizes stored in
|
|
# src/quorumnet/conn_matrix.h and used to establish a quorum p2p mesh that establishes a set of
|
|
# connections with good partial connectivity using a minimal number of outgoing connections.
|
|
#
|
|
# It works by looking at the number of different paths of 2 hops or less (1 hop meaning a direct
|
|
# connection, 2 meaning a connection that passes through 1 intermediate node) and looks for
|
|
# connections between least-connected nodes that increases the most two-hop minimum paths. It then
|
|
# establishes this "best" connection, then restarts, continuing until it has achieved a minimum
|
|
# 2-hop connectivity from every node to every other node.
|
|
#
|
|
# This isn't any guarantee that this procedure generates the absolute best connectivity mesh, but it
|
|
# appears to do fairly well.
|
|
|
|
|
|
# If you want to see every path that gets added and the before/after two-hop-connectivity graph
|
|
# after each addition, set this to true:
|
|
TRACE = False
|
|
TRACE = True
|
|
|
|
# N sizes to calculate for. The default calculates for all possible quorums that are capable of
|
|
# achieving supermajority for blink and obligations quorums (10, 7 required) and checkpoint quorums
|
|
# (20, 13 required)
|
|
N = range(7, 21)
|
|
|
|
# This defines how much 2-path connectivity we require for different values of N
|
|
def min_connections(n):
|
|
return 4 if n <= 10 else 2
|
|
|
|
# Some stuff you might need: apt install python3-numpy python3-terminaltables
|
|
import numpy as np
|
|
from terminaltables import SingleTable
|
|
|
|
nodes = None
|
|
conns = None
|
|
|
|
def count_paths_within_two(i, j):
|
|
if i > j:
|
|
i, j = j, i
|
|
paths = 0
|
|
neighbours = []
|
|
for r in range(0, j):
|
|
if conns[r, j] or conns[j, r] > 0:
|
|
neighbours.append(r)
|
|
for c in range(j+1, nodes):
|
|
if conns[j, c] or conns[c, j] > 0:
|
|
neighbours.append(c)
|
|
for n in neighbours:
|
|
if n == i or conns[n, i] > 0 or conns[i, n] > 0:
|
|
paths += 1
|
|
return paths
|
|
|
|
|
|
def within_two_matrix():
|
|
z = np.zeros([nodes, nodes], dtype=int)
|
|
for i in range(nodes):
|
|
for j in range(nodes):
|
|
if i == j:
|
|
continue
|
|
z[i, j] = count_paths_within_two(i, j)
|
|
return z
|
|
|
|
|
|
def print_conns():
|
|
table_data = [["i", "Connections (1 = out, x = in)", "≤2 paths", "Connectivity:"], ["\n".join(str(x) for x in range(nodes)), "", "", ""]]
|
|
for r in range(nodes):
|
|
if table_data[1][1]:
|
|
table_data[1][1] += "\n"
|
|
table_data[1][1] += "".join('-' if r == c else 'x' if conns[c,r] else str(conns[r,c]) for c in range(nodes))
|
|
|
|
z = within_two_matrix()
|
|
for r in range(nodes):
|
|
if table_data[1][2]:
|
|
table_data[1][2] += "\n"
|
|
table_data[1][2] += "".join('-' if r == c else str(z[r,c]) for c in range(nodes))
|
|
|
|
for i in range(nodes):
|
|
myouts, myins = 0, 0
|
|
for j in range(nodes):
|
|
if conns[i, j]:
|
|
myouts += 1
|
|
elif conns[j, i]:
|
|
myins += 1
|
|
if table_data[1][3]:
|
|
table_data[1][3] += "\n"
|
|
table_data[1][3] += "{} (= {} out + {} in)".format(myouts + myins, myouts, myins)
|
|
|
|
print(SingleTable(table_data).table)
|
|
|
|
|
|
def min_within_two():
|
|
m = None
|
|
for i in range(nodes):
|
|
for j in range(i+1, nodes):
|
|
c = count_paths_within_two(i, j)
|
|
if m is None or c < m:
|
|
m = c
|
|
return m
|
|
|
|
|
|
def count_not_within_two(min_paths):
|
|
c = 0
|
|
for i in range(nodes):
|
|
for j in range(i+1, nodes):
|
|
if count_paths_within_two(i, j) <= min_paths:
|
|
c += 1
|
|
return c
|
|
|
|
|
|
def highlight(s, hl, code="\033[32m"):
|
|
return "{}{}\033[0m".format(code, s) if hl else "{}".format(s)
|
|
|
|
|
|
cpp = ""
|
|
|
|
|
|
for n in N:
|
|
nodes = n
|
|
conns = np.zeros([nodes, nodes], dtype=int)
|
|
|
|
target = min_connections(nodes)
|
|
|
|
min_paths = 0
|
|
last_min_paths = 0
|
|
while min_paths < target:
|
|
best = (nodes + nodes, count_not_within_two(min_paths))
|
|
best_ij = (0, 0)
|
|
for i in range(nodes):
|
|
outgoing_conns = sum(conns[i,j] for j in range(nodes)) + 1
|
|
for j in range(nodes):
|
|
incoming_conns = sum(conns[k,j] for k in range(nodes)) + 1
|
|
if i == j or conns[i, j] or conns[j, i]:
|
|
continue
|
|
conns[i, j] = 1
|
|
c = (outgoing_conns + incoming_conns, count_not_within_two(min_paths))
|
|
if c < best:
|
|
best_ij = (i, j)
|
|
best = c
|
|
best_conns = outgoing_conns
|
|
conns[i, j] = 0
|
|
if TRACE:
|
|
before = within_two_matrix()
|
|
conns[best_ij[0], best_ij[1]] = 1
|
|
if TRACE:
|
|
print("Chose connection [{},{}]".format(*best_ij))
|
|
after = within_two_matrix()
|
|
for r in range(nodes):
|
|
print("".join(
|
|
highlight('-' if r == c else '#' if conns[c,r] and conns[r,c] else 'x' if conns[c,r] else conns[r,c], (r,c)==best_ij) for c in range(nodes)
|
|
), end='')
|
|
print(" : " if r == nodes // 2 else " ", end='')
|
|
print("".join(highlight('-' if r == c else before[r,c], after[r,c] > before[r,c], "\033[33m") for c in range(nodes)), end='')
|
|
print(" => " if r == nodes // 2 else " ", end='')
|
|
print("".join(highlight('-' if r == c else after[r,c], after[r,c] > before[r,c]) for c in range(nodes)))
|
|
|
|
|
|
min_paths = min_within_two()
|
|
|
|
if min_paths > last_min_paths:
|
|
print("\n\n\n\n====================================================\nConstructed {}-min-two-hop-paths (N={})\n====================================================\n".format(
|
|
min_paths, nodes))
|
|
print_conns()
|
|
last_min_paths = min_paths
|
|
print("\n\n\n\n")
|
|
|
|
cpp += "template<> constexpr std::array<bool, {N}*{N}> quorum_conn_matrix<{N}>{{{{\n".format(N=n);
|
|
for r in range(nodes):
|
|
cpp += " " + ",".join(str(conns[r,c]) for c in range(nodes)) + ",\n"
|
|
cpp += "}};\n\n"
|
|
|
|
print("C++ code for quorumnet/conn_matrix.h:\n\n\n")
|
|
print(cpp)
|