Merge f5f2131a45df4089b75ff5834bf39aacbf923b27 into f36c3c85ba31ed0fd27c3650794a511ad994a661

This commit is contained in:
Sudipta Basak 2024-06-25 13:16:38 +02:00 committed by GitHub
commit bf97928e5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 522 additions and 202 deletions

View File

@ -32,11 +32,13 @@ class Neighbor:
class Validator: class Validator:
i = 0
def __init__(self, rowIDs, columnIDs): def __init__(self, rowIDs, columnIDs):
self.rowIDs = rowIDs self.rowIDs = rowIDs
self.columnIDs = columnIDs self.columnIDs = columnIDs
def initValidator(nbRows, custodyRows, nbCols, custodyCols): def initValidator(nbRows, custodyRows, nbCols, custodyCols):
random.seed(10 + Validator.i); Validator.i += 1
rowIDs = set(random.sample(range(nbRows), custodyRows)) rowIDs = set(random.sample(range(nbRows), custodyRows))
columnIDs = set(random.sample(range(nbCols), custodyCols)) columnIDs = set(random.sample(range(nbCols), custodyCols))
return Validator(rowIDs, columnIDs) return Validator(rowIDs, columnIDs)
@ -48,7 +50,7 @@ class Node:
"""It returns the node ID.""" """It returns the node ID."""
return str(self.ID) return str(self.ID)
def __init__(self, ID, amIproposer, amImalicious, logger, shape, config, def __init__(self, ID, amIproposer, nodeClass, amImalicious, logger, shape, config,
validators, rows = set(), columns = set()): validators, rows = set(), columns = set()):
"""It initializes the node, and eventual validators, following the simulation configuration in shape and config. """It initializes the node, and eventual validators, following the simulation configuration in shape and config.
@ -82,7 +84,7 @@ class Node:
self.rowIDs = range(shape.nbRows) self.rowIDs = range(shape.nbRows)
self.columnIDs = range(shape.nbCols) self.columnIDs = range(shape.nbCols)
else: else:
self.nodeClass = 1 if (self.ID <= shape.numberNodes * shape.class1ratio) else 2 self.nodeClass = nodeClass
self.vpn = len(validators) #TODO: needed by old code, change to fn self.vpn = len(validators) #TODO: needed by old code, change to fn
self.rowIDs = set(rows) self.rowIDs = set(rows)
@ -96,13 +98,13 @@ class Node:
self.logger.warning("Row custody (*vpn) larger than number of rows!", extra=self.format) self.logger.warning("Row custody (*vpn) larger than number of rows!", extra=self.format)
self.rowIDs = range(self.shape.nbRows) self.rowIDs = range(self.shape.nbRows)
else: else:
self.rowIDs = set(random.sample(range(self.shape.nbRows), self.vpn*self.shape.custodyRows)) self.rowIDs = set(random.sample(range(self.shape.nbRows), max(self.vpn*self.shape.custodyRows, self.shape.minCustodyRows)))
if (self.vpn * self.shape.custodyCols) > self.shape.nbCols: if (self.vpn * self.shape.custodyCols) > self.shape.nbCols:
self.logger.warning("Column custody (*vpn) larger than number of columns!", extra=self.format) self.logger.warning("Column custody (*vpn) larger than number of columns!", extra=self.format)
self.columnIDs = range(self.shape.nbCols) self.columnIDs = range(self.shape.nbCols)
else: else:
self.columnIDs = set(random.sample(range(self.shape.nbCols), self.vpn*self.shape.custodyCols)) self.columnIDs = set(random.sample(range(self.shape.nbCols), max(self.vpn*self.shape.custodyCols, self.shape.minCustodyCols)))
self.rowNeighbors = collections.defaultdict(dict) self.rowNeighbors = collections.defaultdict(dict)
self.columnNeighbors = collections.defaultdict(dict) self.columnNeighbors = collections.defaultdict(dict)
@ -120,10 +122,8 @@ class Node:
# 1 Mbps ~= 1e6 mbps * 0.050 s / (560*8) bits ~= 11 segments/timestep # 1 Mbps ~= 1e6 mbps * 0.050 s / (560*8) bits ~= 11 segments/timestep
if self.amIproposer: if self.amIproposer:
self.bwUplink = shape.bwUplinkProd self.bwUplink = shape.bwUplinkProd
elif self.nodeClass == 1:
self.bwUplink = shape.bwUplink1
else: else:
self.bwUplink = shape.bwUplink2 self.bwUplink = shape.nodeTypes["classes"][self.nodeClass]["def"]['bwUplinks']
self.bwUplink *= 1e3 / 8 * config.stepDuration / config.segmentSize self.bwUplink *= 1e3 / 8 * config.stepDuration / config.segmentSize
self.repairOnTheFly = config.evalConf(self, config.repairOnTheFly, shape) self.repairOnTheFly = config.evalConf(self, config.repairOnTheFly, shape)

