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
|
from BlockDAG import *
|
||||||
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]
|
|
||||||
|
|
||||||
class Spectre(object):
|
class Spectre(object):
|
||||||
""" Contains a BlockDAG, a dictionary of children, a dictionary
|
""" """
|
||||||
of observed pasts, a dictionary of observed votes, and a
|
def __init__(self):
|
||||||
dictionary keyToRep that takes a block ID as key and has as
|
self.dag = BlockDAG()
|
||||||
its value a representative block ID of some block with an
|
self.childRelation = {}
|
||||||
identical past."""
|
def setDAG(self, dagIn=None):
|
||||||
def __init__(self, dagIn=None):
|
|
||||||
if dagIn is None:
|
if dagIn is None:
|
||||||
self.dag = BlockDAG()
|
self.dag = BlockDAG()
|
||||||
else:
|
else:
|
||||||
self.dag = dagIn
|
self.dag = copy.deepcopy(dagIn)
|
||||||
self.children = {}
|
self.updateChildren() # self.childRelation is a dict: self.childRelation[key]={block with key in block.parents}
|
||||||
self.seenPasts = {}
|
|
||||||
self.seenVotes = {}
|
|
||||||
self.keyToRep = {}
|
|
||||||
def updateChildren(self):
|
def updateChildren(self):
|
||||||
""" Update children dictionary: if child is a block with some
|
self.childRelation = {}
|
||||||
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 = {}
|
|
||||||
q = deque()
|
q = deque()
|
||||||
self.updateChildren()
|
for blockID in self.dag.leaves:
|
||||||
if blockID in self.children:
|
q.append(blockID)
|
||||||
for ch in self.children[blockID]:
|
while len(q) > 0:
|
||||||
q.append(ch)
|
blockID = q.popleft()
|
||||||
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 parent in self.dag.blocks[blockID].parents:
|
for parent in self.dag.blocks[blockID].parents:
|
||||||
q.append(parent) # Start a queue with all the parents of blockID.
|
q.append(parent)
|
||||||
while len(q)>0: # Fill pastIDs with all past block IDs.
|
if parent not in self.childRelation:
|
||||||
nextID = q.popleft() # Take a block out of queue.
|
self.childRelation.update({parent:[]})
|
||||||
if nextID not in pastIDs: # Record this blockID into dictionary pastIDs
|
self.childRelation[parent].append(blockID)
|
||||||
pastIDs.update({nextID:nextID})
|
def vote(self, subdag=None):
|
||||||
for parent in self.dag.blocks[nextID].parents:
|
result = {}
|
||||||
q.append(parent) # For each parent of the current block, enqueue them.
|
if subdag is None:
|
||||||
# now queue is empty
|
result = self.vote(self.dag)
|
||||||
result = BlockDAG(genBlock=self.dag.genesisBlock) # Create dummy new BlockDAG
|
elif len(subdag.blocks)>1:
|
||||||
for child in self.children[self.dag.genesisBlock.id]:
|
canopy = []
|
||||||
if child in pastIDs:
|
nextdag = copy.deepcopy(subdag)
|
||||||
q.append(child) # Enqueue children of the genesis block that are in the past of blockID
|
while len(nextdag.blocks)>1:
|
||||||
while len(q)>0:
|
canopy.append(nextdag.leaves)
|
||||||
child = q.popleft() # Take a block out of queue.
|
nextdag = self.pruneLeaves(nextdag)
|
||||||
for ch in self.children[child]:
|
partialVotes = {}
|
||||||
if ch in pastIDs: # Enqueue children of the block that are in the past of blockID
|
for votingBlock in subdag.blocks:
|
||||||
q.append(ch)
|
partialVotes.update({votingBlock:{}})
|
||||||
if child not in result.blocks:
|
for layer in canopy:
|
||||||
result.addBlock(blockIn=self.dag.blocks[child]) # Include current block into dummy BlockDAG
|
for votingBlock in layer:
|
||||||
self.seenPasts.update({blockID:copy.deepcopy(result)}) # Include dummy BlockDAG into seenPasts
|
thisPast = self.getPast(votingBlock)
|
||||||
self.keyToRep.update({blockID:blockID}) # Include blockID in the equivalence class of blockID
|
recursiveVote = self.vote(thisPast)
|
||||||
return result # Return dummy BlockDAG
|
partialVotes[votingBlock] = copy.deepcopy(recursiveVote)
|
||||||
|
futureIDs = self.getFutureIDs(votingBlock)
|
||||||
def stripLeaves(self, subdag):
|
for blockX in subdag.blocks:
|
||||||
""" Takes as input a subdag and outputs a sub-subdag where all leaves from subdag have been deleted. """
|
for blockY in subdag.blocks:
|
||||||
result = BlockDAG(subdag.genesisBlock)
|
if blockX in thisPast.blocks and blockY in thisPast.blocks:
|
||||||
q = deque()
|
pass
|
||||||
q.append(subdag.genesisBlock.id)
|
elif blockX in thisPast.blocks and blockY not in thisPast.blocks:
|
||||||
while len(q)>0:
|
partialVotes[votingBlock].update({(blockX,votingBlock):True, (blockX, blockY):True, (votingBlock, blockY):True})
|
||||||
block = q.popleft()
|
elif blockX not in thisPast.blocks and blockY in thisPast.blocks:
|
||||||
if block not in subdag.leaves:
|
partialVotes[votingBlock].update({(blockY,votingBlock):True, (blockY,blockX):True, (votingBlock,blockX):True})
|
||||||
result.addBlock(subdag.blocks[block])
|
else:
|
||||||
return result
|
partialVotes[votingBlock].update({(votingBlock,blockX):True, (votingBlock,blockY):True})
|
||||||
|
s=0
|
||||||
def getMajorityOfFuture(self, votingBlock, futureIDs, blockX, blockY, partial):
|
for fid in futureIDs:
|
||||||
s = 0
|
if fid in subdag.blocks:
|
||||||
result = None
|
#print("partialvotes[fid]=",partialVotes[fid])
|
||||||
for fids in futureIDs:
|
if (blockX, blockY) in partialVotes[fid]:
|
||||||
if (fids,blockX,blockY) in partial:
|
|
||||||
s = s+1
|
s = s+1
|
||||||
elif (fids, blockY, blockX) in partial:
|
elif (blockY, blockX) in partialVotes[fid]:
|
||||||
s = s-1
|
s = s-1
|
||||||
if s > 0:
|
if s > 0:
|
||||||
result = (block,blockX,blockY)
|
partialVotes[votingBlock].update({(blockX,blockY):True})
|
||||||
elif s < 0:
|
elif s < 0:
|
||||||
result = (block,blockY,blockX)
|
partialVotes[votingBlock].update({(blockY,blockX):True})
|
||||||
return result
|
|
||||||
def getSum(self, result, subdag, partial):
|
|
||||||
for blockX in subdag.blocks:
|
for blockX in subdag.blocks:
|
||||||
for blockY in subdag.blocks:
|
for blockY in subdag.blocks:
|
||||||
s = 0
|
s = 0
|
||||||
for votingBlock in subdag.blocks:
|
for votingBlock in subdag.blocks:
|
||||||
if (votingBlock, blockX, blockY) in partial:
|
if (blockX, blockY) in partialVotes[votingBlock]:
|
||||||
s = s+1
|
s=s+1
|
||||||
elif (votingBlock, blockY, blockX) in partial:
|
elif (blockY, blockX) in partialVotes[votingBlock]:
|
||||||
s = s-1
|
s=s-1
|
||||||
if s > 0:
|
if s > 0:
|
||||||
result.update({(blockX,blockY):True})
|
result.update({(blockX,blockY):True})
|
||||||
elif s < 0:
|
elif s < 0:
|
||||||
result.update({(blockY, blockX):True})
|
result.update({(blockY,blockX):True})
|
||||||
return result
|
return result
|
||||||
def getVote(self, subdag=None):
|
def pruneLeaves(self, subdag):
|
||||||
""" This algorithm takes a subdag as input and computes how that subdag votes on its own interior order.
|
newsubdag = BlockDAG()
|
||||||
The output is a dictionary where all values are True and keys are of the form (blockX, blockY)
|
newsubdag.startDAG(subdag.genBlock.id, subdag.genBlock)
|
||||||
signifying that the network has decided blockX < blockY.
|
q = deque()
|
||||||
|
for child in self.childRelation[subdag.genBlock.id]:
|
||||||
The total vote of the subdag on any pair of blocks (blockX, blockY) is defined as the majority of votes
|
if child in subdag.blocks:
|
||||||
of the blocks in the subdag, i.e. the majority of votes of the form (votingBlock, blockX, blockY). Each
|
q.append(child)
|
||||||
votingBlock votes using the following rules:
|
while len(q) > 0:
|
||||||
(i) blocks from the past of votingBlock should precede votingBlock and blocks not from the past of votingBlock
|
nextBlock = q.popleft()
|
||||||
(ii) votingBlock should precede blocks not from the past of votingBlock
|
if nextBlock not in subdag.leaves:
|
||||||
(iii) votingBlock votes on pairs not from the past of votingBlock by majority of the future of votingBlock
|
newsubdag.addLeaf(blockIn=subdag.blocks[nextBlock])
|
||||||
(iv) votingBlock votes on pairs from the past of votingBlock by calling getVote on the past of votingBlock
|
if nextBlock in self.childRelation:
|
||||||
"""
|
if len(self.childRelation[nextBlock])>1:
|
||||||
result = {} # Dictionary initially empty.
|
for child in self.childRelation[nextBlock]:
|
||||||
if subdag is None:
|
if child in subdag.blocks:
|
||||||
subdag = copy.deepcopy(self.dag)
|
q.append(child)
|
||||||
for blockID in subdag.blocks:
|
return newsubdag
|
||||||
result.update({(blockID,blockID):True}) # All blocks vote reflexively: x <= x for each x
|
def addBlock(self, blockIn=None):
|
||||||
if len(subdag.blocks) > 1:
|
if blockIn == None:
|
||||||
partial = {} # This dictionary will have all True values and keys of the form (votingBlock, blockX, blockY)
|
parents = copy.deepcopy(self.dag.leaves)
|
||||||
for blockID in subdag.blocks:
|
blockID = str(len(self.dag)+1)
|
||||||
partial.update({(blockID,blockID,blockID):True}) # All votingBlocks vote reflexively for themselves.
|
blockIn = Block(blockID, parents)
|
||||||
|
|
||||||
# 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.
|
|
||||||
canopy = []
|
|
||||||
nextdag = copy.deepcopy(subdag)
|
|
||||||
while len(nextdag.blocks) > 1:
|
|
||||||
canopy.append(nextdag.leaves)
|
|
||||||
nextdag = self.stripLeaves(nextdag)
|
|
||||||
|
|
||||||
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:
|
else:
|
||||||
# In this case, we can't tell, maybe or maybe not: perhaps the past of
|
blockID = blockIn.id
|
||||||
# block has been computed, but the key self.keyToRep[block] has not
|
self.dag.addLeaf(blockIn)
|
||||||
# been determined yet.
|
for parent in blockIn.parents:
|
||||||
thisPast = self.getPast(block) # We first compute the past, see if it has been
|
if parent not in self.childRelation:
|
||||||
for key in self.seenPasts: # computed before at any other key. If so, we
|
self.childRelation.update({parent:[]})
|
||||||
if self.seenPasts[key]==thisPast: # can pull up the recursive vote and
|
self.childRelation[parent].append(blockID)
|
||||||
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
|
def getPastIDs(self, block):
|
||||||
futureIDs = self.getFutureIDs(block)
|
thisPast = {}
|
||||||
|
q = deque()
|
||||||
# STEP 3: For every pair of blocks, either both in the pair are in the past (see thisRecVote)
|
self.updateChildren()
|
||||||
# or one of the pair is in the past (inducing a natural order)
|
for parent in self.dag.blocks[block].parents:
|
||||||
# or both in the pair are not in the past (majority of votes from futureIDs)
|
q.append(parent)
|
||||||
for key in thisRecVote: # Extend thisRecVote into partial by taking each key of (blockX, blockY)
|
while len(q) > 0:
|
||||||
newVote = (block, copy.deepcopy(key[0]), copy.deepcopy(key[1])) # and inserting (votingBlock, blockX, blockY).
|
nextPastBlockID = q.popleft()
|
||||||
if newVote not in partial:
|
if nextPastBlockID not in thisPast:
|
||||||
partial.update({newVote:True})
|
thisPast.update({nextPastBlockID:self.dag.blocks[nextPastBlockID]})
|
||||||
for blockX in subdag.blocks:
|
for parent in self.dag.blocks[nextPastBlockID].parents:
|
||||||
for blockY in subdag.blocks:
|
q.append(parent)
|
||||||
# Since we took thisRecVote into account, we can disregard the case: "both in past"
|
return thisPast
|
||||||
if blockX in thisPast.blocks and blockY in thisPast.blocks:
|
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
|
pass
|
||||||
# If blockX is in the past of block and blockY is not, then block votes blockX < block < blockY
|
return thisFuture
|
||||||
elif blockX in thisPast.blocks and blockY not in thisPast.blocks:
|
def getPast(self, block):
|
||||||
partial.update({(block,blockX,block):True,(block,blockX,blockY):True,(block,block,blockY):True})
|
pastIDs = self.getPastIDs(block)
|
||||||
# If blockY is in the past of block and blockX is not, then block votes blockY < block < blockX
|
subdag = BlockDAG()
|
||||||
elif blockX not in thisPast.blocks and blockY in thisPast.blocks:
|
subdag.startDAG(idIn = self.dag.genBlock.id, genBlockIn = self.dag.genBlock)
|
||||||
partial.update({(block,blockY,block):True,(block,blockY,blockX):True,(block,block,blockX):True})
|
q = deque()
|
||||||
# If blockX and blockY not in the past, then....
|
for child in self.childRelation[self.dag.genBlock.id]:
|
||||||
elif blockX not in thisPast.blocks and blockY not in thisPast.blocks:
|
if child in pastIDs:
|
||||||
# Could be the that blockX=blockY=block.
|
q.append(child)
|
||||||
if blockX == blockY and blockX == block:
|
while len(q) > 0:
|
||||||
# partial.update({(block,block,block):True}) # Unnecessary, we already did this (line 150)
|
nextBlock = q.popleft()
|
||||||
pass
|
if nextBlock in pastIDs:
|
||||||
# Could be that blockY=block but blockX != block and blockX not in the past of block
|
subdag.addLeaf(self.dag.blocks[nextBlock])
|
||||||
# In this case, block votes that block < blockX.
|
for child in self.childRelation[nextBlock]:
|
||||||
elif blockX != block and blockY == block:
|
if child in pastIDs:
|
||||||
partial.update({(block,block,blockX):True})
|
q.append(child)
|
||||||
# Could be that blockX=block but blockY != block and blockY not in the past of block.
|
return subdag
|
||||||
# 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)
|
|
||||||
return result
|
|
||||||
|
|
||||||
class Test_Spectre(unittest.TestCase):
|
class Test_Spectre(unittest.TestCase):
|
||||||
def test_Spectre(self):
|
def test_Spectre(self):
|
||||||
# CREATE BLOCKCHAIN
|
shepard=Spectre()
|
||||||
genBlock = Block(idIn="0")
|
b0 = Block()
|
||||||
brock = BlockDAG(genBlock)
|
b0.setID("0")
|
||||||
shepard = Spectre(brock)
|
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)
|
b1 = Block()
|
||||||
shepard.addBlock(copy.deepcopy(newBlock))
|
b1.setParents(parentList={"0":b0})
|
||||||
vote = shepard.getVote()
|
b1.setID("1")
|
||||||
oldVote = copy.deepcopy(vote)
|
shepard.addBlock(b1)
|
||||||
self.assertTrue(("0","0") in vote)
|
|
||||||
self.assertTrue(("0","1") in vote)
|
|
||||||
self.assertTrue(("1","1") in vote)
|
|
||||||
|
|
||||||
genBlock = Block(idIn="0")
|
b2 = Block()
|
||||||
brock = BlockDAG(genBlock)
|
b2.setParents(parentList={"0":b0})
|
||||||
shepard = Spectre(brock)
|
b2.setID("2")
|
||||||
block1 = Block(idIn="1", parentList={genBlock.id:genBlock})
|
shepard.addBlock(b2)
|
||||||
block2 = Block(idIn="2", parentList={genBlock.id:genBlock})
|
|
||||||
block3 = Block(idIn="3", parentList={"1":block1, "2":block2})
|
b3 = Block()
|
||||||
block4 = Block(idIn="4", parentList={"2":block2})
|
b3.setParents(parentList={"1":b1, "2":b2})
|
||||||
shepard.addBlock(copy.deepcopy(block1))
|
b3.setID("3")
|
||||||
shepard.addBlock(copy.deepcopy(block2))
|
shepard.addBlock(b3)
|
||||||
shepard.addBlock(copy.deepcopy(block3))
|
|
||||||
shepard.addBlock(copy.deepcopy(block4))
|
b4 = Block()
|
||||||
vote = shepard.getVote()
|
b4.setParents(parentList={"2":b2})
|
||||||
print(vote)
|
b4.setID("4")
|
||||||
self.assertTrue(("0","2") in vote)
|
shepard.addBlock(b4)
|
||||||
self.assertTrue(("2", "1") in vote)
|
|
||||||
self.assertTrue(("1", "3") in vote)
|
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", "4") in vote)
|
self.assertTrue("3" in shepard.dag.leaves and "4" in shepard.dag.leaves)
|
||||||
self.assertFalse(("4", "2") in vote)
|
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)
|
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Spectre)
|
||||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue