mirror of
https://github.com/monero-project/research-lab.git
synced 2024-12-22 19:49:35 +00:00
commit
442b4836af
4 changed files with 241 additions and 283 deletions
|
@ -7,42 +7,56 @@ import time
|
||||||
|
|
||||||
class Block(object):
|
class Block(object):
|
||||||
""" Fundamental object. Contains dict of blockIDs:(parent blocks) """
|
""" Fundamental object. Contains dict of blockIDs:(parent blocks) """
|
||||||
def __init__(self, idIn=None, parentList=None):
|
def __init__(self):
|
||||||
self.parents = {}
|
self.id = "" # string
|
||||||
self.id = ""
|
self.timestamp = None # format tbd
|
||||||
self.data = None
|
self.data = None # payload
|
||||||
def setParents(self, parentList=None):
|
self.parents = {} # block ID : pointer to block
|
||||||
if parentList is not None:
|
self.children = {} # block ID : pointer to block
|
||||||
self.parents = copy.deepcopy(parentList)
|
def addChild(self, childIn):
|
||||||
def setID(self, idIn = None):
|
if childIn not in self.children:
|
||||||
if idIn is not None:
|
self.children.update({childIn.id:childIn})
|
||||||
self.id = copy.deepcopy(idIn)
|
def addChildren(self, childrenIn):
|
||||||
|
for child in childrenIn:
|
||||||
|
self.addChild(childrenIn[child])
|
||||||
|
def addParent(self, parentIn):
|
||||||
|
if parentIn not in self.parents:
|
||||||
|
self.parents.update({parentIn.id:parentIn})
|
||||||
|
def addParents(self, parentsIn):
|
||||||
|
for parent in parentsIn:
|
||||||
|
self.addParent(parentsIn[parent])
|
||||||
|
|
||||||
|
|
||||||
class Test_Block(unittest.TestCase):
|
class Test_Block(unittest.TestCase):
|
||||||
def test_Block(self):
|
def test_Block(self):
|
||||||
b0 = Block()
|
b0 = Block()
|
||||||
b0.setParents()
|
b0.id = "0"
|
||||||
b0.setID("0")
|
|
||||||
self.assertTrue(b0.data is None)
|
self.assertTrue(b0.data is None)
|
||||||
self.assertTrue(len(b0.parents)==0)
|
self.assertTrue(len(b0.parents)==0)
|
||||||
|
|
||||||
b1 = Block()
|
b1 = Block()
|
||||||
b1.setParents(parentList={"0":b0})
|
b1.parents.update({"0":b0})
|
||||||
b1.setID("1")
|
b1.id = "1"
|
||||||
|
for parentID in b1.parents:
|
||||||
|
b1.parents[parentID].children.update({b1.id:b1})
|
||||||
self.assertTrue(b1.data is None)
|
self.assertTrue(b1.data is None)
|
||||||
self.assertTrue(len(b1.parents)==1)
|
self.assertTrue(len(b1.parents)==1)
|
||||||
self.assertTrue("0" in b1.parents)
|
self.assertTrue("0" in b1.parents)
|
||||||
|
|
||||||
b2 = Block()
|
b2 = Block()
|
||||||
b2.setParents(parentList={"0":b0})
|
b2.parents.update({"0":b0})
|
||||||
b2.setID("2")
|
b2.id = "2"
|
||||||
|
for parentID in b2.parents:
|
||||||
|
b2.parents[parentID].children.update({b2.id:b2})
|
||||||
self.assertTrue(b2.data is None)
|
self.assertTrue(b2.data is None)
|
||||||
self.assertTrue(len(b2.parents)==1)
|
self.assertTrue(len(b2.parents)==1)
|
||||||
self.assertTrue("0" in b2.parents)
|
self.assertTrue("0" in b2.parents)
|
||||||
|
|
||||||
b3 = Block()
|
b3 = Block()
|
||||||
b3.setParents(parentList={"1":b1, "2":b2})
|
b3.parents.update({"1":b1, "2":b2})
|
||||||
b3.setID("3")
|
b3.id = "3"
|
||||||
|
for parentID in b3.parents:
|
||||||
|
b3.parents[parentID].children.update({b3.id:b3})
|
||||||
self.assertTrue(b3.data is None)
|
self.assertTrue(b3.data is None)
|
||||||
self.assertTrue(len(b3.parents)==2)
|
self.assertTrue(len(b3.parents)==2)
|
||||||
self.assertTrue("1" in b3.parents)
|
self.assertTrue("1" in b3.parents)
|
||||||
|
@ -50,8 +64,10 @@ class Test_Block(unittest.TestCase):
|
||||||
self.assertFalse("0" in b3.parents)
|
self.assertFalse("0" in b3.parents)
|
||||||
|
|
||||||
b4 = Block()
|
b4 = Block()
|
||||||
b4.setParents(parentList={"2":b2})
|
b4.parents.update({"2":b2})
|
||||||
b4.setID("4")
|
b4.id = "4"
|
||||||
|
for parentID in b4.parents:
|
||||||
|
b4.parents[parentID].children.update({b4.id:b4})
|
||||||
self.assertTrue(b4.data is None)
|
self.assertTrue(b4.data is None)
|
||||||
self.assertTrue(len(b4.parents)==1)
|
self.assertTrue(len(b4.parents)==1)
|
||||||
self.assertTrue("2" in b4.parents)
|
self.assertTrue("2" in b4.parents)
|
||||||
|
|
|
@ -1,72 +1,227 @@
|
||||||
from Block import *
|
from Block import *
|
||||||
|
#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####
|
||||||
class BlockDAG(object):
|
class BlockDAG(object):
|
||||||
""" Collection of >=1 block. Also tracks IDs of leaf blocks, adds new leaves. """
|
""" Collection of >=1 block. """
|
||||||
def __init__(self):
|
def __init__(self, params=None):
|
||||||
self.blocks = {}
|
self.genesis = Block()
|
||||||
self.leaves = {}
|
self.genesis.id = "0"
|
||||||
self.genBlock = None
|
self.blocks = {self.genesis.id:self.genesis}
|
||||||
def startDAG(self, idIn=None, genBlockIn=None):
|
self.leaves = {self.genesis.id:self.genesis}
|
||||||
if genBlockIn is not None:
|
|
||||||
genesisBlock = genBlockIn
|
# 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
|
||||||
else:
|
else:
|
||||||
genesisBlock = Block()
|
self.security = 10
|
||||||
if idIn is None:
|
self.vote = {}
|
||||||
genesisBlock.setID(idIn="0")
|
self.pending = {}
|
||||||
else:
|
for blockZ in self.votBlocks:
|
||||||
genesisBlock.setID(idIn)
|
for blockX in self.ordBlocks:
|
||||||
self.genBlock = genesisBlock
|
for blockY in self.ordBlocks:
|
||||||
self.blocks.update({self.genBlock.id:self.genBlock})
|
self.vote.update({(blockZ,blockX,blockY):0})
|
||||||
self.leaves.update({self.genBlock.id:self.genBlock})
|
self.pending.update({(blockZ,blockX,blockY):0})
|
||||||
def addLeaf(self, blockIn):
|
|
||||||
self.blocks.update({blockIn.id:blockIn})
|
def computeVote(self, dagIn):
|
||||||
self.leaves.update({blockIn.id:blockIn})
|
(canopy, fullCanopy) = self.pick(dagIn)
|
||||||
for parent in blockIn.parents:
|
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:
|
||||||
if parent in self.leaves:
|
if parent in self.leaves:
|
||||||
del self.leaves[parent]
|
del self.leaves[parent]
|
||||||
|
self.blocks[parent].addChild(newBlock)
|
||||||
|
self.leaves.update({newBlock.id:newBlock})
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class Test_BlockDAG(unittest.TestCase):
|
class Test_BlockDAG(unittest.TestCase):
|
||||||
def test_BlockDAG(self):
|
def test_BlockDAG(self):
|
||||||
dag = BlockDAG()
|
dag = BlockDAG()
|
||||||
dag.startDAG()
|
|
||||||
self.assertTrue("0" in dag.blocks)
|
self.assertTrue("0" in dag.blocks)
|
||||||
self.assertTrue("0" in dag.leaves)
|
self.assertTrue("0" in dag.leaves)
|
||||||
self.assertTrue(len(dag.blocks)==1)
|
self.assertTrue(len(dag.blocks)==1)
|
||||||
self.assertTrue(len(dag.leaves)==1)
|
self.assertTrue(len(dag.leaves)==1)
|
||||||
b0 = dag.genBlock
|
b0 = dag.genesis
|
||||||
|
|
||||||
b1 = Block()
|
dag.makeBlock("1",{"0":b0})
|
||||||
b1.setParents(parentList={"0":b0})
|
b1 = dag.blocks["1"]
|
||||||
b1.setID("1")
|
|
||||||
dag.addLeaf(b1)
|
|
||||||
self.assertTrue("1" in dag.blocks)
|
self.assertTrue("1" in dag.blocks)
|
||||||
self.assertTrue("1" in dag.leaves)
|
self.assertTrue("1" in dag.leaves)
|
||||||
self.assertTrue("0" not in dag.leaves)
|
self.assertTrue("0" not in dag.leaves)
|
||||||
self.assertTrue(len(dag.blocks)==2)
|
self.assertTrue(len(dag.blocks)==2)
|
||||||
self.assertTrue(len(dag.leaves)==1)
|
self.assertTrue(len(dag.leaves)==1)
|
||||||
|
self.assertTrue("1" in b0.children)
|
||||||
|
self.assertTrue("1" in dag.genesis.children)
|
||||||
|
self.assertTrue("1" in dag.blocks[dag.genesis.id].children)
|
||||||
|
|
||||||
|
dag.makeBlock("2", {"0":b0})
|
||||||
|
b2 = dag.blocks["2"]
|
||||||
|
|
||||||
|
dag.makeBlock("3", {"1":b1, "2":b2})
|
||||||
|
b3 = dag.blocks["3"]
|
||||||
|
|
||||||
|
dag.makeBlock("4", {"2":b2})
|
||||||
|
b4 = dag.blocks["4"]
|
||||||
|
|
||||||
|
print(dag.computeVote(dag))
|
||||||
|
|
||||||
|
|
||||||
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)
|
suite = unittest.TestLoader().loadTestsFromTestCase(Test_BlockDAG)
|
||||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||||
|
|
||||||
|
|
|
@ -1,216 +0,0 @@
|
||||||
from BlockDAG import *
|
|
||||||
|
|
||||||
class Spectre(object):
|
|
||||||
""" """
|
|
||||||
def __init__(self):
|
|
||||||
self.dag = BlockDAG()
|
|
||||||
self.childRelation = {}
|
|
||||||
def setDAG(self, dagIn=None):
|
|
||||||
if dagIn is None:
|
|
||||||
self.dag = BlockDAG()
|
|
||||||
else:
|
|
||||||
self.dag = copy.deepcopy(dagIn)
|
|
||||||
self.updateChildren() # self.childRelation is a dict: self.childRelation[key]={block with key in block.parents}
|
|
||||||
def updateChildren(self):
|
|
||||||
self.childRelation = {}
|
|
||||||
q = deque()
|
|
||||||
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)
|
|
||||||
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:
|
|
||||||
result = self.vote(self.dag)
|
|
||||||
elif len(subdag.blocks)>1:
|
|
||||||
canopy = []
|
|
||||||
nextdag = copy.deepcopy(subdag)
|
|
||||||
while len(nextdag.blocks)>1:
|
|
||||||
canopy.append(nextdag.leaves)
|
|
||||||
nextdag = self.pruneLeaves(nextdag)
|
|
||||||
partialVotes = {}
|
|
||||||
for votingBlock in subdag.blocks:
|
|
||||||
partialVotes.update({votingBlock:{}})
|
|
||||||
for layer in canopy:
|
|
||||||
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):
|
|
||||||
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)
|
|
||||||
|
|
||||||
b1 = Block()
|
|
||||||
b1.setParents(parentList={"0":b0})
|
|
||||||
b1.setID("1")
|
|
||||||
shepard.addBlock(b1)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Sarang and Moneromooo implement bulletproofs.
|
||||||
|
|
||||||
|
Usually 90+% space complexity savings, up to 25% verification time complexity savings over traditional range proofs.
|
Loading…
Reference in a new issue