mirror of
https://github.com/monero-project/research-lab.git
synced 2024-11-17 00:07:42 +00:00
Spectre passing unit tests!
This commit is contained in:
parent
6229c28243
commit
71dbe05174
3 changed files with 326 additions and 275 deletions
61
source-code/Spectre/Block.py
Normal file
61
source-code/Spectre/Block.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import unittest
|
||||
import math
|
||||
import numpy as np
|
||||
import copy
|
||||
from collections import deque
|
||||
import time
|
||||
|
||||
class Block(object):
|
||||
""" Fundamental object. Contains dict of blockIDs:(parent blocks) """
|
||||
def __init__(self, idIn=None, parentList=None):
|
||||
self.parents = {}
|
||||
self.id = ""
|
||||
self.data = None
|
||||
def setParents(self, parentList=None):
|
||||
if parentList is not None:
|
||||
self.parents = copy.deepcopy(parentList)
|
||||
def setID(self, idIn = None):
|
||||
if idIn is not None:
|
||||
self.id = copy.deepcopy(idIn)
|
||||
|
||||
class Test_Block(unittest.TestCase):
|
||||
def test_Block(self):
|
||||
b0 = Block()
|
||||
b0.setParents()
|
||||
b0.setID("0")
|
||||
self.assertTrue(b0.data is None)
|
||||
self.assertTrue(len(b0.parents)==0)
|
||||
|
||||
b1 = Block()
|
||||
b1.setParents(parentList={"0":b0})
|
||||
b1.setID("1")
|
||||
self.assertTrue(b1.data is None)
|
||||
self.assertTrue(len(b1.parents)==1)
|
||||
self.assertTrue("0" in b1.parents)
|
||||
|
||||
b2 = Block()
|
||||
b2.setParents(parentList={"0":b0})
|
||||
b2.setID("2")
|
||||
self.assertTrue(b2.data is None)
|
||||
self.assertTrue(len(b2.parents)==1)
|
||||
self.assertTrue("0" in b2.parents)
|
||||
|
||||
b3 = Block()
|
||||
b3.setParents(parentList={"1":b1, "2":b2})
|
||||
b3.setID("3")
|
||||
self.assertTrue(b3.data is None)
|
||||
self.assertTrue(len(b3.parents)==2)
|
||||
self.assertTrue("1" in b3.parents)
|
||||
self.assertTrue("2" in b3.parents)
|
||||
self.assertFalse("0" in b3.parents)
|
||||
|
||||
b4 = Block()
|
||||
b4.setParents(parentList={"2":b2})
|
||||
b4.setID("4")
|
||||
self.assertTrue(b4.data is None)
|
||||
self.assertTrue(len(b4.parents)==1)
|
||||
self.assertTrue("2" in b4.parents)
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
|
72
source-code/Spectre/BlockDAG.py
Normal file
72
source-code/Spectre/BlockDAG.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
from Block import *
|
||||
|
||||
class BlockDAG(object):
|
||||
""" Collection of >=1 block. Also tracks IDs of leaf blocks, adds new leaves. """
|
||||
def __init__(self):
|
||||
self.blocks = {}
|
||||
self.leaves = {}
|
||||
self.genBlock = None
|
||||
def startDAG(self, idIn=None, genBlockIn=None):
|
||||
if genBlockIn is not None:
|
||||
genesisBlock = genBlockIn
|
||||
else:
|
||||
genesisBlock = Block()
|
||||
if idIn is None:
|
||||
genesisBlock.setID(idIn="0")
|
||||
else:
|
||||
genesisBlock.setID(idIn)
|
||||
self.genBlock = genesisBlock
|
||||
self.blocks.update({self.genBlock.id:self.genBlock})
|
||||
self.leaves.update({self.genBlock.id:self.genBlock})
|
||||
def addLeaf(self, blockIn):
|
||||
self.blocks.update({blockIn.id:blockIn})
|
||||
self.leaves.update({blockIn.id:blockIn})
|
||||
for parent in blockIn.parents:
|
||||
if parent in self.leaves:
|
||||
del self.leaves[parent]
|
||||
|
||||
|
||||
class Test_BlockDAG(unittest.TestCase):
|
||||
def test_BlockDAG(self):
|
||||
dag = BlockDAG()
|
||||
dag.startDAG()
|
||||
self.assertTrue("0" in dag.blocks)
|
||||
self.assertTrue("0" in dag.leaves)
|
||||
self.assertTrue(len(dag.blocks)==1)
|
||||
self.assertTrue(len(dag.leaves)==1)
|
||||
b0 = dag.genBlock
|
||||
|
||||
b1 = Block()
|
||||
b1.setParents(parentList={"0":b0})
|
||||
b1.setID("1")
|
||||
dag.addLeaf(b1)
|
||||
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)
|
||||
|
||||
|
||||
b2 = Block()
|
||||
b2.setParents(parentList={"0":b0})
|
||||
b2.setID("2")
|
||||
dag.addLeaf(b2)
|
||||
|
||||
b3 = Block()
|
||||
b3.setParents(parentList={"1":b1, "2":b2})
|
||||
b3.setID("3")
|
||||
dag.addLeaf(b3)
|
||||
|
||||
b4 = Block()
|
||||
b4.setParents(parentList={"2":b2})
|
||||
b4.setID("4")
|
||||
dag.addLeaf(b4)
|
||||
|
||||
self.assertTrue("0" in dag.blocks and "1" in dag.blocks and "2" in dag.blocks and "3" in dag.blocks and "4" in dag.blocks)
|
||||
self.assertTrue("3" in dag.leaves and "4" in dag.leaves)
|
||||
self.assertTrue(len(dag.blocks)==5 and len(dag.leaves)==2)
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(Test_BlockDAG)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
|
|
@ -1,298 +1,216 @@
|
|||
import unittest
|
||||
import math
|
||||
import numpy as np
|
||||
import copy
|
||||
from collections import deque
|
||||
import time
|
||||
|
||||
class Block(object):
|
||||
""" Fundamental object. Contains dict of pointers to parent blocks. """
|
||||
def __init__(self, idIn=None, parentList=None):
|
||||
if parentList is None:
|
||||
self.parents = {}
|
||||
else:
|
||||
self.parents = parentList
|
||||
self.id = copy.deepcopy(idIn) # Hash of payload || header || extra2 and anything else i want to add later.
|
||||
self.data = None
|
||||
|
||||
class BlockDAG(object):
|
||||
""" Set of blocks (self.blocks), a distinguished genesis block, and a subset of leaf blocks (self.leaves)"""
|
||||
def __init__(self, genBlock=None):
|
||||
""" Creates vanilla BlockDAG with blank genesis block"""
|
||||
self.blocks = {}
|
||||
self.leaves = {}
|
||||
self.genesisBlock = None
|
||||
if genBlock is None:
|
||||
self.genesisBlock = Block()
|
||||
else:
|
||||
self.genesisBlock = copy.deepcopy(genBlock)
|
||||
self.blocks.update({copy.deepcopy(self.genesisBlock.id):self.genesisBlock})
|
||||
self.leaves.update({copy.deepcopy(self.genesisBlock.id):self.genesisBlock})
|
||||
def addBlock(self, blockIn):
|
||||
""" Add new leaf blockIn to BlockDAG, update leafset """
|
||||
self.blocks.update({blockIn.id:blockIn})
|
||||
self.leaves.update({blockIn.id:blockIn})
|
||||
q = deque()
|
||||
for parent in blockIn.parents:
|
||||
if parent in self.leaves:
|
||||
q.append(parent)
|
||||
while len(q)>0:
|
||||
parent = q.popleft()
|
||||
del self.leaves[parent]
|
||||
from BlockDAG import *
|
||||
|
||||
class Spectre(object):
|
||||
""" Contains a BlockDAG, a dictionary of children, a dictionary
|
||||
of observed pasts, a dictionary of observed votes, and a
|
||||
dictionary keyToRep that takes a block ID as key and has as
|
||||
its value a representative block ID of some block with an
|
||||
identical past."""
|
||||
def __init__(self, dagIn=None):
|
||||
""" """
|
||||
def __init__(self):
|
||||
self.dag = BlockDAG()
|
||||
self.childRelation = {}
|
||||
def setDAG(self, dagIn=None):
|
||||
if dagIn is None:
|
||||
self.dag = BlockDAG()
|
||||
else:
|
||||
self.dag = dagIn
|
||||
self.children = {}
|
||||
self.seenPasts = {}
|
||||
self.seenVotes = {}
|
||||
self.keyToRep = {}
|
||||
self.dag = copy.deepcopy(dagIn)
|
||||
self.updateChildren() # self.childRelation is a dict: self.childRelation[key]={block with key in block.parents}
|
||||
def updateChildren(self):
|
||||
""" Update children dictionary: if child is a block with some
|
||||
parent, ensures that child is in self.children[parent]."""
|
||||
for child in self.dag.blocks:
|
||||
for parent in self.dag.blocks[child].parents:
|
||||
if parent not in self.children:
|
||||
self.children.update({parent:[]})
|
||||
self.children[parent].append(child)
|
||||
def addBlock(self, blockIn):
|
||||
""" Takes block as input and includes it into the DAG and updates children."""
|
||||
self.dag.addBlock(blockIn)
|
||||
self.updateChildren()
|
||||
def getFutureIDs(self, blockID):
|
||||
""" Returns a dict of all block IDs of blocks in the future of blockID """
|
||||
result = {}
|
||||
self.childRelation = {}
|
||||
q = deque()
|
||||
self.updateChildren()
|
||||
if blockID in self.children:
|
||||
for ch in self.children[blockID]:
|
||||
q.append(ch)
|
||||
while len(q)>0:
|
||||
ch = q.popleft()
|
||||
if ch not in result:
|
||||
result.update({ch:ch})
|
||||
return result
|
||||
|
||||
def getPast(self, blockID):
|
||||
""" Returns a sub-BlockDAG() with all the blocks from the past of blockID.
|
||||
This method is made marginally more efficient using keyToRep and only
|
||||
comping votes once for each given history. """
|
||||
result = None # This will be the result returned. If we get None something went wrong.
|
||||
if blockID in self.keyToRep: # In this case, we have computed the past before.
|
||||
result = self.seenPasts[self.keyToRep[blockID]]
|
||||
else: # In this case, we must compute the past.
|
||||
q = deque()
|
||||
pastIDs = {} # This is just a dictionary of block IDs we will check against.
|
||||
for blockID in self.dag.leaves:
|
||||
q.append(blockID)
|
||||
while len(q) > 0:
|
||||
blockID = q.popleft()
|
||||
for parent in self.dag.blocks[blockID].parents:
|
||||
q.append(parent) # Start a queue with all the parents of blockID.
|
||||
while len(q)>0: # Fill pastIDs with all past block IDs.
|
||||
nextID = q.popleft() # Take a block out of queue.
|
||||
if nextID not in pastIDs: # Record this blockID into dictionary pastIDs
|
||||
pastIDs.update({nextID:nextID})
|
||||
for parent in self.dag.blocks[nextID].parents:
|
||||
q.append(parent) # For each parent of the current block, enqueue them.
|
||||
# now queue is empty
|
||||
result = BlockDAG(genBlock=self.dag.genesisBlock) # Create dummy new BlockDAG
|
||||
for child in self.children[self.dag.genesisBlock.id]:
|
||||
if child in pastIDs:
|
||||
q.append(child) # Enqueue children of the genesis block that are in the past of blockID
|
||||
while len(q)>0:
|
||||
child = q.popleft() # Take a block out of queue.
|
||||
for ch in self.children[child]:
|
||||
if ch in pastIDs: # Enqueue children of the block that are in the past of blockID
|
||||
q.append(ch)
|
||||
if child not in result.blocks:
|
||||
result.addBlock(blockIn=self.dag.blocks[child]) # Include current block into dummy BlockDAG
|
||||
self.seenPasts.update({blockID:copy.deepcopy(result)}) # Include dummy BlockDAG into seenPasts
|
||||
self.keyToRep.update({blockID:blockID}) # Include blockID in the equivalence class of blockID
|
||||
return result # Return dummy BlockDAG
|
||||
|
||||
def stripLeaves(self, subdag):
|
||||
""" Takes as input a subdag and outputs a sub-subdag where all leaves from subdag have been deleted. """
|
||||
result = BlockDAG(subdag.genesisBlock)
|
||||
q = deque()
|
||||
q.append(subdag.genesisBlock.id)
|
||||
while len(q)>0:
|
||||
block = q.popleft()
|
||||
if block not in subdag.leaves:
|
||||
result.addBlock(subdag.blocks[block])
|
||||
return result
|
||||
|
||||
def getMajorityOfFuture(self, votingBlock, futureIDs, blockX, blockY, partial):
|
||||
s = 0
|
||||
result = None
|
||||
for fids in futureIDs:
|
||||
if (fids,blockX,blockY) in partial:
|
||||
s = s+1
|
||||
elif (fids, blockY, blockX) in partial:
|
||||
s = s-1
|
||||
if s > 0:
|
||||
result = (block,blockX,blockY)
|
||||
elif s < 0:
|
||||
result = (block,blockY,blockX)
|
||||
return result
|
||||
def getSum(self, result, subdag, partial):
|
||||
for blockX in subdag.blocks:
|
||||
for blockY in subdag.blocks:
|
||||
s = 0
|
||||
for votingBlock in subdag.blocks:
|
||||
if (votingBlock, blockX, blockY) in partial:
|
||||
s = s+1
|
||||
elif (votingBlock, blockY, blockX) in partial:
|
||||
s = s-1
|
||||
if s > 0:
|
||||
result.update({(blockX,blockY):True})
|
||||
elif s < 0:
|
||||
result.update({(blockY, blockX):True})
|
||||
return result
|
||||
def getVote(self, subdag=None):
|
||||
""" This algorithm takes a subdag as input and computes how that subdag votes on its own interior order.
|
||||
The output is a dictionary where all values are True and keys are of the form (blockX, blockY)
|
||||
signifying that the network has decided blockX < blockY.
|
||||
|
||||
The total vote of the subdag on any pair of blocks (blockX, blockY) is defined as the majority of votes
|
||||
of the blocks in the subdag, i.e. the majority of votes of the form (votingBlock, blockX, blockY). Each
|
||||
votingBlock votes using the following rules:
|
||||
(i) blocks from the past of votingBlock should precede votingBlock and blocks not from the past of votingBlock
|
||||
(ii) votingBlock should precede blocks not from the past of votingBlock
|
||||
(iii) votingBlock votes on pairs not from the past of votingBlock by majority of the future of votingBlock
|
||||
(iv) votingBlock votes on pairs from the past of votingBlock by calling getVote on the past of votingBlock
|
||||
"""
|
||||
result = {} # Dictionary initially empty.
|
||||
q.append(parent)
|
||||
if parent not in self.childRelation:
|
||||
self.childRelation.update({parent:[]})
|
||||
self.childRelation[parent].append(blockID)
|
||||
def vote(self, subdag=None):
|
||||
result = {}
|
||||
if subdag is None:
|
||||
subdag = copy.deepcopy(self.dag)
|
||||
for blockID in subdag.blocks:
|
||||
result.update({(blockID,blockID):True}) # All blocks vote reflexively: x <= x for each x
|
||||
if len(subdag.blocks) > 1:
|
||||
partial = {} # This dictionary will have all True values and keys of the form (votingBlock, blockX, blockY)
|
||||
for blockID in subdag.blocks:
|
||||
partial.update({(blockID,blockID,blockID):True}) # All votingBlocks vote reflexively for themselves.
|
||||
|
||||
# We are first going to compute votes for leaves. Then, we will compute votes for the blocks
|
||||
# that would be leaves if all leaves of the current BlockDAG were to be deleted. We repeat
|
||||
# this as expected until we only have the genesis block remaining. We call the structure canopy.
|
||||
result = self.vote(self.dag)
|
||||
elif len(subdag.blocks)>1:
|
||||
canopy = []
|
||||
nextdag = copy.deepcopy(subdag)
|
||||
while len(nextdag.blocks) > 1:
|
||||
while len(nextdag.blocks)>1:
|
||||
canopy.append(nextdag.leaves)
|
||||
nextdag = self.stripLeaves(nextdag)
|
||||
|
||||
nextdag = self.pruneLeaves(nextdag)
|
||||
partialVotes = {}
|
||||
for votingBlock in subdag.blocks:
|
||||
partialVotes.update({votingBlock:{}})
|
||||
for layer in canopy:
|
||||
for block in layer: # Compute votes from leaf-to-root as described above
|
||||
|
||||
# STEP 1: Get recursive vote, store in thisRecVote.
|
||||
if block in self.keyToRep:
|
||||
# In this case, the past of block and its vote have been computed already
|
||||
# and stored with key self.keyToRep[block] in self.seenPasts
|
||||
# and self.seenVotes, respectively
|
||||
thisPast = self.seenPasts[self.keyToRep[block]]
|
||||
thisRecVote = self.seenVotes[self.keyToRep[block]]
|
||||
else:
|
||||
# In this case, we can't tell, maybe or maybe not: perhaps the past of
|
||||
# block has been computed, but the key self.keyToRep[block] has not
|
||||
# been determined yet.
|
||||
thisPast = self.getPast(block) # We first compute the past, see if it has been
|
||||
for key in self.seenPasts: # computed before at any other key. If so, we
|
||||
if self.seenPasts[key]==thisPast: # can pull up the recursive vote and
|
||||
self.keyToRep.update({block:key}) # update the keyToRep to note the
|
||||
thisRecVote = self.seenVotes[key] # alternative key.
|
||||
break
|
||||
else: # We enter this case if we did not find any past that matches thisPast.
|
||||
self.keyToRep.update({block:block}) # In this case, keyToRep is identity
|
||||
thisRecVote = self.getVote(thisPast) # And we recursively compute the vote
|
||||
self.seenVotes.update({self.keyToRep[block]:thisRecVote}) # then we store
|
||||
self.seenPasts.update({self.keyToRep[block]:thisPast}) # the vote and the past.
|
||||
|
||||
# STEP 2: Get block IDs that have a vote on pairs from the past of block
|
||||
futureIDs = self.getFutureIDs(block)
|
||||
|
||||
# STEP 3: For every pair of blocks, either both in the pair are in the past (see thisRecVote)
|
||||
# or one of the pair is in the past (inducing a natural order)
|
||||
# or both in the pair are not in the past (majority of votes from futureIDs)
|
||||
for key in thisRecVote: # Extend thisRecVote into partial by taking each key of (blockX, blockY)
|
||||
newVote = (block, copy.deepcopy(key[0]), copy.deepcopy(key[1])) # and inserting (votingBlock, blockX, blockY).
|
||||
if newVote not in partial:
|
||||
partial.update({newVote:True})
|
||||
for blockX in subdag.blocks:
|
||||
for blockY in subdag.blocks:
|
||||
# Since we took thisRecVote into account, we can disregard the case: "both in past"
|
||||
if blockX in thisPast.blocks and blockY in thisPast.blocks:
|
||||
pass
|
||||
# If blockX is in the past of block and blockY is not, then block votes blockX < block < blockY
|
||||
elif blockX in thisPast.blocks and blockY not in thisPast.blocks:
|
||||
partial.update({(block,blockX,block):True,(block,blockX,blockY):True,(block,block,blockY):True})
|
||||
# If blockY is in the past of block and blockX is not, then block votes blockY < block < blockX
|
||||
elif blockX not in thisPast.blocks and blockY in thisPast.blocks:
|
||||
partial.update({(block,blockY,block):True,(block,blockY,blockX):True,(block,block,blockX):True})
|
||||
# If blockX and blockY not in the past, then....
|
||||
elif blockX not in thisPast.blocks and blockY not in thisPast.blocks:
|
||||
# Could be the that blockX=blockY=block.
|
||||
if blockX == blockY and blockX == block:
|
||||
# partial.update({(block,block,block):True}) # Unnecessary, we already did this (line 150)
|
||||
pass
|
||||
# Could be that blockY=block but blockX != block and blockX not in the past of block
|
||||
# In this case, block votes that block < blockX.
|
||||
elif blockX != block and blockY == block:
|
||||
partial.update({(block,block,blockX):True})
|
||||
# Could be that blockX=block but blockY != block and blockY not in the past of block.
|
||||
# In this case, block votes that block < blockY
|
||||
elif blockX == block and blockY != block:
|
||||
partial.update({(block, block, blockY):True})
|
||||
# Last case: blockX != block != blockY, use majority of future blocks.
|
||||
elif blockX != block and blockY != block:
|
||||
maj = self.getMajorityOfFuture(block, futureIDs, blockX, blockY, partial)
|
||||
if maj is not None:
|
||||
partial.update({maj:True})
|
||||
else:
|
||||
print("DOOM AND GLOOM HOW DID YOU FIND YOURSELF HERE YOUNG CHILD?")
|
||||
for key in partial:
|
||||
print("Key = ", key, ", \t, val = ", partial[key])
|
||||
result = self.getSum(result, subdag, partial)
|
||||
for votingBlock in layer:
|
||||
thisPast = self.getPast(votingBlock)
|
||||
recursiveVote = self.vote(thisPast)
|
||||
partialVotes[votingBlock] = copy.deepcopy(recursiveVote)
|
||||
futureIDs = self.getFutureIDs(votingBlock)
|
||||
for blockX in subdag.blocks:
|
||||
for blockY in subdag.blocks:
|
||||
if blockX in thisPast.blocks and blockY in thisPast.blocks:
|
||||
pass
|
||||
elif blockX in thisPast.blocks and blockY not in thisPast.blocks:
|
||||
partialVotes[votingBlock].update({(blockX,votingBlock):True, (blockX, blockY):True, (votingBlock, blockY):True})
|
||||
elif blockX not in thisPast.blocks and blockY in thisPast.blocks:
|
||||
partialVotes[votingBlock].update({(blockY,votingBlock):True, (blockY,blockX):True, (votingBlock,blockX):True})
|
||||
else:
|
||||
partialVotes[votingBlock].update({(votingBlock,blockX):True, (votingBlock,blockY):True})
|
||||
s=0
|
||||
for fid in futureIDs:
|
||||
if fid in subdag.blocks:
|
||||
#print("partialvotes[fid]=",partialVotes[fid])
|
||||
if (blockX, blockY) in partialVotes[fid]:
|
||||
s = s+1
|
||||
elif (blockY, blockX) in partialVotes[fid]:
|
||||
s = s-1
|
||||
if s > 0:
|
||||
partialVotes[votingBlock].update({(blockX,blockY):True})
|
||||
elif s < 0:
|
||||
partialVotes[votingBlock].update({(blockY,blockX):True})
|
||||
for blockX in subdag.blocks:
|
||||
for blockY in subdag.blocks:
|
||||
s = 0
|
||||
for votingBlock in subdag.blocks:
|
||||
if (blockX, blockY) in partialVotes[votingBlock]:
|
||||
s=s+1
|
||||
elif (blockY, blockX) in partialVotes[votingBlock]:
|
||||
s=s-1
|
||||
if s > 0:
|
||||
result.update({(blockX,blockY):True})
|
||||
elif s < 0:
|
||||
result.update({(blockY,blockX):True})
|
||||
return result
|
||||
def pruneLeaves(self, subdag):
|
||||
newsubdag = BlockDAG()
|
||||
newsubdag.startDAG(subdag.genBlock.id, subdag.genBlock)
|
||||
q = deque()
|
||||
for child in self.childRelation[subdag.genBlock.id]:
|
||||
if child in subdag.blocks:
|
||||
q.append(child)
|
||||
while len(q) > 0:
|
||||
nextBlock = q.popleft()
|
||||
if nextBlock not in subdag.leaves:
|
||||
newsubdag.addLeaf(blockIn=subdag.blocks[nextBlock])
|
||||
if nextBlock in self.childRelation:
|
||||
if len(self.childRelation[nextBlock])>1:
|
||||
for child in self.childRelation[nextBlock]:
|
||||
if child in subdag.blocks:
|
||||
q.append(child)
|
||||
return newsubdag
|
||||
def addBlock(self, blockIn=None):
|
||||
if blockIn == None:
|
||||
parents = copy.deepcopy(self.dag.leaves)
|
||||
blockID = str(len(self.dag)+1)
|
||||
blockIn = Block(blockID, parents)
|
||||
else:
|
||||
blockID = blockIn.id
|
||||
self.dag.addLeaf(blockIn)
|
||||
for parent in blockIn.parents:
|
||||
if parent not in self.childRelation:
|
||||
self.childRelation.update({parent:[]})
|
||||
self.childRelation[parent].append(blockID)
|
||||
|
||||
def getPastIDs(self, block):
|
||||
thisPast = {}
|
||||
q = deque()
|
||||
self.updateChildren()
|
||||
for parent in self.dag.blocks[block].parents:
|
||||
q.append(parent)
|
||||
while len(q) > 0:
|
||||
nextPastBlockID = q.popleft()
|
||||
if nextPastBlockID not in thisPast:
|
||||
thisPast.update({nextPastBlockID:self.dag.blocks[nextPastBlockID]})
|
||||
for parent in self.dag.blocks[nextPastBlockID].parents:
|
||||
q.append(parent)
|
||||
return thisPast
|
||||
def getFutureIDs(self, block):
|
||||
thisFuture = {}
|
||||
q = deque()
|
||||
self.updateChildren()
|
||||
if block in self.childRelation:
|
||||
# If this is the case, then block has at least one child.
|
||||
for child in self.childRelation[block]:
|
||||
q.append(child)
|
||||
while len(q) > 0:
|
||||
nextFutureBlockID = q.popleft()
|
||||
if nextFutureBlockID not in thisFuture:
|
||||
thisFuture.update({nextFutureBlockID:self.dag.blocks[nextFutureBlockID]})
|
||||
if nextFutureBlockID in self.childRelation:
|
||||
if len(self.childRelation[nextFutureBlockID]) > 0:
|
||||
for child in self.childRelation[nextFutureBlockID]:
|
||||
q.append(child)
|
||||
else: # In this case, block has no children, so futureIDs should be empty.
|
||||
pass
|
||||
return thisFuture
|
||||
def getPast(self, block):
|
||||
pastIDs = self.getPastIDs(block)
|
||||
subdag = BlockDAG()
|
||||
subdag.startDAG(idIn = self.dag.genBlock.id, genBlockIn = self.dag.genBlock)
|
||||
q = deque()
|
||||
for child in self.childRelation[self.dag.genBlock.id]:
|
||||
if child in pastIDs:
|
||||
q.append(child)
|
||||
while len(q) > 0:
|
||||
nextBlock = q.popleft()
|
||||
if nextBlock in pastIDs:
|
||||
subdag.addLeaf(self.dag.blocks[nextBlock])
|
||||
for child in self.childRelation[nextBlock]:
|
||||
if child in pastIDs:
|
||||
q.append(child)
|
||||
return subdag
|
||||
|
||||
class Test_Spectre(unittest.TestCase):
|
||||
def test_Spectre(self):
|
||||
# CREATE BLOCKCHAIN
|
||||
genBlock = Block(idIn="0")
|
||||
brock = BlockDAG(genBlock)
|
||||
shepard = Spectre(brock)
|
||||
shepard=Spectre()
|
||||
b0 = Block()
|
||||
b0.setID("0")
|
||||
shepard.dag.startDAG(idIn="0", genBlockIn=b0)
|
||||
self.assertTrue(len(shepard.dag.leaves)==1 and len(shepard.dag.blocks)==1)
|
||||
|
||||
newBlock = Block(idIn="1", parentList=shepard.dag.leaves)
|
||||
shepard.addBlock(copy.deepcopy(newBlock))
|
||||
vote = shepard.getVote()
|
||||
oldVote = copy.deepcopy(vote)
|
||||
self.assertTrue(("0","0") in vote)
|
||||
self.assertTrue(("0","1") in vote)
|
||||
self.assertTrue(("1","1") in vote)
|
||||
b1 = Block()
|
||||
b1.setParents(parentList={"0":b0})
|
||||
b1.setID("1")
|
||||
shepard.addBlock(b1)
|
||||
|
||||
genBlock = Block(idIn="0")
|
||||
brock = BlockDAG(genBlock)
|
||||
shepard = Spectre(brock)
|
||||
block1 = Block(idIn="1", parentList={genBlock.id:genBlock})
|
||||
block2 = Block(idIn="2", parentList={genBlock.id:genBlock})
|
||||
block3 = Block(idIn="3", parentList={"1":block1, "2":block2})
|
||||
block4 = Block(idIn="4", parentList={"2":block2})
|
||||
shepard.addBlock(copy.deepcopy(block1))
|
||||
shepard.addBlock(copy.deepcopy(block2))
|
||||
shepard.addBlock(copy.deepcopy(block3))
|
||||
shepard.addBlock(copy.deepcopy(block4))
|
||||
vote = shepard.getVote()
|
||||
print(vote)
|
||||
self.assertTrue(("0","2") in vote)
|
||||
self.assertTrue(("2", "1") in vote)
|
||||
self.assertTrue(("1", "3") in vote)
|
||||
self.assertTrue(("3", "4") in vote)
|
||||
self.assertFalse(("4", "2") in vote)
|
||||
b2 = Block()
|
||||
b2.setParents(parentList={"0":b0})
|
||||
b2.setID("2")
|
||||
shepard.addBlock(b2)
|
||||
|
||||
b3 = Block()
|
||||
b3.setParents(parentList={"1":b1, "2":b2})
|
||||
b3.setID("3")
|
||||
shepard.addBlock(b3)
|
||||
|
||||
b4 = Block()
|
||||
b4.setParents(parentList={"2":b2})
|
||||
b4.setID("4")
|
||||
shepard.addBlock(b4)
|
||||
|
||||
self.assertTrue("0" in shepard.dag.blocks and "1" in shepard.dag.blocks and "2" in shepard.dag.blocks and "3" in shepard.dag.blocks and "4" in shepard.dag.blocks)
|
||||
self.assertTrue("3" in shepard.dag.leaves and "4" in shepard.dag.leaves)
|
||||
self.assertTrue(len(shepard.dag.blocks)==5 and len(shepard.dag.leaves)==2)
|
||||
self.assertTrue("0" in shepard.childRelation and "1" in shepard.childRelation and "2" in shepard.childRelation)
|
||||
self.assertFalse("3" in shepard.childRelation)
|
||||
self.assertFalse("4" in shepard.childRelation)
|
||||
self.assertTrue("1" in shepard.childRelation["0"] and "2" in shepard.childRelation["0"])
|
||||
self.assertTrue("3" in shepard.childRelation["1"])
|
||||
self.assertTrue("3" in shepard.childRelation["2"] and "4" in shepard.childRelation["2"])
|
||||
self.assertFalse("4" in shepard.childRelation["1"])
|
||||
self.assertFalse("0" in shepard.childRelation["1"])
|
||||
|
||||
vote = shepard.vote()
|
||||
#print(vote)
|
||||
self.assertTrue(("0", "1") in vote and \
|
||||
("0", "2") in vote and \
|
||||
("0", "3") in vote and \
|
||||
("0", "4") in vote and \
|
||||
("2", "1") in vote and \
|
||||
("2", "3") in vote and \
|
||||
("2", "4") in vote and \
|
||||
("1", "3") in vote and \
|
||||
("1", "4") in vote and \
|
||||
("3", "4") in vote)
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Spectre)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue