4
4
import logging , random
5
5
import pandas as pd
6
6
from functools import partial , partialmethod
7
+ from collections import deque
7
8
from datetime import datetime
8
9
from DAS .tools import *
9
10
from DAS .results import *
@@ -21,6 +22,7 @@ def __init__(self, shape, config, execID):
21
22
self .format = {"entity" : "Simulator" }
22
23
self .execID = execID
23
24
self .result = Result (self .shape , self .execID )
25
+ self .dhtResult = Result (self .shape , self .execID )
24
26
self .validators = []
25
27
self .logger = []
26
28
self .logLevel = config .logLevel
@@ -31,6 +33,7 @@ def __init__(self, shape, config, execID):
31
33
self .distC = []
32
34
self .nodeRows = []
33
35
self .nodeColumns = []
36
+ self .dhtNetwork = DHTNetwork (0 , 0 , [0 ])
34
37
35
38
# In GossipSub the initiator might push messages without participating in the mesh.
36
39
# proposerPublishOnly regulates this behavior. If set to true, the proposer is not
@@ -178,23 +181,6 @@ def initNetwork(self):
178
181
self .logger .debug ("Val %d : rowN %s" , i , self .validators [i ].rowNeighbors , extra = self .format )
179
182
self .logger .debug ("Val %d : colN %s" , i , self .validators [i ].columnNeighbors , extra = self .format )
180
183
181
- def initDHTNetwork (self ):
182
- """ Compose the DHT network based on the pre-initialized Validators """
183
- # compose the DHT networking layer
184
- self .logger .info ("Initializing DHTNetwork... with %d nodes" % self .shape .numberNodes , extra = self .format )
185
- self .DHTNetwork = DHTNetwork (self .execID , self .shape .failureRate , self .config .stepDuration )
186
-
187
- # initialize each of the routing tables
188
- startTime = time .time ()
189
- _ = self .DHTNetwork .init_with_random_peers (self .config .numJobs , self .shape .numberNodes ,
190
- self .shape .k , self .shape .alpha , self .shape .k , self .config .nilStepsToStopLookup )
191
- self .logger .info ("DHT fast-init (%d jobs) done in %.2f secs" , self .config .numJobs , time .time ()- startTime , extra = self .format )
192
-
193
- # add the initialized DHTClient back to the Validator
194
- for val in self .validators :
195
- val .addDHTClient (self .DHTNetwork .nodestore .get_node (val .ID ))
196
- # the network should be ready to go :)
197
-
198
184
def initLogger (self ):
199
185
"""It initializes the logger."""
200
186
logging .TRACE = 5
@@ -239,7 +225,7 @@ def runBlockBroadcasting(self):
239
225
self .glob .checkRowsColumns (self .validators )
240
226
for i in range (0 ,self .shape .numberNodes ):
241
227
if i == self .proposerID :
242
- self .validators [i ].initBlock ()
228
+ self .block = self . validators [i ].initBlock () # Keep the OG block that we are broadcasting
243
229
else :
244
230
self .validators [i ].logIDs ()
245
231
arrived , expected , ready , validatedall , validated = self .glob .checkStatus (self .validators )
@@ -253,7 +239,7 @@ def runBlockBroadcasting(self):
253
239
oldMissingSamples = missingSamples
254
240
self .logger .debug ("PHASE SEND %d" % steps , extra = self .format )
255
241
for i in range (0 ,self .shape .numberNodes ):
256
- self .validators [i ].send ()
242
+ self .validators [i ].sendToNeigbors ()
257
243
self .logger .debug ("PHASE RECEIVE %d" % steps , extra = self .format )
258
244
for i in range (1 ,self .shape .numberNodes ):
259
245
self .validators [i ].receiveRowsColumns ()
@@ -325,6 +311,89 @@ def runBlockBroadcasting(self):
325
311
self .result .populate (self .shape , self .config , missingVector )
326
312
return self .result
327
313
328
- def runBlockPublicationToDHT (self ):
329
- """It runs the main DHT simulation, where the block proposer has to send the segments to the XOR close enough nodes."""
314
+ def initDHTNetwork (self ):
315
+ """ Compose the DHT network based on the pre-initialized Validators """
316
+ # compose the DHT networking layer
317
+ self .logger .info ("Initializing DHTNetwork... with %d nodes" % self .shape .numberNodes , extra = self .format )
318
+ self .dhtNetwork = DHTNetwork (self .execID , self .shape .failureRate , [self .config .stepDuration ])
319
+
320
+ # initialize each of the routing tables
321
+ startTime = time .time ()
322
+ _ = self .dhtNetwork .init_with_random_peers (self .config .numJobs , self .shape .numberNodes ,
323
+ self .shape .k , self .shape .alpha , self .shape .k , self .config .nilStepsToStopLookup )
324
+ self .logger .info ("DHT fast-init (%d jobs) done in %.2f secs" , self .config .numJobs , time .time ()- startTime , extra = self .format )
325
+
326
+ # add the initialized DHTClient back to the Validator
327
+ for val in self .validators :
328
+ val .addDHTclient (self .dhtNetwork .nodestore .get_node (val .ID ))
329
+ # the network should be ready to go :)
330
+
331
+ def runBlockPublicationToDHT (self , strategy ):
332
+ """It runs the dht simulation to seed the DHT with blocks' info"""
333
+
334
+ if strategy == "builder-seeding-segments" :
335
+ self .logger .info ("Seeding DHT with '%s' strategy" % strategy , extra = self .format )
336
+ self .dhtBlockProposerSeedingDHTwithSegments ()
337
+ else :
338
+ self .logger .error ("unable to identify DHT seeding strategy '%s'" % strategy , extra = self .format )
339
+
330
340
return
341
+
342
+ def dhtBlockProposerSeedingDHTwithSegments (self ):
343
+ """It runs the simulation where the block builder has to seed the DHT with all the block segments"""
344
+ # check who is the block proposer
345
+ blockProposer = self .dhtNetwork .nodestore .get_node (self .proposerID )
346
+ self .logger .info ("Node %d will start providing the block to the DHT!" % self .proposerID , extra = self .format )
347
+
348
+ # make a dht lookup for each of the segments in the block
349
+ # TODO: currently sequential, add randomness later
350
+ # TODO: it is pretty hard to define the bandwidth usage of so many lookups,
351
+ # a concurrency degree could help though (only XX lookups at the time)
352
+ totalSegements = self .shape .blockSize * self .shape .blockSize
353
+ segmentIDs = deque (maxlen = totalSegements )
354
+ segmentHashes = deque (maxlen = totalSegements )
355
+ segmentValues = deque (maxlen = totalSegements )
356
+ closestNodes = deque (maxlen = totalSegements )
357
+ lookupAggrDelays = deque (maxlen = totalSegements )
358
+ lookupTotalAttempts = deque (maxlen = totalSegements )
359
+ lookupConnectedNodes = deque (maxlen = totalSegements )
360
+ lookupProcessExecTime = deque (maxlen = totalSegements )
361
+
362
+ lookupStartTime = time .time ()
363
+ for rowID in range (self .shape .blockSize ):
364
+ for columnID in range (self .shape .blockSize ):
365
+ segmentID = self .block .getUniqueIDforSegment (rowID , columnID )
366
+ segmentHash = self .block .getSegmentHash (rowID , columnID )
367
+ segmentValue = self .block .getSegment (rowID , columnID )
368
+ self .logger .debug (f"starting DHT lookup for segment { segmentID } with hash { segmentHash } " ,
369
+ extra = self .format )
370
+ nodes , _ , summary , aggrDelay = blockProposer .lookup_for_hash (segmentHash )
371
+ self .logger .debug (
372
+ f"finished DHT lookup for segment { segmentID } with hash { segmentHash } in { summary ['finishTime' ] - summary ['startTime' ]} secs" ,
373
+ extra = self .format )
374
+ segmentIDs .append (segmentID )
375
+ segmentHashes .append (segmentHash )
376
+ segmentValues .append (segmentValue )
377
+ closestNodes .append (nodes )
378
+ lookupAggrDelays .append (aggrDelay )
379
+ lookupTotalAttempts .append (summary ["connectionAttempts" ])
380
+ lookupConnectedNodes .append (summary ["successfulCons" ])
381
+ lookupProcessExecTime .append (summary ["finishTime" ] - summary ["startTime" ])
382
+ self .logger .info (f"lookup for the { totalSegements } segments done in { time .time () - lookupStartTime } secs" ,
383
+ extra = self .format )
384
+
385
+ # make the provide operation of the segments to the closest nodes
386
+ # TODO: at the moment, this only supports the standard Provide operation (mimicking IPFS' provide operation)
387
+ # for each segment add the K closest nodes as neighbours
388
+
389
+ # start the dissemination of the segments based on avg latency windows,
390
+ # track Tx and Rx stats
391
+ # remember, opening a connection uses one latency step
392
+
393
+ # when there are no more segments to disseminate, get all the metrics
394
+ # Avg successful provides vs failed ones on provide
395
+ # avg time for the lookup
396
+
397
+ # TODO: do we want to check if the content would be retrievable?
398
+
399
+ return
0 commit comments