research-lab/source-code/Spectre/BlockHandler.py
Brandon Goodell cdaeb98d1d udpate ffs
2017-12-12 15:33:05 -07:00

245 lines
11 KiB
Python

'''
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 *
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 = {} # dict {blockIdent : block} root blocks
self.leaves = {}
self.antichainCutoff = 600 # stop re-orging after this many layers
self.pendingVotes = {}
self.votes = {}
def _addBlocks(self, blocksIn):
print("Adding Blocks")
# Take dict of {blockIdent : block} and call _addBlock on each.
for b in blocksIn:
self._addBlock(blocksIn[b])
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 = {b.ident:b}
self.blocks.update(diffDict)
self.family.update({b.ident:{}})
self.family[b.ident].update({"parents":b.parents, "children":{}})
for parentIdent in b.parents:
if parentIdent not in self.family:
self.family.update({parentIdent:{}})
if "parents" not in self.family[parentIdent]:
self.family[parentIdent].update({"parents":{}})
if "children" not in self.family[parentIdent]:
self.family[parentIdent].update({"children":{}})
self.family[parentIdent]["parents"].update(b.parents)
self.family[parentIdent]["children"].update(diffDict)
if parentIdent in self.leaves:
del self.leaves[parentIdent]
if len(b.parents)==0 and b.ident not in self.roots:
self.roots.update(diffDict)
self.leaves.update(diffDict)
def inPast(self, x, y):
print("Testing if in past")
# Return true if y is an ancestor of x
q = deque()
for pid in self.blocks[x].parents:
if pid==y:
return True
break
q.append(pid)
while(len(q)>0):
nxtIdent = q.popleft()
if len(self.blocks[nxtIdent].parents) > 0:
for pid in self.blocks[nxtIdent].parents:
if pid==y:
return True
break
q.append(pid)
return False
def vote(self):
print("Voting")
# Compute partial spectre vote for top several layers of
# the dag.
(U, vids) = self.leafBackAntichain()
self.votes = {}
q = deque()
self.pendingVotes = {}
for i in range(len(U)):
for leafId in U[i]:
if i > 0:
self.sumPendingVotes(leafId, vids)
for x in U[i]:
if x != leafId:
q.append(x)
while(len(q)>0):
x = q.popleft()
if (leafId, leafId, x) not in self.votes:
self.votes.update({(leafId, leafId, x):1})
else:
try:
assert self.votes[(leafId, leafId, x)]==1
except AssertionError:
print("Woops, we found (leafId, leafId, x) as a key in self.votes while running vote(), and it should be +1, but it isn't:\n\n", (leafId, leafId, x), self.votes[(leafId, leafId, x)], "\n\n")
if (leafId, x, leafId) not in self.votes:
self.votes.update({(leafId, x, leafId):-1})
else:
try:
assert self.votes[(leafId,x,leafId)]==-1
except AssertionError:
print("Woops, we found (leafId, x, leafId) as a key in self.votes while running vote(), and it should be +1, but it isn't:\n\n", (leafId, x, leafId), self.votes[(leafId, x, leafId)], "\n\n")
self.transmitVote(leafId, leafId, x)
for pid in self.blocks[x].parents:
if not self.inPast(leafId, pid) and pid in vids and pid != leafId:
q.append(pid)
print(self.votes)
def sumPendingVotes(self, blockId, vulnIds):
print("Summing pending votes")
# For a blockId, take all pending votes for vulnerable IDs (x,y)
# if the net is positive vote 1, if the net is negative vote -1
# otherwise vote 0.
for x in vulnIds:
for y in vulnIds:
if (blockId, x, y) in self.pendingVotes:
if self.pendingVotes[(blockId, x, y)] > 0:
if (blockId, x, y) not in self.votes:
self.votes.update({(blockId, x, y):1})
else:
try:
assert self.votes[(blockId,x,y)]==1
except AssertionError:
print("Woops, we found (blockId, x, y) as a key in self.votes, and it should be +1, but it isn't:\n\n", (blockId, x, y), self.votes[(blockId, x,y)], "\n\n")
if (blockId, y, x) not in self.votes:
self.votes.update({(blockId, y, x):-1})
else:
try:
assert self.votes[(blockId,y,x)]==-1
except AssertionError:
print("Woops, we found (blockId, y, x) as a key in self.votes, and it should be -1, but it isn't:\n\n", (blockId, y, x), self.votes[(blockId, y,x)], "\n\n")
self.transmitVote(blockId, x, y)
elif self.pendingVotes[(blockId, x, y)] < 0:
if (blockId, x, y) not in self.votes:
self.votes.update({(blockId, x, y):-1})
else:
try:
assert self.votes[(blockId,x,y)]==-1
except AssertionError:
print("Woops, we found (blockId, x, y) as a key in self.votes, and it should be -1, but it isn't:\n\n", (blockId, x, y), self.votes[(blockId, x,y)], "\n\n")
if (blockId, y, x) not in self.votes:
self.votes.update({(blockId, y, x):1})
else:
try:
assert self.votes[(blockId,y,x)]==1
except AssertionError:
print("Woops, we found (blockId, y, x) as a key in self.votes, and it should be +1, but it isn't:\n\n", (blockId, x, y), self.votes[(blockId, x,y)], "\n\n")
self.transmitVote(blockId, y, x)
else:
if (blockId, x, y) not in self.votes:
self.votes.update({(blockId, x, y):0})
else:
try:
assert self.votes[(blockId,x,y)]==0
except AssertionError:
print("Woops, we found (blockId, x, y) as a key in self.votes, and it should be 0, but it isn't:\n\n", (blockId, x, y), self.votes[(blockId, x,y)], "\n\n")
if (blockId, y, x) not in self.votes:
self.votes.update({(blockId, y, x):0})
else:
try:
assert self.votes[(blockId,y,x)]==0
except AssertionError:
print("Woops, we found (blockId, y, x) as a key in self.votes, and it should be 0, but it isn't:\n\n", (blockId, y, x), self.votes[(blockId, y,x)], "\n\n")
def transmitVote(self, v, x, y):
print("Transmitting votes")
q = deque()
for pid in self.blocks[v].parents:
q.append(pid)
while(len(q)>0):
print("Length of queue = ", len(q))
nxtPid = q.popleft()
if (nxtPid, x, y) not in self.pendingVotes:
self.pendingVotes.update({(nxtPid,x,y):1})
self.pendingVotes.update({(nxtPid,y,x):-1})
else:
self.pendingVotes[(nxtPid,x,y)] += 1
self.pendingVotes[(nxtPid,y,x)] -= 1
if len(self.blocks[nxtPid].parents) > 0:
for pid in self.blocks[nxtPid].parents:
if pid != nxtPid:
q.append(pid)
def leafBackAntichain(self):
print("Computing antichain")
temp = copy.deepcopy(self)
decomposition = []
vulnIdents = None
decomposition.append(temp.leaves)
vulnIdents = decomposition[-1]
temp = temp.pruneLeaves()
while(len(temp.blocks)>0 and len(decomposition) < self.antichainCutoff):
decomposition.append(temp.leaves)
for xid in decomposition[-1]:
if xid not in vulnIdents:
vulnIdents.update({xid:decomposition[-1][xid]})
temp = temp.pruneLeaves()
return decomposition, vulnIdents
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
class Test_RoBlock(unittest.TestCase):
def test_BlockHandler(self):
R = BlockHandler()
b = Block()
b.data = "zirconium encrusted tweezers"
b._recomputeIdent()
R._addBlock(b)
b = Block()
b.data = "brontosaurus slippers cannot exist"
b.addParents(R.leaves)
R._addBlock(b)
R.vote()
suite = unittest.TestLoader().loadTestsFromTestCase(Test_RoBlock)
unittest.TextTestRunner(verbosity=1).run(suite)