Testing new versions

This commit is contained in:
b-g-goodell 2017-12-12 11:15:56 -07:00
parent 807d29ac0a
commit 41c8b73f2b
2 changed files with 313 additions and 60 deletions

View file

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

@ -0,0 +1,245 @@
'''
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 RoBlocks object.
'''
from Block import *
class RoBlocks(object):
def __init__(self):
print("Initializing")
# Initialize a RoBlocks 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 = RoBlocks()
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_RoBlocks(self):
R = RoBlocks()
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)