2022-11-30 14:28:27 +00:00
|
|
|
#!/bin/python
|
|
|
|
|
2022-12-20 10:13:54 +00:00
|
|
|
import networkx as nx
|
2023-01-23 17:04:54 +00:00
|
|
|
import logging, random
|
2022-11-30 14:28:27 +00:00
|
|
|
from datetime import datetime
|
|
|
|
from DAS.tools import *
|
2023-01-16 21:43:52 +00:00
|
|
|
from DAS.results import *
|
2022-11-30 14:28:27 +00:00
|
|
|
from DAS.observer import *
|
|
|
|
from DAS.validator import *
|
|
|
|
|
|
|
|
class Simulator:
|
2023-02-15 14:06:42 +00:00
|
|
|
"""This class implements the main DAS simulator."""
|
2022-11-30 14:28:27 +00:00
|
|
|
|
2023-01-23 17:04:54 +00:00
|
|
|
def __init__(self, shape):
|
2023-02-15 14:06:42 +00:00
|
|
|
"""It initializes the simulation with a set of parameters (shape)."""
|
2023-01-23 17:04:54 +00:00
|
|
|
self.shape = shape
|
2022-11-30 14:28:27 +00:00
|
|
|
self.format = {"entity": "Simulator"}
|
2023-01-23 17:04:54 +00:00
|
|
|
self.result = Result(self.shape)
|
2023-02-08 19:10:26 +00:00
|
|
|
self.validators = []
|
|
|
|
self.logger = []
|
|
|
|
self.logLevel = logging.INFO
|
|
|
|
self.proposerID = 0
|
|
|
|
self.glob = []
|
2022-11-30 14:28:27 +00:00
|
|
|
|
|
|
|
def initValidators(self):
|
2023-02-15 14:06:42 +00:00
|
|
|
"""It initializes all the validators in the network."""
|
2023-01-23 17:04:54 +00:00
|
|
|
self.glob = Observer(self.logger, self.shape)
|
2022-11-30 14:28:27 +00:00
|
|
|
self.glob.reset()
|
|
|
|
self.validators = []
|
2023-01-23 17:04:54 +00:00
|
|
|
rows = list(range(self.shape.blockSize)) * int(self.shape.chi*self.shape.numberValidators/self.shape.blockSize)
|
|
|
|
columns = list(range(self.shape.blockSize)) * int(self.shape.chi*self.shape.numberValidators/self.shape.blockSize)
|
|
|
|
random.shuffle(rows)
|
|
|
|
random.shuffle(columns)
|
|
|
|
for i in range(self.shape.numberValidators):
|
|
|
|
val = Validator(i, int(not i!=0), self.logger, self.shape, rows, columns)
|
2022-11-30 14:28:27 +00:00
|
|
|
if i == self.proposerID:
|
|
|
|
val.initBlock()
|
|
|
|
self.glob.setGoldenData(val.block)
|
|
|
|
else:
|
|
|
|
val.logIDs()
|
|
|
|
self.validators.append(val)
|
|
|
|
|
2023-01-23 17:04:54 +00:00
|
|
|
def initNetwork(self):
|
2023-02-15 14:06:42 +00:00
|
|
|
"""It initializes the simulated network."""
|
2023-01-23 17:04:54 +00:00
|
|
|
self.shape.netDegree = 6
|
|
|
|
rowChannels = [[] for i in range(self.shape.blockSize)]
|
|
|
|
columnChannels = [[] for i in range(self.shape.blockSize)]
|
2022-12-20 10:13:54 +00:00
|
|
|
for v in self.validators:
|
|
|
|
for id in v.rowIDs:
|
|
|
|
rowChannels[id].append(v)
|
|
|
|
for id in v.columnIDs:
|
|
|
|
columnChannels[id].append(v)
|
|
|
|
|
2023-01-23 17:04:54 +00:00
|
|
|
for id in range(self.shape.blockSize):
|
|
|
|
|
2023-02-15 01:50:03 +00:00
|
|
|
# If the number of nodes in a channel is smaller or equal to the
|
|
|
|
# requested degree, a fully connected graph is used. For n>d, a random
|
|
|
|
# d-regular graph is set up. (For n=d+1, the two are the same.)
|
|
|
|
if (len(rowChannels[id]) <= self.shape.netDegree):
|
|
|
|
self.logger.debug("Graph fully connected with degree %d !" % (len(rowChannels[id]) - 1), extra=self.format)
|
|
|
|
G = nx.complete_graph(len(rowChannels[id]))
|
|
|
|
else:
|
|
|
|
G = nx.random_regular_graph(self.shape.netDegree, len(rowChannels[id]))
|
2022-12-20 10:13:54 +00:00
|
|
|
if not nx.is_connected(G):
|
2023-01-16 21:43:52 +00:00
|
|
|
self.logger.error("Graph not connected for row %d !" % id, extra=self.format)
|
2022-12-20 10:13:54 +00:00
|
|
|
for u, v in G.edges:
|
|
|
|
val1=rowChannels[id][u]
|
|
|
|
val2=rowChannels[id][v]
|
2023-01-25 20:36:53 +00:00
|
|
|
val1.rowNeighbors[id].update({val2.ID : Neighbor(val2, self.shape.blockSize)})
|
|
|
|
val2.rowNeighbors[id].update({val1.ID : Neighbor(val1, self.shape.blockSize)})
|
2023-01-23 17:04:54 +00:00
|
|
|
|
2023-02-15 01:50:03 +00:00
|
|
|
if (len(columnChannels[id]) <= self.shape.netDegree):
|
|
|
|
self.logger.debug("Graph fully connected with degree %d !" % (len(columnChannels[id]) - 1), extra=self.format)
|
|
|
|
G = nx.complete_graph(len(columnChannels[id]))
|
|
|
|
else:
|
|
|
|
G = nx.random_regular_graph(self.shape.netDegree, len(columnChannels[id]))
|
2022-12-20 10:13:54 +00:00
|
|
|
if not nx.is_connected(G):
|
2023-01-16 21:43:52 +00:00
|
|
|
self.logger.error("Graph not connected for column %d !" % id, extra=self.format)
|
2022-12-20 10:13:54 +00:00
|
|
|
for u, v in G.edges:
|
|
|
|
val1=columnChannels[id][u]
|
|
|
|
val2=columnChannels[id][v]
|
2023-01-25 20:36:53 +00:00
|
|
|
val1.columnNeighbors[id].update({val2.ID : Neighbor(val2, self.shape.blockSize)})
|
|
|
|
val2.columnNeighbors[id].update({val1.ID : Neighbor(val1, self.shape.blockSize)})
|
2022-12-20 10:13:54 +00:00
|
|
|
|
2023-02-15 01:51:47 +00:00
|
|
|
if self.logger.isEnabledFor(logging.DEBUG):
|
2023-02-16 16:33:31 +00:00
|
|
|
for i in range(0, self.shape.numberValidators):
|
2023-02-15 01:51:47 +00:00
|
|
|
self.logger.debug("Val %d : rowN %s", i, self.validators[i].rowNeighbors, extra=self.format)
|
|
|
|
self.logger.debug("Val %d : colN %s", i, self.validators[i].columnNeighbors, extra=self.format)
|
|
|
|
|
2022-11-30 14:28:27 +00:00
|
|
|
def initLogger(self):
|
2023-02-15 14:06:42 +00:00
|
|
|
"""It initializes the logger."""
|
2022-11-30 14:28:27 +00:00
|
|
|
logger = logging.getLogger("DAS")
|
|
|
|
logger.setLevel(self.logLevel)
|
|
|
|
ch = logging.StreamHandler()
|
|
|
|
ch.setLevel(self.logLevel)
|
|
|
|
ch.setFormatter(CustomFormatter())
|
|
|
|
logger.addHandler(ch)
|
|
|
|
self.logger = logger
|
|
|
|
|
2023-01-16 21:43:52 +00:00
|
|
|
|
2023-01-23 17:04:54 +00:00
|
|
|
def resetShape(self, shape):
|
2023-02-15 14:06:42 +00:00
|
|
|
"""It resets the parameters of the simulation."""
|
2023-01-23 17:04:54 +00:00
|
|
|
self.shape = shape
|
2023-01-25 20:51:59 +00:00
|
|
|
self.result = Result(self.shape)
|
2023-01-16 21:43:52 +00:00
|
|
|
for val in self.validators:
|
2023-01-23 17:04:54 +00:00
|
|
|
val.shape.failureRate = shape.failureRate
|
|
|
|
val.shape.chi = shape.chi
|
2023-01-16 21:43:52 +00:00
|
|
|
|
2022-11-30 14:28:27 +00:00
|
|
|
|
|
|
|
def run(self):
|
2023-02-15 14:06:42 +00:00
|
|
|
"""It runs the main simulation until the block is available or it gets stucked."""
|
2022-11-30 14:28:27 +00:00
|
|
|
self.glob.checkRowsColumns(self.validators)
|
2022-12-20 10:13:54 +00:00
|
|
|
self.validators[self.proposerID].broadcastBlock()
|
|
|
|
arrived, expected = self.glob.checkStatus(self.validators)
|
|
|
|
missingSamples = expected - arrived
|
2023-01-16 21:43:52 +00:00
|
|
|
missingVector = []
|
2023-01-13 15:51:27 +00:00
|
|
|
steps = 0
|
2023-01-26 00:12:13 +00:00
|
|
|
while(True):
|
2023-01-16 21:43:52 +00:00
|
|
|
missingVector.append(missingSamples)
|
2022-11-30 14:28:27 +00:00
|
|
|
oldMissingSamples = missingSamples
|
2023-02-15 01:55:06 +00:00
|
|
|
self.logger.debug("PHASE SEND %d" % steps, extra=self.format)
|
2023-01-26 00:12:13 +00:00
|
|
|
for i in range(0,self.shape.numberValidators):
|
|
|
|
self.validators[i].sendRows()
|
|
|
|
self.validators[i].sendColumns()
|
2023-02-15 01:55:06 +00:00
|
|
|
self.logger.debug("PHASE RECEIVE %d" % steps, extra=self.format)
|
2023-01-23 17:04:54 +00:00
|
|
|
for i in range(1,self.shape.numberValidators):
|
2022-12-20 10:13:54 +00:00
|
|
|
self.validators[i].receiveRowsColumns()
|
2023-02-15 01:55:06 +00:00
|
|
|
self.logger.debug("PHASE RESTORE %d" % steps, extra=self.format)
|
2023-01-23 17:04:54 +00:00
|
|
|
for i in range(1,self.shape.numberValidators):
|
2022-12-07 14:46:45 +00:00
|
|
|
self.validators[i].restoreRows()
|
|
|
|
self.validators[i].restoreColumns()
|
2023-02-15 01:55:06 +00:00
|
|
|
self.logger.debug("PHASE LOG %d" % steps, extra=self.format)
|
2023-01-26 00:12:13 +00:00
|
|
|
for i in range(0,self.shape.numberValidators):
|
2022-11-30 14:28:27 +00:00
|
|
|
self.validators[i].logRows()
|
|
|
|
self.validators[i].logColumns()
|
2023-01-25 23:34:21 +00:00
|
|
|
self.validators[i].updateStats()
|
2022-11-30 14:28:27 +00:00
|
|
|
|
2022-12-20 10:13:54 +00:00
|
|
|
arrived, expected = self.glob.checkStatus(self.validators)
|
|
|
|
missingSamples = expected - arrived
|
2023-01-11 16:20:19 +00:00
|
|
|
missingRate = missingSamples*100/expected
|
2023-01-16 21:43:52 +00:00
|
|
|
self.logger.debug("step %d, missing %d of %d (%0.02f %%)" % (steps, missingSamples, expected, missingRate), extra=self.format)
|
2022-11-30 14:28:27 +00:00
|
|
|
if missingSamples == oldMissingSamples:
|
2023-02-15 14:06:42 +00:00
|
|
|
self.logger.debug("The block cannot be recovered, failure rate %d!" % self.shape.failureRate, extra=self.format)
|
2023-01-25 20:51:59 +00:00
|
|
|
missingVector.append(missingSamples)
|
2022-11-30 14:28:27 +00:00
|
|
|
break
|
|
|
|
elif missingSamples == 0:
|
2023-01-25 20:51:59 +00:00
|
|
|
#self.logger.info("The entire block is available at step %d, with failure rate %d !" % (steps, self.shape.failureRate), extra=self.format)
|
|
|
|
missingVector.append(missingSamples)
|
2022-11-30 14:28:27 +00:00
|
|
|
break
|
|
|
|
else:
|
2023-01-13 15:51:27 +00:00
|
|
|
steps += 1
|
2022-11-30 14:28:27 +00:00
|
|
|
|
2023-01-25 20:51:59 +00:00
|
|
|
self.result.populate(self.shape, missingVector)
|
|
|
|
return self.result
|
2022-11-30 14:28:27 +00:00
|
|
|
|