View File

@ -83,8 +83,8 @@ class Observer:
sampleProgress = arrived / expected sampleProgress = arrived / expected
nodeProgress = ready / (len(validators)-1) nodeProgress = ready / (len(validators)-1)
validatorCnt = sum([v.vpn for v in validators[1:]]) validatorCnt = sum([v.vpn for v in validators[1:]])
validatorAllProgress = validatedall / validatorCnt validatorAllProgress = (validatedall / validatorCnt) if validatorCnt != 0 else 1
validatorProgress = validated / validatorCnt validatorProgress = (validated / validatorCnt) if validatorCnt != 0 else 1
return missingSamples, sampleProgress, nodeProgress, validatorAllProgress, validatorProgress return missingSamples, sampleProgress, nodeProgress, validatorAllProgress, validatorProgress
@ -96,7 +96,7 @@ class Observer:
return np.mean(l) if l else np.NaN return np.mean(l) if l else np.NaN
trafficStats = {} trafficStats = {}
for cl in range(0,3): for cl in self.config.nodeClasses:
Tx = [v.statsTxInSlot for v in validators if v.nodeClass == cl] Tx = [v.statsTxInSlot for v in validators if v.nodeClass == cl]
Rx = [v.statsRxInSlot for v in validators if v.nodeClass == cl] Rx = [v.statsRxInSlot for v in validators if v.nodeClass == cl]
RxDup = [v.statsRxDupInSlot for v in validators if v.nodeClass == cl] RxDup = [v.statsRxDupInSlot for v in validators if v.nodeClass == cl]

View File

@ -24,7 +24,6 @@ class Result:
self.restoreColumnCount = [0] * shape.numberNodes self.restoreColumnCount = [0] * shape.numberNodes
self.repairedSampleCount = [0] * shape.numberNodes self.repairedSampleCount = [0] * shape.numberNodes
self.numberNodes = shape.numberNodes self.numberNodes = shape.numberNodes
self.class1ratio = shape.class1ratio
def copyValidators(self, validators): def copyValidators(self, validators):
"""Copy information from simulator.validators to result.""" """Copy information from simulator.validators to result."""

View File

@ -3,7 +3,7 @@
class Shape: class Shape:
"""This class represents a set of parameters for a specific simulation.""" """This class represents a set of parameters for a specific simulation."""
def __init__(self, nbCols, nbColsK, nbRows, nbRowsK, def __init__(self, nbCols, nbColsK, nbRows, nbRowsK,
numberNodes, failureModel, failureRate, maliciousNodes, class1ratio, custodyRows, custodyCols, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): numberNodes, failureModel, failureRate, maliciousNodes, custodyRows, custodyCols, minCustodyRows, minCustodyCols, netDegree, bwUplinkProd, run, nodeTypes):
"""Initializes the shape with the parameters passed in argument.""" """Initializes the shape with the parameters passed in argument."""
self.run = run self.run = run
self.numberNodes = numberNodes self.numberNodes = numberNodes
@ -15,14 +15,13 @@ class Shape:
self.failureRate = failureRate self.failureRate = failureRate
self.maliciousNodes = maliciousNodes self.maliciousNodes = maliciousNodes
self.netDegree = netDegree self.netDegree = netDegree
self.class1ratio = class1ratio
self.custodyRows = custodyRows self.custodyRows = custodyRows
self.custodyCols = custodyCols self.custodyCols = custodyCols
self.vpn1 = vpn1 self.minCustodyRows = minCustodyRows
self.vpn2 = vpn2 self.minCustodyCols = minCustodyCols
self.bwUplinkProd = bwUplinkProd self.bwUplinkProd = bwUplinkProd
self.bwUplink1 = bwUplink1 self.nodeTypes = nodeTypes
self.bwUplink2 = bwUplink2 self.nodeClasses = [0] + [_k for _k in nodeTypes["classes"].keys()]
self.randomSeed = "" self.randomSeed = ""
def __repr__(self): def __repr__(self):
@ -35,17 +34,15 @@ class Shape:
shastr += "-nn-"+str(self.numberNodes) shastr += "-nn-"+str(self.numberNodes)
shastr += "-fm-"+str(self.failureModel) shastr += "-fm-"+str(self.failureModel)
shastr += "-fr-"+str(self.failureRate) shastr += "-fr-"+str(self.failureRate)
shastr += "-c1r-"+str(self.class1ratio)
shastr += "-cusr-"+str(self.custodyRows) shastr += "-cusr-"+str(self.custodyRows)
shastr += "-cusc-"+str(self.custodyCols) shastr += "-cusc-"+str(self.custodyCols)
shastr += "-vpn1-"+str(self.vpn1) shastr += "-mcusr-"+str(self.minCustodyRows)
shastr += "-vpn2-"+str(self.vpn2) shastr += "-mcusc-"+str(self.minCustodyCols)
shastr += "-bwupprod-"+str(self.bwUplinkProd) shastr += "-bwupprod-"+str(self.bwUplinkProd)
shastr += "-bwup1-"+str(self.bwUplink1)
shastr += "-bwup2-"+str(self.bwUplink2)
shastr += "-nd-"+str(self.netDegree) shastr += "-nd-"+str(self.netDegree)
shastr += "-r-"+str(self.run) shastr += "-r-"+str(self.run)
shastr += "-mn-"+str(self.maliciousNodes) shastr += "-mn-"+str(self.maliciousNodes)
shastr += "-ntypes-"+str(self.nodeTypes['group'])
return shastr return shastr
def setSeed(self, seed): def setSeed(self, seed):

