mirror of
https://github.com/monero-project/research-lab.git
synced 2024-12-23 03:59:40 +00:00
668 lines
34 KiB
Python
668 lines
34 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 *
|
|
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)
|