2017-10-23 23:52:11 +00:00
from Block import *
2017-11-05 23:17:34 +00:00
#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####
2017-10-23 23:52:11 +00:00
class BlockDAG ( object ) :
2017-11-05 23:17:34 +00:00
""" Collection of >=1 block. """
def __init__ ( self , params = None ) :
self . genesis = Block ( )
self . genesis . id = " 0 "
self . blocks = { self . genesis . id : self . genesis }
self . leaves = { self . genesis . id : self . genesis }
# Blocks from top-down antichain subsets covering >= 1/2 of blockDAG
self . votBlocks = { self . genesis . id : self . genesis }
# Blocks from top-down antichain subsets "non-negl" likely to re-org
self . ordBlocks = { self . genesis . id : self . genesis }
if params is not None :
self . security = params
2017-10-23 23:52:11 +00:00
else :
2017-11-05 23:17:34 +00:00
self . security = 10
self . vote = { }
self . pending = { }
for blockZ in self . votBlocks :
for blockX in self . ordBlocks :
for blockY in self . ordBlocks :
self . vote . update ( { ( blockZ , blockX , blockY ) : 0 } )
self . pending . update ( { ( blockZ , blockX , blockY ) : 0 } )
def computeVote ( self , dagIn ) :
( canopy , fullCanopy ) = self . pick ( dagIn )
for layer in fullCanopy :
for blockZ in layer :
if blockZ not in dagIn . votBlocks :
continue
else :
for blockX in layer :
if blockX not in dagIn . ordBlocks :
continue
else :
for blockY in layer :
if blockY not in dagIn . ordBlocks :
continue
else :
if self . inPast ( dagIn , blockY , blockZ ) and self . inPast ( dagIn , blockX , blockZ ) :
# then Z votes recursively
if blockZ not in dagIn . seenPasts :
dagIn . seenPasts . update ( { blockZ : dagIn . getPast ( blockZ ) } )
dagIn . seenVotes . update ( { blockZ : dagIn . vote ( dagIn . seenPasts [ blockZ ] ) } )
dagIn . vote . update ( { ( blockZ , blockX , blockY ) : dagIn . seenVotes [ blockZ ] [ ( blockX , blockY ) ] , ( blockZ , blockY , blockX ) : dagIn . seenVotes [ blockZ ] [ ( blockY , blockX ) ] , ( blockZ , blockX , blockZ ) : 1 , ( blockZ , blockZ , blockX ) : - 1 , ( blockZ , blockY , blockZ ) : 1 , ( blockZ , blockZ , blockY ) : - 1 } )
elif self . inPast ( dagIn , blockY , blockZ ) and not self . inPast ( dagIn , blockX , blockZ ) :
dagIn . vote . update ( { ( blockZ , blockX , blockY ) : - 1 , ( blockZ , blockY , blockX ) : 1 , ( blockZ , blockX , blockZ ) : - 1 , ( blockZ , blockZ , blockX ) : 1 , ( blockZ , blockZ , blockY ) : - 1 , ( blockZ , blockY , blockZ ) : 1 } ) # Then Z votes Y < Z < X
elif not self . inPast ( dagIn , blockY , blockZ ) and self . inPast ( dagIn , blockX , blockZ ) :
dagIn . vote . update ( { ( blockZ , blockX , blockY ) : 1 , ( blockZ , blockY , blockX ) : - 1 , ( blockZ , blockX , blockZ ) : 1 , ( blockZ , blockZ , blockX ) : - 1 , ( blockZ , blockZ , blockY ) : 1 , ( blockZ , blockY , blockZ ) : - 1 } ) # Then Z votes X < Z < Y
else :
if dagIn . pending [ ( blockZ , blockX , blockY ) ] > 0 :
dagIn . vote . update ( { ( blockZ , blockX , blockY ) : 1 , ( blockZ , blockY , blockX ) : - 1 , ( blockZ , blockX , blockZ ) : - 1 , ( blockZ , blockZ , blockX ) : 1 , ( blockZ , blockY , blockZ ) : - 1 , ( blockZ , blockZ , blockY ) : 1 } )
elif dagIn . pending [ ( blockZ , blockX , blockY ) ] < 0 :
dagIn . vote . update ( { ( blockZ , blockX , blockY ) : - 1 , ( blockZ , blockY , blockX ) : 1 , ( blockZ , blockX , blockZ ) : - 1 , ( blockZ , blockZ , blockX ) : 1 , ( blockZ , blockY , blockZ ) : - 1 , ( blockZ , blockZ , blockY ) : 1 } )
else :
dagIn . vote . update ( { ( blockZ , blockX , blockY ) : 0 , ( blockZ , blockY , blockX ) : 0 , ( blockZ , blockX , blockZ ) : - 1 , ( blockZ , blockZ , blockX ) : 1 , ( blockZ , blockY , blockZ ) : - 1 , ( blockZ , blockZ , blockY ) : 1 } )
q = deque ( )
for p in dagIn . blocks [ blockZ ] . parents :
if p in dagIn . votBlocks :
q . append ( p )
while ( len ( q ) > 0 ) :
nextBlock = q . popleft ( )
if ( nextBlock , blockX , blockY ) not in dagIn . pending :
dagIn . pending . update ( { ( nextBlock , blockX , blockY ) : 0 } )
if ( nextBlock , blockY , blockX ) not in dagIn . pending :
dagIn . pending . update ( { ( nextBlock , blockY , blockX ) : 0 } )
if dagIn . vote [ ( blockZ , blockX , blockY ) ] > 0 :
dagIn . pending [ ( nextBlock , blockX , blockY ) ] + = 1
dagIn . pending [ ( nextBlock , blockY , blockX ) ] - = 1
elif dagIn . vote [ ( blockZ , blockX , blockY ) ] < 0 :
dagIn . pending [ ( nextBlock , blockX , blockY ) ] - = 1
dagIn . pending [ ( nextBlock , blockY , blockX ) ] + = 1
for p in dagIn . blocks [ nextBlock ] . parents :
if p in dagIn . votBlocks :
q . append ( p )
totalVote = { }
for blockX in dagIn . ordBlocks :
for blockY in dagIn . ordBlocks :
if ( blockX , blockY ) not in totalVote :
totalVote . update ( { ( blockX , blockY ) : 0 , ( blockY , blockX ) : 0 } )
for blockZ in dagIn . votBlocks :
if dagIn . vote [ ( blockZ , blockX , blockY ) ] > 0 :
totalVote [ ( blockX , blockY ) ] + = 1
elif dagIn . vote [ ( blockZ , blockX , blockY ) ] < 0 :
totalVote [ ( blockX , blockY ) ] - = 1
if totalVote [ ( blockX , blockY ) ] > 0 :
totalVote [ ( blockX , blockY ) ] = 1
elif totalVote [ ( blockX , blockY ) ] < 0 :
totalVote [ ( blockX , blockY ) ] = - 1
return totalVote
def pick ( self , dagIn ) :
""" Pick voting blocks and orderable blocks """
( canopy , fullCanopy ) = self . antichain ( dagIn )
dagIn . votBlocks = { }
dagIn . ordBlocks = { }
idx = 0
count = len ( canopy [ idx ] )
for block in canopy [ idx ] :
dagIn . votBlocks . update ( { block : dagIn . blocks [ block ] } )
dagIn . ordBlocks . update ( { blcok : dagIn . blocks [ block ] } )
numVoters = 1 - ( ( - len ( dagIn . blocks ) ) / / 2 )
while ( count < numVoters ) :
idx + = 1
count + = len ( canopy [ idx ] )
for block in canopy [ idx ] :
dagIn . votBlocks . update ( { block : dagIn . blocks [ block ] } )
if idx < self . security :
dagIn . ordBlocks . update ( { block : dagIn . blocks [ block ] } )
return ( canopy , fullCanopy )
def makeBlock ( self , idIn , parentsIn ) :
assert idIn not in self . blocks
newBlock = Block ( )
newBlock . id = idIn
newBlock . addParents ( parentsIn )
self . blocks . update ( { newBlock . id : newBlock } )
for parent in parentsIn :
2017-10-23 23:52:11 +00:00
if parent in self . leaves :
del self . leaves [ parent ]
2017-11-05 23:17:34 +00:00
self . blocks [ parent ] . addChild ( newBlock )
self . leaves . update ( { newBlock . id : newBlock } )
2017-10-23 23:52:11 +00:00
2017-11-05 23:17:34 +00:00
def pruneLeaves ( self , dagIn ) :
result = BlockDAG ( )
result . genesis . id = dagIn . genesis . id
q = deque ( )
for child in dagIn . genesis . children :
if child not in dagIn . leaves :
q . append ( child )
while ( len ( q ) > 0 ) :
nextBlock = q . popleft ( )
result . makeBlock ( nextBlock , dagIn . blocks [ nextBlock ] . parents )
for child in dagIn . blocks [ nextBlock ] . children :
if child not in dagIn . leaves :
q . append ( child )
return result
def antichain ( self , dagIn ) :
canopy = [ ]
fullCanopy = [ ]
nextDag = dagIn
canopy . append ( nextDag . leaves )
fullCanopy . append ( nextDag . leaves )
while ( len ( nextDag . blocks ) > 1 ) :
nextDag = dagIn . pruneLeaves ( dagIn )
canopy . append ( nextDag . leaves )
fullCanopy . append ( fullCanopy [ - 1 ] )
for leaf in nextDag . leaves :
fullCanopy [ - 1 ] . append ( leaf )
nextDag = self . pruneLeaves ( dagIn )
return ( canopy , fullCanopy )
def inPast ( self , dagIn , y , x ) :
""" self.inPast(dag, y,x) if and only if y is in the past of x in dag """
found = False
if y in dagIn . blocks [ x ] . parents :
found = True
else :
q = deque ( )
for parent in dagIn . blocks [ x ] . parents :
q . append ( parent )
while ( len ( q ) > 0 ) :
nextBlock = q . popleft ( )
if y in dagIn . blocks [ nextBlock ] . parents :
found = True
break
else :
for parent in dagIn . blocks [ nextBlock ] . parents :
q . append ( parent )
return found
2017-10-23 23:52:11 +00:00
2017-11-05 23:17:34 +00:00
def getPast ( self , dagIn , block ) :
subdag = BlockDAG ( )
subdag . genesis = dagIn . genesis
q = deque ( )
for child in dagIn . genesis . children :
if self . inPast ( dagIn , child , block ) :
q . append ( child )
while len ( q ) > 0 :
nextBlock = q . popleft ( )
subdag . makeBlock ( dagIn . blocks [ nextBlock ] )
for child in dagIn . blocks [ nextBlock ] . children :
if self . inPast ( dagIn , child , block ) :
q . append ( child )
return subdag
2017-10-23 23:52:11 +00:00
class Test_BlockDAG ( unittest . TestCase ) :
def test_BlockDAG ( self ) :
dag = BlockDAG ( )
self . assertTrue ( " 0 " in dag . blocks )
self . assertTrue ( " 0 " in dag . leaves )
self . assertTrue ( len ( dag . blocks ) == 1 )
self . assertTrue ( len ( dag . leaves ) == 1 )
2017-11-05 23:17:34 +00:00
b0 = dag . genesis
2017-10-23 23:52:11 +00:00
2017-11-05 23:17:34 +00:00
dag . makeBlock ( " 1 " , { " 0 " : b0 } )
b1 = dag . blocks [ " 1 " ]
2017-10-23 23:52:11 +00:00
self . assertTrue ( " 1 " in dag . blocks )
self . assertTrue ( " 1 " in dag . leaves )
self . assertTrue ( " 0 " not in dag . leaves )
self . assertTrue ( len ( dag . blocks ) == 2 )
self . assertTrue ( len ( dag . leaves ) == 1 )
2017-11-05 23:17:34 +00:00
self . assertTrue ( " 1 " in b0 . children )
self . assertTrue ( " 1 " in dag . genesis . children )
self . assertTrue ( " 1 " in dag . blocks [ dag . genesis . id ] . children )
2017-10-23 23:52:11 +00:00
2017-11-05 23:17:34 +00:00
dag . makeBlock ( " 2 " , { " 0 " : b0 } )
b2 = dag . blocks [ " 2 " ]
2017-10-23 23:52:11 +00:00
2017-11-05 23:17:34 +00:00
dag . makeBlock ( " 3 " , { " 1 " : b1 , " 2 " : b2 } )
b3 = dag . blocks [ " 3 " ]
dag . makeBlock ( " 4 " , { " 2 " : b2 } )
b4 = dag . blocks [ " 4 " ]
2017-10-23 23:52:11 +00:00
2017-11-05 23:17:34 +00:00
print ( dag . computeVote ( dag ) )
2017-10-23 23:52:11 +00:00
suite = unittest . TestLoader ( ) . loadTestsFromTestCase ( Test_BlockDAG )
unittest . TextTestRunner ( verbosity = 1 ) . run ( suite )