Merge pull request #18 from b-g-goodell/super-cali-spectre-licious

Super cali spectre licious
This commit is contained in:
Brandon Goodell 2017-12-14 02:30:55 -05:00 committed by GitHub
commit 84a2cda970
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 735 additions and 288 deletions

View file

@ -1,77 +1,83 @@
import unittest import unittest
import math import math
import numpy as np
import copy import copy
from collections import deque from collections import deque
import time import time
import hashlib
class Block(object): class Block(object):
""" Fundamental object. Contains dict of blockIDs:(parent blocks) """ """
def __init__(self): Fundamental object. Attributes:
self.id = "" # string data = payload dict with keys "timestamp" and "txns" and others
self.timestamp = None # format tbd ident = string
self.data = None # payload parents = dict {blockID : parentBlock}
self.parents = {} # block ID : pointer to block Functions:
self.children = {} # block ID : pointer to block addParents : takes dict {blockID : parentBlock} as input
def addChild(self, childIn): and updates parents to include.
if childIn not in self.children: _recomputeIdent : recomputes identity
self.children.update({childIn.id:childIn}) Usage:
def addChildren(self, childrenIn): b0 = Block(dataIn = stuff, parentsIn = None)
for child in childrenIn: b1 = Block(dataIn = otherStuff, parentsIn = { b0.ident : b0 })
self.addChild(childrenIn[child])
def addParent(self, parentIn): """
if parentIn not in self.parents: def __init__(self, dataIn=None, parentsIn=[]):
self.parents.update({parentIn.id:parentIn}) # Initialize with empty payload, no identity, and empty parents.
def addParents(self, parentsIn): self.data = dataIn
for parent in parentsIn: self.ident = hash(str(0))
self.addParent(parentsIn[parent]) assert type(parentsIn)==type([])
self.parents = parentsIn
self._recomputeIdent()
def addParents(self, parentsIn=[]): # list of parentIdents
if self.parents is None:
self.parents = parentsIn
else:
self.parents = self.parents + parentsIn
self._recomputeIdent()
def _recomputeIdent(self):
m = str(0) + str(self.data) + str(self.parents)
self.ident = hash(m)
class Test_Block(unittest.TestCase): class Test_Block(unittest.TestCase):
def test_Block(self): def test_Block(self):
# b0 -> b1 -> {both b2, b3} -> b4... oh, and say b3 -> b5 also
b0 = Block() b0 = Block()
b0.id = "0" b0.data = {"timestamp" : time.time()}
self.assertTrue(b0.data is None) time.sleep(1)
self.assertTrue(len(b0.parents)==0)
b1 = Block() b1 = Block()
b1.parents.update({"0":b0}) b1.data = {"timestamp" : time.time(), "txns" : [1,2,3]}
b1.id = "1" b1.addParents({b0.ident:b0}) # updateIdent called with addParent.
for parentID in b1.parents: time.sleep(1)
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 = Block()
b2.parents.update({"0":b0}) b2.data = {"timestamp" : time.time(), "txns" : None}
b2.id = "2" b2.addParents({b1.ident:b1})
for parentID in b2.parents: time.sleep(1)
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 = Block()
b3.parents.update({"1":b1, "2":b2}) b3.data = {"timestamp" : time.time(), "txns" : None}
b3.id = "3" b3.addParents({b1.ident:b1})
for parentID in b3.parents: time.sleep(1)
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)
self.assertTrue("2" in b3.parents)
self.assertFalse("0" in b3.parents)
b4 = Block() b4 = Block()
b4.parents.update({"2":b2}) b4.data = {"timestamp" : time.time()} # see how sloppy we can be wheeee
b4.id = "4" b4.addParents({b2.ident:b2, b3.ident:b3})
for parentID in b4.parents: time.sleep(1)
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)
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block) b5 = Block()
unittest.TextTestRunner(verbosity=1).run(suite) b5.data = {"timestamp" : time.time(), "txns" : "stuff" }
b5.addParents({b3.ident:b3})
self.assertTrue(len(b1.parents)==1 and b0.ident in b1.parents)
self.assertTrue(len(b2.parents)==1 and b1.ident in b2.parents)
self.assertTrue(len(b3.parents)==1 and b1.ident in b3.parents)
self.assertTrue(len(b4.parents)==2)
self.assertTrue(b2.ident in b4.parents and b3.ident in b4.parents)
self.assertTrue(len(b5.parents)==1 and b3.ident in b5.parents)
#suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block)
#unittest.TextTestRunner(verbosity=1).run(suite)

