mirror of
https://github.com/monero-project/research-lab.git
synced 2024-12-22 19:49:35 +00:00
158 lines
6.4 KiB
Python
158 lines
6.4 KiB
Python
from Block import *
|
|
|
|
class Blockchain(object):
|
|
'''
|
|
Not a true blockchain, of course, but tracks block objects (timestamps) as above.
|
|
Each node should be responsible for finding the chain with most cumulative work.
|
|
Right now we assume Nakamoto consensus (konsensnakamoto).
|
|
'''
|
|
def __init__(self, params=[], verbosity=True):
|
|
self.blocks = {}
|
|
self.leaves = {}
|
|
self.miningIdents = None
|
|
self.verbose = verbosity
|
|
|
|
def addBlock(self, blockToAdd):
|
|
# In our model we assume difficulty scores of blocks are correct (otherwise they would
|
|
# be rejected in the real life network, and we aren't trying to model spam attacks).
|
|
try:
|
|
assert blockToAdd.ident not in self.blocks
|
|
except AssertionError:
|
|
print("Error, tried to add block that already exists in blockchain.")
|
|
else:
|
|
self.blocks.update({blockToAdd.ident:blockToAdd})
|
|
self.leaves.update({blockToAdd.ident:blockToAdd})
|
|
if blockToAdd.parent in self.leaves:
|
|
del self.leaves[blockToAdd.parent]
|
|
self.whichLeaf()
|
|
|
|
def whichLeaf(self):
|
|
# Determine which leaf shall be the parent leaf.
|
|
# If the chain has forked *ever* this will not be the case.
|
|
maxCumDiff = 0.0
|
|
self.miningIdents = []
|
|
for ident in self.leaves:
|
|
tempCumDiff = 0.0
|
|
thisBlockIdent = ident
|
|
tempCumDiff += self.blocks[thisBlockIdent].diff
|
|
while self.blocks[thisBlockIdent].parent is not None:
|
|
thisBlockIdent = self.blocks[thisBlockIdent].parent
|
|
tempCumDiff += self.blocks[thisBlockIdent].diff
|
|
if tempCumDiff > maxCumDiff:
|
|
# If more than one leaf ties for maxCumDiff, each node in the
|
|
# network should pick one of these two arbitrarily. Since we
|
|
# are storing each blockchain in a hash table (unordered!), for
|
|
# each node in the network that observes a tie, each possible leaf
|
|
# is equally likely to have been the first one found! So
|
|
# we don't need to do anything for the node to select which chain
|
|
# to work off of.
|
|
self.miningIdents = [ident]
|
|
maxCumDiff = tempCumDiff
|
|
elif tempCumDiff == maxCumDiff:
|
|
self.miningIdents.append(ident)
|
|
#print("leaf ident = ", str(ident), ", and tempCumDiff = ", str(tempCumDiff), " and maxCumDiff = ", str(maxCumDiff))
|
|
|
|
|
|
class Test_Blockchain(unittest.TestCase):
|
|
def test_bc(self):
|
|
bill = Blockchain([], verbosity=True)
|
|
|
|
name = newIdent(0)
|
|
t = time.time()
|
|
s = t+1
|
|
diff = 1.0
|
|
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
|
|
genesis = Block(params)
|
|
|
|
self.assertEqual(genesis.ident,name)
|
|
self.assertEqual(genesis.discoTimestamp,t)
|
|
self.assertEqual(genesis.arrivTimestamp,t+1)
|
|
self.assertTrue(genesis.parent is None)
|
|
self.assertEqual(genesis.diff,diff)
|
|
|
|
bill.addBlock(genesis)
|
|
|
|
self.assertTrue(genesis.ident in bill.blocks)
|
|
self.assertTrue(genesis.ident in bill.leaves)
|
|
self.assertEqual(len(bill.miningIdents),1)
|
|
self.assertEqual(genesis.ident, bill.miningIdents[0])
|
|
|
|
name = newIdent(1)
|
|
t = time.time()
|
|
s = t+1
|
|
diff = 2.0
|
|
params = {"ident":name, "disco":t, "arriv":s, "parent":genesis.ident, "diff":diff}
|
|
blockA = Block(params)
|
|
bill.addBlock(blockA)
|
|
|
|
bill.whichLeaf()
|
|
|
|
self.assertTrue(blockA.ident in bill.blocks)
|
|
self.assertTrue(blockA.ident in bill.leaves)
|
|
self.assertFalse(genesis.ident in bill.leaves)
|
|
self.assertTrue(genesis.ident in bill.blocks)
|
|
self.assertEqual(len(bill.miningIdents),1)
|
|
self.assertEqual(blockA.ident, bill.miningIdents[0])
|
|
|
|
name = newIdent(1)
|
|
t = time.time()
|
|
s = t+1
|
|
diff = 2.0
|
|
params = {"ident":name, "disco":t, "arriv":s, "parent":genesis.ident, "diff":diff}
|
|
blockB = Block(params)
|
|
bill.addBlock(blockB)
|
|
|
|
self.assertTrue(blockB.ident in bill.blocks)
|
|
self.assertTrue(blockB.ident in bill.leaves)
|
|
self.assertEqual(bill.blocks[blockB.ident].parent, genesis.ident)
|
|
|
|
self.assertTrue(blockA.ident in bill.blocks)
|
|
self.assertTrue(blockA.ident in bill.leaves)
|
|
self.assertEqual(bill.blocks[blockA.ident].parent, genesis.ident)
|
|
|
|
self.assertTrue(genesis.ident in bill.blocks)
|
|
self.assertFalse(genesis.ident in bill.leaves)
|
|
self.assertTrue(bill.blocks[genesis.ident].parent is None)
|
|
|
|
bill.whichLeaf()
|
|
print(bill.miningIdents)
|
|
|
|
self.assertEqual(type(bill.miningIdents), type([]))
|
|
self.assertTrue(len(bill.miningIdents), 2)
|
|
|
|
name = newIdent(2)
|
|
t = time.time()
|
|
diff = 3.14159
|
|
params = {"ident":name, "disco":t, "arriv":s, "parent":blockB.ident, "diff":diff}
|
|
blockC = Block(params)
|
|
bill.addBlock(blockC)
|
|
|
|
self.assertTrue(blockC.ident in bill.blocks)
|
|
self.assertTrue(blockC.ident in bill.leaves)
|
|
|
|
self.assertTrue(blockB.ident in bill.blocks)
|
|
self.assertFalse(blockB.ident in bill.leaves)
|
|
|
|
self.assertTrue(blockA.ident in bill.blocks)
|
|
self.assertTrue(blockA.ident in bill.leaves)
|
|
|
|
self.assertTrue(genesis.ident in bill.blocks)
|
|
self.assertFalse(genesis.ident in bill.leaves)
|
|
|
|
bill.whichLeaf()
|
|
|
|
#for blockIdent in bill.blocks:
|
|
# ident = bill.blocks[blockIdent].ident
|
|
# disco = bill.blocks[blockIdent].discoTimestamp
|
|
# arriv = bill.blocks[blockIdent].arrivTimestamp
|
|
# parent = bill.blocks[blockIdent].parent
|
|
# diff = bill.blocks[blockIdent].diff
|
|
# print(str(ident) + ", " + str(disco) + ", " + str(arriv) + ", " + str(parent) + ", " + str(diff) + ", " + str() + "\n")
|
|
#print(bill.miningIdents)
|
|
self.assertEqual(len(bill.miningIdents), 1)
|
|
self.assertEqual(bill.miningIdents[0], blockC.ident)
|
|
|
|
|
|
|
|
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Blockchain)
|
|
unittest.TextTestRunner(verbosity=1).run(suite)
|