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 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) """ """
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): def __init__(self):
self.id = "" # string # Initialize with empty payload, no identity, and empty parents.
self.timestamp = None # format tbd self.data = None
self.data = None # payload self.ident = hash(str(0))
self.parents = {} # block ID : pointer to block self.parents = None
self.children = {} # block ID : pointer to block self.addParents({})
def addChild(self, childIn):
if childIn not in self.children: def addParents(self, parentsIn): # dict of parents
self.children.update({childIn.id:childIn}) if self.parents is None:
def addChildren(self, childrenIn): self.parents = parentsIn
for child in childrenIn: else:
self.addChild(childrenIn[child]) self.parents.update(parentsIn)
def addParent(self, parentIn): self._recomputeIdent()
if parentIn not in self.parents:
self.parents.update({parentIn.id:parentIn}) def _recomputeIdent(self):
def addParents(self, parentsIn): m = str(0) + str(self.data) + str(self.parents)
for parent in parentsIn: self.ident = hash(m)
self.addParent(parentsIn[parent])
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) b5 = Block()
self.assertTrue(len(b4.parents)==1) b5.data = {"timestamp" : time.time(), "txns" : "stuff" }
self.assertTrue("2" in b4.parents) b5.addParents({b3.ident:b3})
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block) self.assertTrue(len(b1.parents)==1 and b0.ident in b1.parents)
unittest.TextTestRunner(verbosity=1).run(suite) 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)