View file

@ -1,227 +0,0 @@
from Block import *
#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####
class BlockDAG(object):
""" 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:
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()
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.genesis
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))
suite = unittest.TestLoader().loadTestsFromTestCase(Test_BlockDAG)
unittest.TextTestRunner(verbosity=1).run(suite)

View file

@ -0,0 +1,668 @@
'''
A handler for Block.py that takes a collection of blocks (which
only reference parents) as input data. It uses a doubly-linked
tree to determine precedent relationships efficiently, and it can
use that precedence relationship to produce a reduced/robust pre-
cedence relationship as output (the spectre precedence relationship
between blocks.
Another handler will extract a coherent/robust list of non-conflict-
ing transactions from a reduced/robust BlockHandler object.
'''
from Block import *
import random
class BlockHandler(object):
def __init__(self):
#print("Initializing")
# Initialize a BlockHandler object.
self.data = None
self.blocks = {} # Set of blocks (which track parents)
self.family = {} # Doubly linked list tracks parent-and-child links
self.invDLL = {} # subset of blocks unlikely to be re-orged
self.roots = [] # list of root blockIdents
self.leaves = [] # list of leaf blockIdents
self.antichains = []
self.vids = []
self.antichainCutoff = 600 # stop re-orging after this many layers
self.pendingVotes = {}
self.votes = {}
self.totalVotes = {}
def addBlock(self, b):
#print("Adding block")
# Take a single block b and add to self.blocks, record family
# relations, update leaf monitor, update root monitor if nec-
# essary
diffDict = {copy.deepcopy(b.ident):copy.deepcopy(b)}
try:
assert b.ident not in self.blocks
except AssertionError:
print("Woops, tried to add a block with ident in self.blocks, overwriting old block")
self.blocks.update(diffDict)
try:
assert b.ident not in self.leaves
except AssertionError:
print("Woops, tried to add a block to leaf set that is already in the leafset, aborting.")
self.leaves.append(b.ident) # New block is always a leaf
try:
assert b.ident not in self.family
except AssertionError:
print("woops, tried to add a block that already has a recorded family history, aborting.")
self.family.update({b.ident:{"parents":b.parents, "children":[]}})
# Add fam history fam (new blocks have no children yet)
# Now update each parent's family history to reflect the new child
if b.parents is not None:
if len(b.parents)>0:
for parentIdent in b.parents:
if parentIdent not in self.family:
# This should never occur.
print("Hey, what? confusedTravolta.gif... parentIdent not in self.family, parent not correct somehow.")
self.family.update({parentIdent:{}})
if "parents" not in self.family[parentIdent]:
# This should never occur.
print("Hey, what? confusedTravolta.gif... family history of parent lacks sub-dict for parentage, parent not correct somehow")
self.family[parentIdent].update({"parents":[]})
if "children" not in self.family[parentIdent]:
# This should never occur.
print("Hey, what? confusedTravolta.gif... family history of parent lacks sub-dict for children, parent not correct somehow")
self.family[parentIdent].update({"children":[]})
if self.blocks[parentIdent].parents is not None:
for pid in self.blocks[parentIdent].parents:
if pid not in self.family[parentIdent]["parents"]:
self.family[parentIdent]["parents"].append(pid)
#for p in self.blocks[parentIdent].parents: self.family[parentIdent]["parents"].append(p)
# Update "children" sub-dict of family history of parent
self.family[parentIdent]["children"].append(b.ident)
# If the parent was previously a leaf, it is no longer
if parentIdent in self.leaves:
self.leaves.remove(parentIdent)
else:
if b.ident not in self.roots:
self.roots.append(b.ident)
if b.ident not in self.leaves:
self.leaves.append(b.ident)
if b.ident not in self.family:
self.family.update({b.ident:{"parents":{}, "children":{}}})
else:
if b.ident not in self.roots:
self.roots.append(b.ident)
if b.ident not in self.leaves:
self.leaves.append(b.ident)
if b.ident not in self.family:
self.family.update({b.ident:{"parents":{}, "children":{}}})
pass
def hasAncestor(self, xid, yid):
# Return true if y is an ancestor of x
assert xid in self.blocks
assert yid in self.blocks
q = deque()
found = False
if self.blocks[xid].parents is not None:
for pid in self.blocks[xid].parents:
if pid==yid:
found = True
break
q.append(pid)
while(len(q)>0 and not found):
xid = q.popleft()
if self.blocks[xid].parents is not None:
if len(self.blocks[xid].parents) > 0:
for pid in self.blocks[xid].parents:
if pid==yid:
found = True
q.append(pid)
return found
def pruneLeaves(self):
#print("Pruning leaves")
out = BlockHandler()
q = deque()
for rootIdent in self.roots:
q.append(rootIdent)
while(len(q)>0):
thisIdent = q.popleft()
if thisIdent not in self.leaves:
out.addBlock(self.blocks[thisIdent])
for chIdent in self.family[thisIdent]["children"]:
q.append(chIdent)
return out
def leafBackAntichain(self):
#print("Computing antichain")
temp = copy.deepcopy(self)
decomposition = []
vulnIdents = []
decomposition.append([])
for lid in temp.leaves:
decomposition[-1].append(lid)
vulnIdents = copy.deepcopy(decomposition[-1])
temp = temp.pruneLeaves()
while(len(temp.blocks)>0 and len(decomposition) < self.antichainCutoff):
decomposition.append([])
for lid in temp.leaves:
decomposition[-1].append(lid)
for xid in decomposition[-1]:
if xid not in vulnIdents:
vulnIdents.append(xid)
temp = temp.pruneLeaves()
return decomposition, vulnIdents
def transmitVote(self, votingIdents):
(vid, xid, yid) = votingIdents
q = deque()
for wid in self.blocks[vid].parents:
if wid in self.vids:
q.append(wid)
while(len(q)>0):
wid = q.popleft()
if (wid,xid,yid) not in self.pendingVotes:
self.pendingVotes.update({(wid,xid,yid):0})
if (wid,yid,xid) not in self.pendingVotes:
self.pendingVotes.update({(wid,yid,xid):0})
self.pendingVotes[(wid,xid,yid)]+=1
self.pendingVotes[(wid,yid,xid)]-=1
#print(self.blocks[wid].parents)
for pid in self.blocks[wid].parents:
if pid in self.vids:
q.append(pid)
def voteFor(self, votingIdents, touched):
(vid, xid, yid) = votingIdents
self.votes.update({(vid,xid,yid):1, (vid,yid,xid):-1})
touched.update({(vid,xid,yid):True, (vid,yid,xid):True})
self.transmitVote((vid,xid,yid))
return touched
def sumPendingVote(self, vid, touched):
pastR = self.pastOf(vid)
for xid in self.vids:
for yid in self.vids:
if (vid, xid, yid) in self.pendingVotes:
if self.pendingVotes[(vid,xid,yid)] > 0:
touched = self.voteFor((vid,xid,yid), touched)
elif self.pendingVotes[(vid,xid,yid)] <0:
touched = self.voteFor((vid,yid,xid), touched)
else:
self.votes.update({(vid,xid,yid): 0, (vid,yid,xid): 0})
touched.update({(vid,xid,yid): True, (vid,yid,xid): True})
#R = self.pastOf(vid)
#touched = R.vote(touched)
return touched
def vote(self,touchedIn={}):
U, V = self.leafBackAntichain()
self.antichains = U
self.vids = V
touched = touchedIn
for i in range(len(U)):
for vid in U[i]: # ID of voting block
touched = self.sumPendingVote(vid, touched)
for j in range(i+1):
for xid in U[j]: # Voting block compares self to xid
# Note if j=i, xid and vid are incomparable.
# If j < i, then xid may have vid as an ancestor.
# vid can never have xid as an ancestor.
# In all cases, vid votes that vid precedes xid
if xid==vid:
continue
else:
touched = self.voteFor((vid,vid,xid),touched)
# For each ancestor of xid that is not an ancestor of vid,
# we can apply the same!
q = deque()
for pid in self.blocks[xid].parents:
if pid in self.vids and not self.hasAncestor(vid,pid):
q.append(pid)
while(len(q)>0):
wid = q.popleft()
for pid in self.blocks[wid].parents:
if pid in self.vids and not self.hasAncestor(vid, pid):
q.append(pid)
touched = self.voteFor((vid,vid,wid),touched)
R = self.pastOf(vid)
R.vote()
for xid in R.blocks:
touched = self.voteFor((vid,xid,vid), touched)
for yid in R.blocks:
if (xid, yid) in R.totalVotes:
if R.totalVotes[(xid,yid)]:
touched = self.voteFor((vid,xid,yid), touched)
elif (yid, xid) in R.totalVotes:
if R.totalVotes[(yid,xid)]:
touched = self.voteFor((vid, yid, xid), touched)
self.computeTotalVotes()
return touched
def computeTotalVotes(self):
for xid in self.vids:
for yid in self.vids:
s = 0
found = False
for vid in self.vids:
if (vid, xid, yid) in self.votes or (vid, yid, xid) in self.votes:
found = True
if self.votes[(vid, xid, yid)]==1:
s+= 1
elif self.votes[(vid,yid,xid)]==-1:
s-= 1
if found:
if s > 0:
self.totalVotes.update({(xid, yid):True, (yid,xid):False})
elif s < 0:
self.totalVotes.update({(xid,yid):False, (yid,xid):True})
elif s==0:
self.totalVotes.update({(xid,yid):False, (yid,xid):False})
else:
if (xid,yid) in self.totalVotes:
del self.totalVotes[(xid,yid)]
if (yid,xid) in self.totalVotes:
del self.totalVotes[(yid,xid)]
def pastOf(self, xid):
R = BlockHandler()
identsToAdd = {}
q = deque()
for pid in self.blocks[xid].parents:
q.append(pid)
while(len(q)>0):
yid = q.popleft()
if yid not in identsToAdd:
identsToAdd.update({yid:True})
for pid in self.blocks[yid].parents:
q.append(pid)
for rid in self.roots:
if rid in identsToAdd:
q.append(rid)
while(len(q)>0):
yid = q.popleft()
if yid not in R.blocks:
R.addBlock(self.blocks[yid])
for pid in self.family[yid]["children"]:
if pid in identsToAdd:
q.append(pid)
return R
class Test_BlockHandler(unittest.TestCase):
def test_betterTest(self):
R = BlockHandler()
self.assertTrue(R.data is None)
self.assertEqual(len(R.blocks),0)
self.assertEqual(type(R.blocks),type({}))
self.assertEqual(len(R.family),0)
self.assertEqual(type(R.family),type({}))
self.assertEqual(len(R.invDLL),0)
self.assertEqual(type(R.invDLL),type({}))
self.assertEqual(len(R.roots),0)
self.assertEqual(type(R.leaves),type([]))
self.assertEqual(len(R.leaves),0)
self.assertEqual(R.antichainCutoff,600)
self.assertEqual(type(R.roots),type([]))
self.assertEqual(len(R.pendingVotes),0)
self.assertEqual(type(R.pendingVotes),type({}))
self.assertEqual(len(R.votes),0)
self.assertEqual(type(R.votes),type({}))
gen = Block() # genesis block
self.assertTrue(gen.data is None)
self.assertEqual(gen.parents,[])
msg = str(0) + str(None) + str([])
self.assertEqual(gen.ident, hash(msg))
block0 = gen
block1 = Block(parentsIn=[block0.ident], dataIn={"timestamp":time.time(), "txns":"pair of zircon encrusted tweezers"})
block2 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"watch out for that yellow snow"})
block3 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"he had the stank foot"})
block4 = Block(parentsIn=[block2.ident, block3.ident], dataIn={"timestamp":time.time(), "txns":"come here fido"})
block5 = Block(parentsIn=[block3.ident], dataIn={"timestamp":time.time(), "txns":"applied rotation on her sugar plum"})
block6 = Block(parentsIn=[block5.ident], dataIn={"timestamp":time.time(), "txns":"listen to frank zappa for the love of all that is good"})
R.addBlock(block0)
self.assertTrue(block0.ident in R.leaves)
self.assertTrue(block0.ident in R.roots)
R.addBlock(block1)
self.assertTrue(block1.ident in R.leaves and block0.ident not in R.leaves)
R.addBlock(block2)
self.assertTrue(block2.ident in R.leaves and block1.ident not in R.leaves)
R.addBlock(block3)
self.assertTrue(block3.ident in R.leaves and block2.ident in R.leaves and block1.ident not in R.leaves)
R.addBlock(block4)
self.assertTrue(block4.ident in R.leaves and block3.ident not in R.leaves and block2.ident not in R.leaves)
R.addBlock(block5)
self.assertTrue(block4.ident in R.leaves and block5.ident in R.leaves and block3.ident not in R.leaves)
R.addBlock(block6)
self.assertTrue(block4.ident in R.leaves and block6.ident in R.leaves and block5.ident not in R.leaves)
self.assertEqual(len(R.blocks), 7)
self.assertEqual(len(R.family), 7)
self.assertEqual(len(R.invDLL), 0)
self.assertEqual(len(R.roots), 1)
self.assertEqual(len(R.leaves),2)
self.assertEqual(R.antichainCutoff, 600)
self.assertEqual(len(R.pendingVotes),0)
self.assertEqual(len(R.votes),0)
self.assertTrue( R.hasAncestor(block6.ident, block0.ident) and not R.hasAncestor(block0.ident, block6.ident))
self.assertTrue( R.hasAncestor(block5.ident, block0.ident) and not R.hasAncestor(block0.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block0.ident) and not R.hasAncestor(block0.ident, block4.ident))
self.assertTrue( R.hasAncestor(block3.ident, block0.ident) and not R.hasAncestor(block0.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block0.ident) and not R.hasAncestor(block0.ident, block2.ident))
self.assertTrue( R.hasAncestor(block1.ident, block0.ident) and not R.hasAncestor(block0.ident, block1.ident))
self.assertTrue( R.hasAncestor(block6.ident, block1.ident) and not R.hasAncestor(block1.ident, block6.ident))
self.assertTrue( R.hasAncestor(block5.ident, block1.ident) and not R.hasAncestor(block1.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block1.ident) and not R.hasAncestor(block1.ident, block4.ident))
self.assertTrue( R.hasAncestor(block3.ident, block1.ident) and not R.hasAncestor(block1.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block1.ident) and not R.hasAncestor(block1.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block1.ident) and R.hasAncestor(block1.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block6.ident, block2.ident) and not R.hasAncestor(block2.ident, block6.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block2.ident) and not R.hasAncestor(block2.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block2.ident) and not R.hasAncestor(block2.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block2.ident) and not R.hasAncestor(block2.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block2.ident) and R.hasAncestor(block2.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block2.ident) and R.hasAncestor(block2.ident, block0.ident))
self.assertTrue( R.hasAncestor(block6.ident, block3.ident) and not R.hasAncestor(block3.ident, block6.ident))
self.assertTrue( R.hasAncestor(block5.ident, block3.ident) and not R.hasAncestor(block3.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block3.ident) and not R.hasAncestor(block3.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block3.ident) and not R.hasAncestor(block3.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block3.ident) and R.hasAncestor(block3.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block3.ident) and R.hasAncestor(block3.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block6.ident, block4.ident) and not R.hasAncestor(block4.ident, block6.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block4.ident) and not R.hasAncestor(block4.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block4.ident) and R.hasAncestor(block4.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block4.ident) and R.hasAncestor(block4.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block4.ident) and R.hasAncestor(block4.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block4.ident) and R.hasAncestor(block4.ident, block0.ident))
self.assertTrue( R.hasAncestor(block6.ident, block5.ident) and not R.hasAncestor(block5.ident, block6.ident))
self.assertTrue(not R.hasAncestor(block4.ident, block5.ident) and not R.hasAncestor(block5.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block5.ident) and R.hasAncestor(block5.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block5.ident) and not R.hasAncestor(block5.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block5.ident) and R.hasAncestor(block5.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block5.ident) and R.hasAncestor(block5.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block6.ident) and R.hasAncestor(block6.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block4.ident, block6.ident) and not R.hasAncestor(block6.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block6.ident) and R.hasAncestor(block6.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block6.ident) and not R.hasAncestor(block6.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block6.ident) and R.hasAncestor(block6.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block6.ident) and R.hasAncestor(block6.ident, block0.ident))
R = R.pruneLeaves()
self.assertEqual(len(R.blocks), 5)
self.assertEqual(len(R.family), 5)
self.assertEqual(len(R.invDLL), 0)
self.assertEqual(len(R.roots), 1)
self.assertEqual(len(R.leaves),2)
self.assertEqual(R.antichainCutoff, 600)
self.assertEqual(len(R.pendingVotes),0)
self.assertEqual(len(R.votes),0)
self.assertTrue( R.hasAncestor(block5.ident, block0.ident) and not R.hasAncestor(block0.ident, block5.ident))
self.assertTrue( R.hasAncestor(block3.ident, block0.ident) and not R.hasAncestor(block0.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block0.ident) and not R.hasAncestor(block0.ident, block2.ident))
self.assertTrue( R.hasAncestor(block1.ident, block0.ident) and not R.hasAncestor(block0.ident, block1.ident))
self.assertTrue( R.hasAncestor(block5.ident, block1.ident) and not R.hasAncestor(block1.ident, block5.ident))
self.assertTrue( R.hasAncestor(block3.ident, block1.ident) and not R.hasAncestor(block1.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block1.ident) and not R.hasAncestor(block1.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block1.ident) and R.hasAncestor(block1.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block2.ident) and not R.hasAncestor(block2.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block2.ident) and not R.hasAncestor(block2.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block2.ident) and R.hasAncestor(block2.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block2.ident) and R.hasAncestor(block2.ident, block0.ident))
self.assertTrue( R.hasAncestor(block5.ident, block3.ident) and not R.hasAncestor(block3.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block3.ident) and not R.hasAncestor(block3.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block3.ident) and R.hasAncestor(block3.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block3.ident) and R.hasAncestor(block3.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block5.ident) and R.hasAncestor(block5.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block5.ident) and not R.hasAncestor(block5.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block5.ident) and R.hasAncestor(block5.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block5.ident) and R.hasAncestor(block5.ident, block0.ident))
## Formal unit tests for leafBackAntichain() to follow: visual inspection reveals this does what it says on the tin.
#R.vote()
#print(R.votes)
def test_big_bertha(self):
R = BlockHandler()
gen = Block() # genesis block
msg = str(0) + str(None) + str([])
block0 = gen
block1 = Block(parentsIn=[block0.ident], dataIn={"timestamp":time.time(), "txns":"pair of zircon encrusted tweezers"})
block2 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"watch out for that yellow snow"})
block3 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"he had the stank foot"})
block4 = Block(parentsIn=[block2.ident, block3.ident], dataIn={"timestamp":time.time(), "txns":"come here fido"})
block5 = Block(parentsIn=[block3.ident], dataIn={"timestamp":time.time(), "txns":"applied rotation on her sugar plum"})
block6 = Block(parentsIn=[block5.ident], dataIn={"timestamp":time.time(), "txns":"listen to frank zappa for the love of all that is good"})
R.addBlock(block0)
R.addBlock(block1)
R.addBlock(block2)
R.addBlock(block3)
R.addBlock(block4)
R.addBlock(block5)
R.addBlock(block6)
names = {0:block0.ident, 1:block1.ident, 2:block2.ident, 3:block3.ident, 4:block4.ident, 5:block5.ident, 6:block6.ident}
# Testing voteFor
# Verify all roots have children
for rid in R.roots:
self.assertFalse(len(R.family[rid]["children"])==0)
# Verify that all children of all roots have children and collect grandchildren idents
gc = []
for rid in R.roots:
for cid in R.family[rid]["children"]:
self.assertFalse(len(R.family[cid]["children"]) == 0)
gc = gc + R.family[cid]["children"]
# Pick a random grandchild of the root.
gcid = random.choice(gc)
# Pick a random block with gcid in its past
vid = random.choice(list(R.blocks.keys()))
while(not R.hasAncestor(vid, gcid)):
vid = random.choice(list(R.blocks.keys()))
# Pick a random pair of blocks for gcid and vid to vote on.
xid = random.choice(list(R.blocks.keys()))
yid = random.choice(list(R.blocks.keys()))
# Have vid cast vote that xid < yid
R.voteFor((vid,xid,yid),{})
# Verify that R.votes has correct entries
self.assertEqual(R.votes[(vid,xid,yid)], 1)
self.assertEqual(R.votes[(vid,yid,xid)],-1)
# Check that for each ancestor of vid, that they received an appropriate pending vote
q = deque()
for pid in R.blocks[vid].parents:
if pid in R.vids:
q.append(pid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],1)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Now we are going to mess around with how voting at gcid interacts with the above.
# First, we let gcid cast a vote that xid < yid and check that it propagates appropriately as above.
R.voteFor((gcid,xid,yid),{})
self.assertEqual(R.votes[(gcid,xid,yid)],1)
self.assertEqual(R.votes[(gcid,yid,xid)],-1)
for pid in R.blocks[gcid].parents:
if pid in R.vids:
q.append(gpid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],2)
self.assertEqual(R.pendingVotes[(wid,yid,xid)],-2)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Now we are going to have gcid cast the opposite vote. this should change what is stored in R.votes
# but also change pending votes below gcid
R.voteFor((gcid,yid,xid),{})
self.assertEqual(R.votes[(gcid,xid,yid)],-1)
self.assertEqual(R.votes[(gcid,yid,xid)],1)
for pid in R.blocks[gcid].parents:
if pid in R.vids:
q.append(gpid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],0)
self.assertEqual(R.pendingVotes[(wid,yid,xid)],0)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Do again, now pending votes should be negative
R.voteFor((gcid,yid,xid),{})
self.assertEqual(R.votes[(gcid,xid,yid)],-1)
self.assertEqual(R.votes[(gcid,yid,xid)],1)
for pid in R.blocks[gcid].parents:
if pid in R.vids:
q.append(gpid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],-1)
self.assertEqual(R.pendingVotes[(wid,yid,xid)],1)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Test sumPendingVotes
R.sumPendingVote(gcid, {})
self.assertTrue((gcid,xid,yid) in R.votes)
self.assertTrue((gcid,yid,xid) in R.votes)
self.assertEqual(R.votes[(gcid,xid,yid)],-1)
self.assertEqual(R.votes[(gcid,yid,xid)],1)
touched = R.vote()
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
print("Antichain layers:\n")
for layer in R.antichains:
print(layer)
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
for key in R.votes:
print("key = ", key, ", vote = ", R.votes[key])
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
for key in R.totalVotes:
print("key = ", key, ", vote = ", R.totalVotes[key])
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
self.assertTrue((names[0], names[1]) in R.totalVotes and (names[1], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[1])] and not R.totalVotes[(names[1], names[0])])
self.assertTrue((names[0], names[2]) in R.totalVotes and (names[2], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[2])] and not R.totalVotes[(names[2], names[0])])
self.assertTrue((names[0], names[3]) in R.totalVotes and (names[3], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[3])] and not R.totalVotes[(names[3], names[0])])
self.assertTrue((names[0], names[4]) in R.totalVotes and (names[4], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[4])] and not R.totalVotes[(names[4], names[0])])
self.assertTrue((names[0], names[5]) in R.totalVotes and (names[5], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[5])] and not R.totalVotes[(names[5], names[0])])
self.assertTrue((names[0], names[6]) in R.totalVotes and (names[6], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[6])] and not R.totalVotes[(names[6], names[0])])
#### #### #### ####
self.assertTrue((names[1], names[2]) in R.totalVotes and (names[2], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[2])] and not R.totalVotes[(names[2], names[1])])
self.assertTrue((names[1], names[3]) in R.totalVotes and (names[3], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[3])] and not R.totalVotes[(names[3], names[1])])
self.assertTrue((names[1], names[4]) in R.totalVotes and (names[4], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[4])] and not R.totalVotes[(names[4], names[1])])
self.assertTrue((names[1], names[5]) in R.totalVotes and (names[5], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[5])] and not R.totalVotes[(names[5], names[1])])
self.assertTrue((names[1], names[6]) in R.totalVotes and (names[6], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[6])] and not R.totalVotes[(names[6], names[1])])
#### #### #### ####
self.assertTrue((names[2], names[3]) in R.totalVotes and (names[3], names[2]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[2], names[3])] and R.totalVotes[(names[3], names[2])])
self.assertTrue((names[2], names[4]) in R.totalVotes and (names[4], names[2]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[2], names[4])] and not R.totalVotes[(names[4], names[2])])
self.assertTrue((names[2], names[5]) in R.totalVotes and (names[5], names[2]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[2], names[5])] and R.totalVotes[(names[5], names[2])])
self.assertTrue((names[2], names[6]) in R.totalVotes and (names[6], names[2]) in R.totalVotes)
#print("2,6 ", R.totalVotes[(names[2], names[6])])
#print("6,2 ", R.totalVotes[(names[6], names[2])])
self.assertTrue(not R.totalVotes[(names[2], names[6])] and R.totalVotes[(names[6], names[2])])
#### #### #### ####
self.assertTrue((names[3], names[4]) in R.totalVotes and (names[4], names[3]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[3], names[4])] and not R.totalVotes[(names[4], names[3])])
self.assertTrue((names[3], names[5]) in R.totalVotes and (names[5], names[3]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[3], names[5])] and not R.totalVotes[(names[5], names[3])])
self.assertTrue((names[3], names[6]) in R.totalVotes and (names[6], names[3]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[3], names[6])] and not R.totalVotes[(names[6], names[3])])
#### #### #### ####
self.assertTrue((names[4], names[5]) in R.totalVotes and (names[5], names[4]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[4], names[5])] and R.totalVotes[(names[5], names[4])])
self.assertTrue((names[4], names[6]) in R.totalVotes and (names[6], names[4]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[4], names[6])] and R.totalVotes[(names[6], names[4])])
#### #### #### ####
self.assertTrue((names[5], names[6]) in R.totalVotes and (names[6], names[5]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[5], names[6])] and not R.totalVotes[(names[6], names[5])])
#print(R.votes)
suite = unittest.TestLoader().loadTestsFromTestCase(Test_BlockHandler)
unittest.TextTestRunner(verbosity=1).run(suite)