View File

@ -44,6 +44,15 @@ class Simulator:
self.proposerPublishToR = config.evalConf(self, config.proposerPublishToR, shape) self.proposerPublishToR = config.evalConf(self, config.proposerPublishToR, shape)
self.proposerPublishToC = config.evalConf(self, config.proposerPublishToR, shape) self.proposerPublishToC = config.evalConf(self, config.proposerPublishToR, shape)
def getNodeClass(self, nodeIdx):
nodeRatios = [_v['weight'] for _k, _v in self.shape.nodeTypes["classes"].items()]
nodeCounts = [int(self.shape.numberNodes * ratio / sum(nodeRatios)) for ratio in nodeRatios]
commulativeSum = [sum(nodeCounts[:i+1]) for i in range(len(nodeCounts))]
commulativeSum[-1] = self.shape.numberNodes
for i, idx in enumerate(commulativeSum):
if nodeIdx <= idx:
return self.shape.nodeClasses[i + 1]
def initValidators(self): def initValidators(self):
"""It initializes all the validators in the network.""" """It initializes all the validators in the network."""
self.glob = Observer(self.logger, self.shape) self.glob = Observer(self.logger, self.shape)
@ -125,11 +134,11 @@ class Simulator:
self.logger.error("custodyRows has to be smaller than %d" % self.shape.nbRows) self.logger.error("custodyRows has to be smaller than %d" % self.shape.nbRows)
vs = [] vs = []
nodeClass = 1 if (i <= self.shape.numberNodes * self.shape.class1ratio) else 2 nodeClass = self.getNodeClass(i)
vpn = self.shape.vpn1 if (nodeClass == 1) else self.shape.vpn2 vpn = self.shape.nodeTypes["classes"][nodeClass]["def"]['validatorsPerNode']
for v in range(vpn): for v in range(vpn):
vs.append(initValidator(self.shape.nbRows, self.shape.custodyRows, self.shape.nbCols, self.shape.custodyCols)) vs.append(initValidator(self.shape.nbRows, self.shape.custodyRows, self.shape.nbCols, self.shape.custodyCols))
val = Node(i, int(not i!=0), amImalicious_value, self.logger, self.shape, self.config, vs) val = Node(i, int(not i!=0), nodeClass, amImalicious_value, self.logger, self.shape, self.config, vs)
if i == self.proposerID: if i == self.proposerID:
val.initBlock() val.initBlock()
else: else:
@ -311,12 +320,9 @@ class Simulator:
cnN = "nodes ready" cnN = "nodes ready"
cnV = "validators ready" cnV = "validators ready"
cnT0 = "TX builder mean" cnT0 = "TX builder mean"
cnT1 = "TX class1 mean" cnT = lambda i: f"TX class{i} mean"
cnT2 = "TX class2 mean" cnR = lambda i: f"RX class{i} mean"
cnR1 = "RX class1 mean" cnD = lambda i: f"Dup class{i} mean"
cnR2 = "RX class2 mean"
cnD1 = "Dup class1 mean"
cnD2 = "Dup class2 mean"
# if custody is based on the requirements of underlying individual # if custody is based on the requirements of underlying individual
# validators, we can get detailed data on how many validated. # validators, we can get detailed data on how many validated.
@ -325,19 +331,20 @@ class Simulator:
cnVv = validatorProgress cnVv = validatorProgress
else: else:
cnVv = validatorAllProgress cnVv = validatorAllProgress
progressVector.append({ progressDict = {
cnS:sampleProgress, cnS: sampleProgress,
cnN:nodeProgress, cnN: nodeProgress,
cnV:cnVv, cnV: cnVv,
cnT0: trafficStats[0]["Tx"]["mean"], cnT0: trafficStats[0]["Tx"]["mean"]
cnT1: trafficStats[1]["Tx"]["mean"], }
cnT2: trafficStats[2]["Tx"]["mean"], for nc in self.shape.nodeClasses:
cnR1: trafficStats[1]["Rx"]["mean"], if nc != 0:
cnR2: trafficStats[2]["Rx"]["mean"], progressDict[cnT(nc)] = trafficStats[nc]["Tx"]["mean"]
cnD1: trafficStats[1]["RxDup"]["mean"], progressDict[cnR(nc)] = trafficStats[nc]["Rx"]["mean"]
cnD2: trafficStats[2]["RxDup"]["mean"], progressDict[cnD(nc)] = trafficStats[nc]["RxDup"]["mean"]
})
progressVector.append(progressDict)
if missingSamples == oldMissingSamples: if missingSamples == oldMissingSamples:
if len(missingVector) > self.config.steps4StopCondition: if len(missingVector) > self.config.steps4StopCondition:

