From 253f70d248cb85d34e6be33b135e2a25fa813eb7 Mon Sep 17 00:00:00 2001 From: Brandon Goodell Date: Sun, 5 Nov 2017 16:17:34 -0700 Subject: [PATCH] playing --- source-code/Spectre/Block.py | 56 ++++--- source-code/Spectre/BlockDAG.py | 249 ++++++++++++++++++++++++++------ source-code/Spectre/Spectre.py | 216 --------------------------- 3 files changed, 238 insertions(+), 283 deletions(-) delete mode 100644 source-code/Spectre/Spectre.py diff --git a/source-code/Spectre/Block.py b/source-code/Spectre/Block.py index b5198b8..48f01d5 100644 --- a/source-code/Spectre/Block.py +++ b/source-code/Spectre/Block.py @@ -7,42 +7,56 @@ 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) + def __init__(self): + self.id = "" # string + self.timestamp = None # format tbd + self.data = None # payload + self.parents = {} # block ID : pointer to block + self.children = {} # block ID : pointer to block + def addChild(self, childIn): + if childIn not in self.children: + self.children.update({childIn.id:childIn}) + 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): def test_Block(self): b0 = Block() - b0.setParents() - b0.setID("0") + b0.id = "0" self.assertTrue(b0.data is None) self.assertTrue(len(b0.parents)==0) b1 = Block() - b1.setParents(parentList={"0":b0}) - b1.setID("1") + b1.parents.update({"0":b0}) + b1.id = "1" + for parentID in b1.parents: + b1.parents[parentID].children.update({b1.id:b1}) 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") + b2.parents.update({"0":b0}) + b2.id = "2" + for parentID in b2.parents: + b2.parents[parentID].children.update({b2.id:b2}) 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") + b3.parents.update({"1":b1, "2":b2}) + b3.id = "3" + for parentID in b3.parents: + b3.parents[parentID].children.update({b3.id:b3}) self.assertTrue(b3.data is None) self.assertTrue(len(b3.parents)==2) self.assertTrue("1" in b3.parents) @@ -50,8 +64,10 @@ class Test_Block(unittest.TestCase): self.assertFalse("0" in b3.parents) b4 = Block() - b4.setParents(parentList={"2":b2}) - b4.setID("4") + b4.parents.update({"2":b2}) + b4.id = "4" + for parentID in b4.parents: + b4.parents[parentID].children.update({b4.id:b4}) self.assertTrue(b4.data is None) self.assertTrue(len(b4.parents)==1) self.assertTrue("2" in b4.parents) diff --git a/source-code/Spectre/BlockDAG.py b/source-code/Spectre/BlockDAG.py index 524bcd0..3f89be0 100644 --- a/source-code/Spectre/BlockDAG.py +++ b/source-code/Spectre/BlockDAG.py @@ -1,72 +1,227 @@ 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 + """ 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 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: + 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: if parent in self.leaves: 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): 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 + b0 = dag.genesis - b1 = Block() - b1.setParents(parentList={"0":b0}) - b1.setID("1") - dag.addLeaf(b1) + dag.makeBlock("1",{"0":b0}) + b1 = dag.blocks["1"] 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) + 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) unittest.TextTestRunner(verbosity=1).run(suite) - diff --git a/source-code/Spectre/Spectre.py b/source-code/Spectre/Spectre.py deleted file mode 100644 index 1e0959a..0000000 --- a/source-code/Spectre/Spectre.py +++ /dev/null @@ -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) - -