2017-12-12 18:15:56 +00:00
'''
A handler for Block . py that takes a collection of blocks ( which
only reference parents ) as input data . It uses a doubly - linked
tree to determine precedent relationships efficiently , and it can
use that precedence relationship to produce a reduced / robust pre -
cedence relationship as output ( the spectre precedence relationship
between blocks .
Another handler will extract a coherent / robust list of non - conflict -
2017-12-12 22:33:05 +00:00
ing transactions from a reduced / robust BlockHandler object .
2017-12-12 18:15:56 +00:00
'''
from Block import *
2017-12-12 22:33:05 +00:00
class BlockHandler ( object ) :
2017-12-12 18:15:56 +00:00
def __init__ ( self ) :
print ( " Initializing " )
2017-12-12 22:33:05 +00:00
# Initialize a BlockHandler object.
2017-12-12 18:15:56 +00:00
self . data = None
self . blocks = { } # Set of blocks (which track parents)
self . family = { } # Doubly linked list tracks parent-and-child links
self . invDLL = { } # subset of blocks unlikely to be re-orged
self . roots = { } # dict {blockIdent : block} root blocks
self . leaves = { }
self . antichainCutoff = 600 # stop re-orging after this many layers
self . pendingVotes = { }
self . votes = { }
def _addBlocks ( self , blocksIn ) :
print ( " Adding Blocks " )
# Take dict of {blockIdent : block} and call _addBlock on each.
for b in blocksIn :
self . _addBlock ( blocksIn [ b ] )
def _addBlock ( self , b ) :
print ( " Adding block " )
# Take a single block b and add to self.blocks, record family
# relations, update leaf monitor, update root monitor if nec-
# essary
diffDict = { b . ident : b }
self . blocks . update ( diffDict )
self . family . update ( { b . ident : { } } )
self . family [ b . ident ] . update ( { " parents " : b . parents , " children " : { } } )
for parentIdent in b . parents :
if parentIdent not in self . family :
self . family . update ( { parentIdent : { } } )
if " parents " not in self . family [ parentIdent ] :
self . family [ parentIdent ] . update ( { " parents " : { } } )
if " children " not in self . family [ parentIdent ] :
self . family [ parentIdent ] . update ( { " children " : { } } )
self . family [ parentIdent ] [ " parents " ] . update ( b . parents )
self . family [ parentIdent ] [ " children " ] . update ( diffDict )
if parentIdent in self . leaves :
del self . leaves [ parentIdent ]
if len ( b . parents ) == 0 and b . ident not in self . roots :
self . roots . update ( diffDict )
self . leaves . update ( diffDict )
def inPast ( self , x , y ) :
print ( " Testing if in past " )
# Return true if y is an ancestor of x
q = deque ( )
for pid in self . blocks [ x ] . parents :
if pid == y :
return True
break
q . append ( pid )
while ( len ( q ) > 0 ) :
nxtIdent = q . popleft ( )
if len ( self . blocks [ nxtIdent ] . parents ) > 0 :
for pid in self . blocks [ nxtIdent ] . parents :
if pid == y :
return True
break
q . append ( pid )
return False
def vote ( self ) :
print ( " Voting " )
# Compute partial spectre vote for top several layers of
# the dag.
( U , vids ) = self . leafBackAntichain ( )
self . votes = { }
q = deque ( )
self . pendingVotes = { }
for i in range ( len ( U ) ) :
for leafId in U [ i ] :
if i > 0 :
self . sumPendingVotes ( leafId , vids )
for x in U [ i ] :
if x != leafId :
q . append ( x )
while ( len ( q ) > 0 ) :
x = q . popleft ( )
if ( leafId , leafId , x ) not in self . votes :
self . votes . update ( { ( leafId , leafId , x ) : 1 } )
else :
try :
assert self . votes [ ( leafId , leafId , x ) ] == 1
except AssertionError :
print ( " Woops, we found (leafId, leafId, x) as a key in self.votes while running vote(), and it should be +1, but it isn ' t: \n \n " , ( leafId , leafId , x ) , self . votes [ ( leafId , leafId , x ) ] , " \n \n " )
if ( leafId , x , leafId ) not in self . votes :
self . votes . update ( { ( leafId , x , leafId ) : - 1 } )
else :
try :
assert self . votes [ ( leafId , x , leafId ) ] == - 1
except AssertionError :
print ( " Woops, we found (leafId, x, leafId) as a key in self.votes while running vote(), and it should be +1, but it isn ' t: \n \n " , ( leafId , x , leafId ) , self . votes [ ( leafId , x , leafId ) ] , " \n \n " )
self . transmitVote ( leafId , leafId , x )
for pid in self . blocks [ x ] . parents :
if not self . inPast ( leafId , pid ) and pid in vids and pid != leafId :
q . append ( pid )
print ( self . votes )
def sumPendingVotes ( self , blockId , vulnIds ) :
print ( " Summing pending votes " )
# For a blockId, take all pending votes for vulnerable IDs (x,y)
# if the net is positive vote 1, if the net is negative vote -1
# otherwise vote 0.
for x in vulnIds :
for y in vulnIds :
if ( blockId , x , y ) in self . pendingVotes :
if self . pendingVotes [ ( blockId , x , y ) ] > 0 :
if ( blockId , x , y ) not in self . votes :
self . votes . update ( { ( blockId , x , y ) : 1 } )
else :
try :
assert self . votes [ ( blockId , x , y ) ] == 1
except AssertionError :
print ( " Woops, we found (blockId, x, y) as a key in self.votes, and it should be +1, but it isn ' t: \n \n " , ( blockId , x , y ) , self . votes [ ( blockId , x , y ) ] , " \n \n " )
if ( blockId , y , x ) not in self . votes :
self . votes . update ( { ( blockId , y , x ) : - 1 } )
else :
try :
assert self . votes [ ( blockId , y , x ) ] == - 1
except AssertionError :
print ( " Woops, we found (blockId, y, x) as a key in self.votes, and it should be -1, but it isn ' t: \n \n " , ( blockId , y , x ) , self . votes [ ( blockId , y , x ) ] , " \n \n " )
self . transmitVote ( blockId , x , y )
elif self . pendingVotes [ ( blockId , x , y ) ] < 0 :
if ( blockId , x , y ) not in self . votes :
self . votes . update ( { ( blockId , x , y ) : - 1 } )
else :
try :
assert self . votes [ ( blockId , x , y ) ] == - 1
except AssertionError :
print ( " Woops, we found (blockId, x, y) as a key in self.votes, and it should be -1, but it isn ' t: \n \n " , ( blockId , x , y ) , self . votes [ ( blockId , x , y ) ] , " \n \n " )
if ( blockId , y , x ) not in self . votes :
self . votes . update ( { ( blockId , y , x ) : 1 } )
else :
try :
assert self . votes [ ( blockId , y , x ) ] == 1
except AssertionError :
print ( " Woops, we found (blockId, y, x) as a key in self.votes, and it should be +1, but it isn ' t: \n \n " , ( blockId , x , y ) , self . votes [ ( blockId , x , y ) ] , " \n \n " )
self . transmitVote ( blockId , y , x )
else :
if ( blockId , x , y ) not in self . votes :
self . votes . update ( { ( blockId , x , y ) : 0 } )
else :
try :
assert self . votes [ ( blockId , x , y ) ] == 0
except AssertionError :
print ( " Woops, we found (blockId, x, y) as a key in self.votes, and it should be 0, but it isn ' t: \n \n " , ( blockId , x , y ) , self . votes [ ( blockId , x , y ) ] , " \n \n " )
if ( blockId , y , x ) not in self . votes :
self . votes . update ( { ( blockId , y , x ) : 0 } )
else :
try :
assert self . votes [ ( blockId , y , x ) ] == 0
except AssertionError :
print ( " Woops, we found (blockId, y, x) as a key in self.votes, and it should be 0, but it isn ' t: \n \n " , ( blockId , y , x ) , self . votes [ ( blockId , y , x ) ] , " \n \n " )
def transmitVote ( self , v , x , y ) :
print ( " Transmitting votes " )
q = deque ( )
for pid in self . blocks [ v ] . parents :
q . append ( pid )
while ( len ( q ) > 0 ) :
print ( " Length of queue = " , len ( q ) )
nxtPid = q . popleft ( )
if ( nxtPid , x , y ) not in self . pendingVotes :
self . pendingVotes . update ( { ( nxtPid , x , y ) : 1 } )
self . pendingVotes . update ( { ( nxtPid , y , x ) : - 1 } )
else :
self . pendingVotes [ ( nxtPid , x , y ) ] + = 1
self . pendingVotes [ ( nxtPid , y , x ) ] - = 1
if len ( self . blocks [ nxtPid ] . parents ) > 0 :
for pid in self . blocks [ nxtPid ] . parents :
if pid != nxtPid :
q . append ( pid )
def leafBackAntichain ( self ) :
print ( " Computing antichain " )
temp = copy . deepcopy ( self )
decomposition = [ ]
vulnIdents = None
decomposition . append ( temp . leaves )
vulnIdents = decomposition [ - 1 ]
temp = temp . pruneLeaves ( )
while ( len ( temp . blocks ) > 0 and len ( decomposition ) < self . antichainCutoff ) :
decomposition . append ( temp . leaves )
for xid in decomposition [ - 1 ] :
if xid not in vulnIdents :
vulnIdents . update ( { xid : decomposition [ - 1 ] [ xid ] } )
temp = temp . pruneLeaves ( )
return decomposition , vulnIdents
def pruneLeaves ( self ) :
print ( " Pruning leaves " )
2017-12-12 22:33:05 +00:00
out = BlockHandler ( )
2017-12-12 18:15:56 +00:00
q = deque ( )
for rootIdent in self . roots :
q . append ( rootIdent )
while ( len ( q ) > 0 ) :
thisIdent = q . popleft ( )
if thisIdent not in self . leaves :
out . _addBlock ( self . blocks [ thisIdent ] )
for chIdent in self . family [ thisIdent ] [ " children " ] :
q . append ( chIdent )
return out
class Test_RoBlock ( unittest . TestCase ) :
2017-12-12 22:33:05 +00:00
def test_BlockHandler ( self ) :
R = BlockHandler ( )
2017-12-12 18:15:56 +00:00
b = Block ( )
b . data = " zirconium encrusted tweezers "
b . _recomputeIdent ( )
R . _addBlock ( b )
b = Block ( )
b . data = " brontosaurus slippers cannot exist "
b . addParents ( R . leaves )
R . _addBlock ( b )
R . vote ( )
suite = unittest . TestLoader ( ) . loadTestsFromTestCase ( Test_RoBlock )
unittest . TextTestRunner ( verbosity = 1 ) . run ( suite )