File diff suppressed because it is too large Load Diff

View File

@ -76,18 +76,31 @@ proposerPublishToC = "shape.netDegree"
validatorBasedCustody = False validatorBasedCustody = False
custodyRows = range(2, 3, 2) custodyRows = range(2, 3, 2)
custodyCols = range(2, 3, 2) custodyCols = range(2, 3, 2)
minCustodyRows = range(2, 3, 2)
# ratio of class1 nodes (see below for parameters per class) minCustodyCols = range(2, 3, 2)
class1ratios = [0.8]
# Number of validators per beacon node
validatorsPerNode1 = [1]
validatorsPerNode2 = [5]
# Set uplink bandwidth in megabits/second # Set uplink bandwidth in megabits/second
bwUplinksProd = [200] bwUplinksProd = [200]
bwUplinks1 = [10]
bwUplinks2 = [200] nodeTypesGroup = [
{
"group": "g1",
"classes": {
1: {
"weight": 70,
"def": {'validatorsPerNode': 1, 'bwUplinks': 10}
},
2: {
"weight": 20,
"def": {'validatorsPerNode': 5, 'bwUplinks': 200}
},
3: {
"weight": 10,
"def": {'validatorsPerNode': 10, 'bwUplinks': 500}
}
}
}
]
# Step duration in miliseconds (Classic RTT is about 100ms) # Step duration in miliseconds (Classic RTT is about 100ms)
stepDuration = 50 stepDuration = 50
@ -135,11 +148,11 @@ colsK = range(32, 65, 128)
rowsK = range(32, 65, 128) rowsK = range(32, 65, 128)
def nextShape(): def nextShape():
for nbCols, nbColsK, nbRows, nbRowsK, run, fm, fr, mn, class1ratio, chR, chC, vpn1, vpn2, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product( for nbCols, nbColsK, nbRows, nbRowsK, run, fm, fr, mn, chR, chC, minChR, minChC, nn, netDegree, bwUplinkProd, nodeTypes in itertools.product(
cols, colsK, rows, rowsK, runs, failureModels, failureRates, maliciousNodes, class1ratios, custodyRows, custodyCols, validatorsPerNode1, validatorsPerNode2, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): cols, colsK, rows, rowsK, runs, failureModels, failureRates, maliciousNodes, custodyRows, custodyCols, minCustodyRows, minCustodyCols, numberNodes, netDegrees, bwUplinksProd, nodeTypesGroup):
# Network Degree has to be an even number # Network Degree has to be an even number
if netDegree % 2 == 0: if netDegree % 2 == 0:
shape = Shape(nbCols, nbColsK, nbRows, nbRowsK, nn, fm, fr, mn, class1ratio, chR, chC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) shape = Shape(nbCols, nbColsK, nbRows, nbRowsK, nn, fm, fr, mn, chR, chC, minChR, minChC, netDegree, bwUplinkProd, run, nodeTypes)
yield shape yield shape
def evalConf(self, param, shape = None): def evalConf(self, param, shape = None):

View File

@ -213,11 +213,11 @@ def study():
logger.info("A total of %d simulations ran in %d seconds" % (len(results), end-start), extra=format) logger.info("A total of %d simulations ran in %d seconds" % (len(results), end-start), extra=format)
if config.visualization: if config.visualization:
vis = Visualizer(execID, config) # vis = Visualizer(execID, config)
vis.plotHeatmaps() # vis.plotHeatmaps()
visual = Visualizor(execID, config, results) visual = Visualizor(execID, config, results)
visual.plotHeatmaps("nn", "fr") # visual.plotHeatmaps("nn", "fr")
visual.plotAllHeatMaps() visual.plotAllHeatMaps()
if __name__ == "__main__": if __name__ == "__main__":