Block and blockchain working, computing fucking difficulty correctly finally

This commit is contained in:
Brandon Goodell 2018-02-08 13:06:01 -07:00
parent 7c8aa16609
commit f51f35669a
13 changed files with 6752 additions and 130 deletions

View file

@ -51,9 +51,49 @@ class Blockchain(object):
elif tempCumDiff == maxCumDiff:
self.miningIdents.append(ident)
#print("leaf ident = ", str(ident), ", and tempCumDiff = ", str(tempCumDiff), " and maxCumDiff = ", str(maxCumDiff))
assert len(self.miningIdents) > 0
class Test_Blockchain(unittest.TestCase):
def test_addBlock(self):
bill = Blockchain([], verbosity=True)
name = newIdent(0)
t = time.time()
s = t+random.random()
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,s)
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])
self.assertEqual(len(bill.blocks),1)
name = newIdent(1)
t = time.time()
s = t+random.random()
diff = 1.0
params = {"ident":name, "disco":t, "arriv":s, "parent":genesis.ident, "diff":diff}
blockA = Block(params)
bill.addBlock(blockA)
self.assertTrue(blockA.ident in bill.blocks)
self.assertTrue(blockA.ident in bill.leaves)
self.assertTrue(genesis.ident not in bill.leaves)
self.assertEqual(len(bill.miningIdents),1)
self.assertEqual(blockA.ident, bill.miningIdents[0])
self.assertEqual(len(bill.blocks),2)
def test_bc(self):
bill = Blockchain([], verbosity=True)
@ -115,7 +155,7 @@ class Test_Blockchain(unittest.TestCase):
self.assertTrue(bill.blocks[genesis.ident].parent is None)
bill.whichLeaf()
print(bill.miningIdents)
#print(bill.miningIdents)
self.assertEqual(type(bill.miningIdents), type([]))
self.assertTrue(len(bill.miningIdents), 2)

View file

@ -1,4 +1,5 @@
from Blockchain import *
import copy
class Node(object):
'''
@ -17,12 +18,9 @@ class Node(object):
self.edges = {}
def updateBlockchain(self, incBlocks, diffUpdateRate=1, mode="Nakamoto", targetRate=1.0/1209600.0):
# dataToUpdate shall be a dictionary of block identities (as keys) and their associated blocks (as values)
# incBlocks shall be a dictionary of block identities (as keys) and their associated blocks (as values)
# to be added to the local data. We assume difficulty scores have been reported honestly for now.
# Stash a copy of incoming blocks so removing keys won't shrink the size of the dictionary over which
# we are looping.
if self.verbose:
print("\t\t Updating blockchain.")
@ -36,18 +34,19 @@ class Node(object):
if self.verbose:
print("\t\t Now tempData has " + str(len(tempData)) + " entries.")
for key in incBlocks.blocks:
for key in incBlocks:
if key in self.data["blockchain"].blocks:
del tempData[key]
elif incBlocks.blocks[key].parent in self.data["blockchain"].blocks or incBlocks[key].parent is None:
self.data["blockchain"].addBlock(incBlocks.blocks[key])
elif incBlocks[key].parent in self.data["blockchain"].blocks or incBlocks[key].parent is None:
self.data["blockchain"].addBlock(incBlocks[key])
self.data["blockchain"].whichLeaf()
#if len(self.data["blockchain"]) % diffUpdateRate == 0:
# self.updateDifficulty(mode, targetRate)
del tempData[key]
incBlocks = copy.deepcopy(tempData)
if self.verbose:
print("\t\t Now incBlocks has " + str(len(incBlocks.blocks)) + " entries.")
print("\t\t Now incBlocks has " + str(len(incBlocks)) + " entries.")
if self.verbose:
print("\t\tRemaining steps (while loop)")
@ -58,8 +57,9 @@ class Node(object):
for key in incBlocks:
if key in self.data["blockchain"].blocks:
del tempData[key]
elif incBlocks.blocks[key].parent in self.data["blockchain"].blocks:
self.data["blockchain"].addBlock(incBlocks.blocks[key])
elif incBlocks[key].parent in self.data["blockchain"].blocks:
self.data["blockchain"].addBlock(incBlocks[key])
self.data["blockchain"].whichLeaf()
del tempData[key]
incBlocks = copy.deepcopy(tempData)
if self.verbose:
@ -71,9 +71,15 @@ class Node(object):
# Note for default, targetRate = two weeks/period, seven days/week, 24 hours/day, 60 minutes/hour, 60 seconds/minute) = 1209600 seconds/period
if mode=="Nakamoto":
# Use MLE estimate of poisson process, compare to targetRate, update by multiplying by resulting ratio.
if self.verbose:
print("Beginning update of difficulty with Nakamoto method")
count = 2016
bc = self.data["blockchain"]
ident = bc.miningIdent
if self.verbose:
print("Checking that blockchain is 2016*n blocks long and some mining identity has been set")
if len(bc.blocks) % 2016 == 0 and len(bc.miningIdents) > 0:
ident = random.choice(bc.miningIdents)
topTime = copy.deepcopy(bc.blocks[ident].discoTimestamp)
parent = bc.blocks[ident].parent
count = count - 1
@ -89,11 +95,17 @@ class Node(object):
botTime = copy.deepcopy(bc.blocks[ident].discoTimestamp)
# Algebra is okay:
assert 0 <= 2016 - count and 2016 - count < 2017
assert topTime != botTime
# MLE estimate of arrivals per second:
mleDiscoRate = float(2016 - count)/float(topTime - botTime)
mleDiscoRate = float(2015)/float(topTime - botTime)
# Rates can't be negative, but this estimate could be (although it's highly unlikely given Bitcoin's standard choices
# of difficulty update rate, etc.
mleDiscoRate = abs(mleDiscoRate)
if self.verbose:
print("MLE disco rate = " + str(mleDiscoRate) + " and targetRate = " + str(targetRate))
# Rate must be positive... so the MLE for block arrival rate
# assuming a Poisson process _is not even well-defined_ as
# an estimate for block arrival rate assuming timestamps are
@ -101,38 +113,66 @@ class Node(object):
# We use it nonetheless.
# How much should difficulty change?
self.diff = self.diff*(mleDiscoRate/targetRate)
if self.verbose:
print("MLE discovery rate = " + str(mleDiscoRate))
print("Difficulty before adjustment = " + str(self.diff))
# Update difficulty multiplicatively
self.diff = self.diff*mleDiscoRate/targetRate
if self.verbose:
print("Difficulty after adjustment = ", str(self.diff))
elif mode=="vanSaberhagen":
# Similar to above, except use 1200 blocks, discard top 120 and bottom 120 after sorting.
# 4 minute blocks in the original cryptonote, I believe... targetRate = 1.0/
# 4 minutes/period, 60 seconds/minute ~ 240 seconds/period
assert targetRate==1.0/240.0
# assert targetRate==1.0/240.0
count = 1200
ident = self.data.miningIdent
bc = self.data["blockchain"]
bc.whichLeaf()
assert self.diff != 0.0
if len(bc.blocks) > 120:
assert type(bc.miningIdents)==type([])
assert len(bc.miningIdents) > 0
ident = random.choice(bc.miningIdents)
bl = []
bl.append(copy.deepcopy(self.data.blocks[ident].discoTimestamp))
parent = self.data.blocks[ident].parent
bl.append(copy.deepcopy(bc.blocks[ident].discoTimestamp))
parent = bc.blocks[ident].parent
count = count - 1
while count > 0 and parent is not NOne:
while count > 0 and parent is not None:
ident = copy.deepcopy(parent)
bl.append(copy.deepcopy(self.data.blocks[ident].discoTimestamp))
parent = self.data.blocks[ident].parent
bl.append(copy.deepcopy(bc.blocks[ident].discoTimestamp))
parent = bc.blocks[ident].parent
count = count-1
# sort
bl = sorted(bl)
assert len(bl)<=1200
#print("Sample size = " + str(len(bl)))
# remove 10 and 90 %-iles
numOutliers = round(len(bl)/5)//2
assert numOutliers <= 120
#print("Number of outliers = " + str(numOutliers))
if numOutliers > 0:
bl = bl[numOutliers:-numOutliers]
#print("New Sample Size = " + str(len(bl)))
# remove outliers
bl = bl[120:-120]
# get topTime and botTime
if self.verbose:
print("bl[0] = " + str(bl[0]) + ",\tbl[-1] = " + str(bl[-1]))
topTime = bl[-1]
botTime = bl[0]
if self.verbose:
print("list of timestamps = " + str(bl))
print("topTime = " + str(bl[-1]))
print("botTime = " + str(bl[0]))
# Assert algebra will work
assert 0 <= 960 - count and 960 - count < 961
assert topTime > botTime
# 1200 - 2*120 = 1200 - 240 = 960
assert 0 < len(bl) and len(bl) < 961
assert topTime - botTime >= 0.0
# Sort of the MLE: # blocks/difference in reported times
# But not the MLE, since the reported times may not be
@ -158,20 +198,26 @@ class Node(object):
# lambda ~ (1.0/(stdev))*(2/skewness)**(1.0/3.0)
assert targetRate==1.0/120.0
count = 1200
ident = self.data.miningIdent
bc = self.data["blockchain"]
if len(bc.miningIdents) > 0:
ident = random.choice(bc.miningIdents)
bl = []
bl.append(copy.deepcopy(self.data.blocks[ident].discoTimestamp))
parent = self.data.blocks[ident].parent
bl.append(copy.deepcopy(bc.blocks[ident].discoTimestamp))
parent = bc.blocks[ident].parent
count = count - 1
while count > 0 and parent is not NOne:
while count > 0 and parent is not None:
ident = copy.deepcopy(parent)
bl.append(copy.deepcopy(self.data.blocks[ident].discoTimestamp))
parent = self.data.blocks[ident].parent
bl.append(copy.deepcopy(bc.blocks[ident].discoTimestamp))
parent = bc.blocks[ident].parent
count = count-1
if len(bl) > 120:
sk = skew(bl)
va = var(bl)
stdv = sqrt(va)
lam = (1.0/stdv)*(2.0/sk)**(1.0/3.0)
else:
lam = targetRate # we will not change difficulty unless we have at least 120 blocks of data (arbitrarily selected)
self.diff = self.diff*(lam/targetRate)
else:
print("Error, invalid difficulty mode entered.")
@ -193,77 +239,701 @@ class Node(object):
class Test_Node(unittest.TestCase):
def test_node(self):
verbose = True
nellyIdent = newIdent(0)
bill = Blockchain([], verbosity=verbose)
# TODO test each method separately
'''def test_nakamoto(self):
print("Beginning test of Nakamoto difficulty adjustment")
print("Setting initial values")
target = 100.0 # rate = blocks/s
verbose = False
deltaT = 1.0/target # forced wait time
arrivalList = []
mode="Nakamoto"
print("Generating node")
nellyIdent = newIdent(0)
offset = random.random()
intensity = random.random()
print("Generating initial blockchain")
# Create a new initial blockchain object
bill = Blockchain([], verbosity=verbose)
name = newIdent(0)
t = time.time()
s = t+1
t += offset
arrivalList.append(t)
s = t+random.random()
diff = 1.0
oldDiff = copy.deepcopy(diff)
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
genesis = Block(params)
print("Adding block")
bill.addBlock(genesis)
bill.whichLeaf()
# Check that it consists only of the genesis block
self.assertTrue(len(bill.blocks)==1)
self.assertTrue(genesis.ident in bill.blocks)
self.assertTrue(genesis.parent is None)
print("Finish creating node")
# Create node with this blockchain.
nodeData = {"blockchain":bill, "intensity":intensity, "offset":offset}
params = {"ident":nellyIdent, "data":nodeData, "diff":diff, "verbose":verbose}
nelly = Node(params)
# Check node creation worked
self.assertEqual(nelly.ident, nellyIdent)
self.assertEqual(nelly.data["blockchain"], bill)
self.assertEqual(nelly.diff, diff)
self.assertEqual(nelly.data["intensity"], intensity)
self.assertEqual(nelly.data["offset"], offset)
# Sleep and add a block on top of genesis
if verbose:
print("sleeping")
time.sleep(deltaT)
print("Giving genesis block a child")
name = newIdent(1)
t = time.time()
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
diff = oldDiff
params = {"ident":name, "disco":t, "arriv":s, "parent":genesis.ident, "diff":diff}
blockA = Block(params)
nelly.updateBlockchain({blockA.ident:blockA})
oldIdent = blockA.ident
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),2)
self.assertTrue(blockA.ident in nelly.data["blockchain"].blocks)
self.assertTrue(genesis.ident in nelly.data["blockchain"].blocks)
self.assertEqual(genesis.ident, nelly.data["blockchain"].blocks[blockA.ident].parent)
print("Updating difficulty")
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With only two blocks, nothing should change.
self.assertEqual(nelly.diff, oldDiff)
# Print regardless of verbosity:
print("Now generating first difficulty adjustment period.")
# Now we are going to fast forward to right before the first difficulty adjustment.
N = len(nelly.data["blockchain"].blocks)
while(N < 2015):
if N % 100 == 0:
print("\tN=" + str(N))
name = newIdent(N)
t = time.time()
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
diff = nelly.diff
oldDiff = diff
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":diff}
oldIdent = copy.deepcopy(name)
block = Block(params)
nelly.updateBlockchain({block.ident:block})
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With N < 2016, nothing should change.
self.assertEqual(nelly.diff, oldDiff)
N = len(nelly.data["blockchain"].blocks)
time.sleep(deltaT)
name = newIdent(N)
t = time.time()
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
diff = oldDiff
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":diff}
block = Block(params)
nelly.updateBlockchain({block.ident:block})
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With N < 2016, nothing should change.
# Note: 2016 blocks is 2015 block inter-arrival times.
expRatioNumerator = float(2015)/(arrivalList[-1] - arrivalList[-2016])
expRatio = expRatioNumerator/target
expDiff = oldDiff*expRatio
self.assertEqual(nelly.diff, expDiff)
# The following should fail, because our sample size is incorrect.
expRatioNumerator = float(2016)/(arrivalList[-1] - arrivalList[-2016])
expRatio = expRatioNumerator/target
expDiff = oldDiff*expRatio
self.assertFalse(nelly.diff - expDiff == 0.0)
# Print regardless of verbosity:
print("Now generating second difficulty adjustment period.")
# Now we are going to fast forward to right before the next difficulty adjustment.
# This time, though, we are going to re-set the block inter-arrival time deltaT
# to half. This should drive difficulty up.
lastDifficultyScore = copy.deepcopy(nelly.diff)
N = len(nelly.data["blockchain"].blocks)
while(N < 4031):
if N % 100 == 0:
print("\tN=" + str(N))
name = newIdent(N)
t = time.time()
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
diff = nelly.diff
oldDiff = diff
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":diff}
oldIdent = copy.deepcopy(name)
block = Block(params)
nelly.updateBlockchain({block.ident:block})
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With N < 2016, nothing should change.
self.assertEqual(nelly.diff, oldDiff)
N = len(nelly.data["blockchain"].blocks)
time.sleep(0.01*deltaT)
# Now if we add a single new block, we should trigger difficulty adjustment.
name = newIdent(N)
t = time.time()
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
diff = oldDiff
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":diff}
block = Block(params)
nelly.updateBlockchain({block.ident:block})
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
# Update the difficulty score.
nelly.updateDifficulty(mode, targetRate = target)
expRatioNumerator = float(2015)/(arrivalList[-1] - arrivalList[-2016])
expRatio = expRatioNumerator/target
expDiff = oldDiff*expRatio
print("expRatio = " + str(expRatio) + ", lastDifficultyScore = " + str(lastDifficultyScore) + ", new difficulty = " + str(nelly.diff))
self.assertEqual(nelly.diff, expDiff)
'''
def test_vs(self):
print("Beginning test of vanSaberhagen difficulty adjustment.")
print("Setting initial values")
target = 10.0 # 1.0/240.0 # rate = blocks/s
verbose = False
deltaT = 1.0/target # forced wait time
arrivalList = []
mode="vanSaberhagen"
print("Instantiating new node")
nellyIdent = newIdent(0)
offset = random.random()
intensity = random.random()
print("Creating new blockchain for new node")
# Create a new initial blockchain object
bill = Blockchain([], verbosity=verbose)
name = newIdent(0)
t = time.time()
t += offset
arrivalList.append(t)
s = t+random.random()
diff = 1.0
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
genesis = Block(params)
print("Adding genesis block")
bill.addBlock(genesis)
bill.whichLeaf()
time.sleep(10)
# Check that it consists only of the genesis block
self.assertTrue(len(bill.blocks)==1)
self.assertTrue(genesis.ident in bill.blocks)
self.assertTrue(genesis.parent is None)
self.assertTrue(genesis.ident in bill.leaves)
print("Making node")
# Create node with this blockchain.
nodeData = {"blockchain":bill, "intensity":intensity, "offset":offset}
params = {"ident":nellyIdent, "data":nodeData, "diff":diff, "verbose":verbose}
nelly = Node(params)
# Check node creation worked
self.assertEqual(nelly.ident, nellyIdent)
self.assertEqual(nelly.data["blockchain"], bill)
self.assertEqual(nelly.diff, diff)
self.assertEqual(nelly.data["intensity"], intensity)
self.assertEqual(nelly.data["offset"], offset)
# Sleep and add a block on top of genesis
if verbose:
print("sleeping")
time.sleep(deltaT)
print("Give genesis a child")
name = newIdent(1)
t = time.time()
s = t+1
diff = 1.0
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
oldDiff = copy.deepcopy(diff)
diff = copy.deepcopy(nelly.diff)
assert diff != 0.0
params = {"ident":name, "disco":t, "arriv":s, "parent":genesis.ident, "diff":diff}
blockA = Block(params)
bill.addBlock(blockA)
nelly.updateBlockchain({blockA.ident:blockA})
oldIdent = blockA.ident
# Nodes need an identity and a blockchain object and verbosity and difficulty
nodeData = {"blockchain":bill, "intensity":random.random(), "offset":random.random()}
params = {"ident":name, "data":nodeData, "diff":diff, "verbose":verbose}
nelly = Node(params)
nelly.updateDifficulty(mode="Nakamoto")
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),2)
self.assertTrue(blockA.ident in nelly.data["blockchain"].blocks)
self.assertTrue(genesis.ident in nelly.data["blockchain"].blocks)
self.assertEqual(genesis.ident, nelly.data["blockchain"].blocks[blockA.ident].parent)
time.sleep(9)
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With only two blocks, nothing should change.
assert nelly.diff != 0.0
self.assertEqual(nelly.diff, oldDiff)
self.assertFalse(nelly.diff == -0.0)
name = newIdent(len(nelly.data))
# Print regardless of verbosity:
print("Now generating fulls sample size.")
# Now we are going to fast forward to a "full sample size" period of time.
N = len(nelly.data["blockchain"].blocks)
while(N < 1200):
name = newIdent(N)
if N % 100 == 0:
print("\tNow adding block N=" + str(N))
t = time.time()
s = t + 1
params = {"ident":name, "disco":t, "arriv":s, "parent":blockA.ident, "diff":diff}
blockB = Block(params)
nelly.updateBlockchain({blockB.ident:blockB})
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
oldDiff = copy.deepcopy(diff)
diff = copy.deepcopy(nelly.diff)
assert diff != 0.0
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":diff}
oldIdent = copy.deepcopy(name)
block = Block(params)
nelly.updateBlockchain({block.ident:block})
time.sleep(8)
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
name = newIdent(len(nelly.data))
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With N < 100, nothing should change.
if N < 100:
self.assertEqual(nelly.diff, oldDiff)
N = len(nelly.data["blockchain"].blocks)
time.sleep(0.5*deltaT)
print("Adding one more block")
name = newIdent(N)
t = time.time()
s = t + 1
params = {"ident":name, "disco":t, "arriv":s, "parent":blockA.ident, "diff":diff}
blockC = Block(params)
nelly.updateBlockchain({blockC.ident:blockC})
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
oldDiff = diff
diff = nelly.diff
assert diff != 0.0
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":nelly.diff}
block = Block(params)
nelly.updateBlockchain({block.ident:block})
time.sleep(1)
name = newIdent(len(nelly.data))
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With N < 2016, nothing should change.
# Note: 2016 blocks is 2015 block inter-arrival times.
print(str(arrivalList[-120]) + ", " + str(arrivalList[-1080]) + ", " + str(arrivalList[-120]-arrivalList[-1080]) + ", " + str(float(959)/(arrivalList[-120]-arrivalList[-1080]))+ ", " + str(float(float(959)/(arrivalList[-120]-arrivalList[-1080]))/float(target)))
expRatioNumerator = float(959)/(arrivalList[-120] - arrivalList[-1080])
expRatio = expRatioNumerator/target
print(expRatio)
expDiff = oldDiff*expRatio
print(expDiff)
print("expDiff = " + str(expDiff) + " and nelly.diff = " + str(nelly.diff))
self.assertEqual(nelly.diff, expDiff)
# Print regardless of verbosity:
print("Now fast forwarding past the tail end of the last period..")
# Now we are going to fast forward to right before the next difficulty adjustment.
# This time, though, we are going to re-set the block inter-arrival time deltaT
# to half. This should drive difficulty up.
lastDifficultyScore = copy.deepcopy(nelly.diff)
N = len(nelly.data["blockchain"].blocks)
while(N < 1700):
name = newIdent(N)
t = time.time()
s = t + 1
params = {"ident":name, "disco":t, "arriv":s, "parent":blockC.ident, "diff":diff}
blockD = Block(params)
nelly.updateBlockchain({blockD.ident:blockD})
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
diff = nelly.diff
oldDiff = diff
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":diff}
oldIdent = copy.deepcopy(name)
block = Block(params)
nelly.updateBlockchain({block.ident:block})
time.sleep(7)
name = newIdent(len(nelly.data))
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
# Update the difficulty score
nelly.updateDifficulty(mode, targetRate = target) # With N < 2016, nothing should change.
self.assertEqual(nelly.diff, oldDiff)
N = len(nelly.data["blockchain"].blocks)
time.sleep(0.01*deltaT)
# Now if we add a single new block, we should trigger difficulty adjustment.
name = newIdent(N)
t = time.time()
s = t + 1
params = {"ident":name, "disco":t, "arriv":s, "parent":blockD.ident, "diff":diff}
blockE = Block(params)
nelly.updateBlockchain({blockE.ident:blockE})
t += nelly.data["offset"]
arrivalList.append(t)
s = t+random.random()
diff = oldDiff
params = {"ident":name, "disco":t, "arriv":s, "parent":oldIdent, "diff":diff}
block = Block(params)
nelly.updateBlockchain({block.ident:block})
# Check this worked
self.assertEqual(len(nelly.data["blockchain"].blocks),N+1)
self.assertTrue(block.ident in nelly.data["blockchain"].blocks)
# Update the difficulty score.
nelly.updateDifficulty(mode, targetRate = target)
expRatioNumerator = float(959)/(arrivalList[-120] - arrivalList[-1080])
expRatio = expRatioNumerator/target
expDiff = oldDiff*expRatio
print("expRatio = " + str(expRatio) + ", lastDifficultyScore = " + str(lastDifficultyScore) + ", new difficulty = " + str(nelly.diff))
self.assertEqual(nelly.diff, expDiff)
time.sleep(6)
name = newIdent(len(nelly.data))
def test_modexp(self):
pass
'''# Check this worked
if mode == "Nakamoto":
# In this case we take simple MLE estimate
ratio = 1.0/abs(t1-t)
print("Nakamoto mle = " + str(ratio))
ratio = ratio/target
print("Normalized = " + str(ratio))
print("New diff = " + str(ratio*oldDiff))
self.assertEqual(nelly.diff, ratio*oldDiff)
elif mode == "vanSaberhagen":
# In this case, with only 2 blocks, we just use simple MLE again
ratio = 1.0/abs(t1-t)
ratio = ratio/target
self.assertEqual(nelly.diff, ratio*oldDiff)
elif mode == "MOM:expModGauss":
self.assertEqual(nelly.diff, 1.0)
# With at least 120 blocks of data...
#sk = skew(bl)
#va = var(bl)
#stdv = sqrt(va)
#lam = (1.0/stdv)*(2.0/sk)**(1.0/3.0)
#self.diff = self.diff*(lam/targetRate)
# Otherwise, set to 1.0
else:
print("what world are you living in?")
if verbose:
print("sleeping 1 seconds")
time.sleep(deltaT/5.0)
listOfTimes = [copy.deepcopy(t), copy.deepcopy(t1)]
listOfBlocks = []
N = len(nelly.data["blockchain"].blocks)
lastIdent = blockA.ident
bail = False
while N < 10 and not bail:
# Generate new block
name = newIdent(N)
t = time.time()
s = t + 1
params = {"ident":name, "disco":t, "arriv":s, "parent":blockE.ident, "diff":diff}
blockF = Block(params)
nelly.updateBlockchain({blockF.ident:blockF})
t += nelly.data["offset"]
s = t+random.random()
oldDiff = copy.deepcopy(nelly.diff)
print("Current difficulty = ", oldDiff)
params = {"ident":name, "disco":t, "arriv":s, "parent":lastIdent, "diff":oldDiff}
newBlock = Block(params)
# Append new block to running list along with creation time
listOfBlocks.append(newBlock)
listOfTimes.append(copy.deepcopy(t))
# Update nelly's blockchain with newBlock
nelly.updateBlockchain({newBlock.ident:newBlock})
lastIdent = name
# Quick check that this worked:
self.assertTrue(name in nelly.data["blockchain"].blocks)
self.assertEqual(len(nelly.data["blockchain"].blocks), N+1)
N = len(nelly.data["blockchain"].blocks)
# Update difficulty
nelly.updateDifficulty(mode, targetRate = 100.0)
# Quick check that this worked:
if mode == "Nakamoto":
# In this case we take use top block and genesis block
ratio = float(len(nelly.data["blockchain"].blocks) - 1)/(listOfTimes[-1] - listOfTimes[0])
ratio = ratio / target
self.assertEqual(nelly.diff, ratio*oldDiff)
print("Hoped for difficulty = " + str(oldDiff*ratio) + ", and computed = " + str(nelly.diff))
elif mode == "vanSaberhagen":
# This case coincides with nakamoto until block 10
ratio = float( len(nelly.data["blockchain"].blocks) - 1)/(listOfTimes[-1] - listOfTimes[0])
ratio = ratio / target
self.assertEqual(nelly.diff, ratio*oldDiff)
elif mode == "MOM:expModGauss":
self.assertEqual(nelly.diff, 1.0)
# With at least 120 blocks of data...
#sk = skew(bl)
#va = var(bl)
#stdv = sqrt(va)
#lam = (1.0/stdv)*(2.0/sk)**(1.0/3.0)
#self.diff = self.diff*(lam/targetRate)
# Otherwise, set to 1.0
else:
print("what world are you living in?")
# Sleep a random time
print("Sleeping a random sub-second, working on block " + str(N))
deltaT = deltaT*ratio
time.sleep(deltaT/5.0)
while N < 120 and not bail:
# Generate new block
name = newIdent(N)
t = time.time()
t += nelly.data["offset"]
s = t+random.random()
oldDiff = copy.deepcopy(nelly.diff)
params = {"ident":name, "disco":t, "arriv":s, "parent":lastIdent, "diff":oldDiff}
newBlock = Block(params)
# Append new block to running list along with creation time
listOfBlocks.append(newBlock)
listOfTimes.append(copy.deepcopy(t))
# Update nelly's blockchain with newBlock
nelly.updateBlockchain({newBlock.ident:newBlock})
lastIdent = name
# Quick check that this worked:
self.assertTrue(name in nelly.data["blockchain"].blocks)
self.assertEqual(len(nelly.data["blockchain"].blocks), N+1)
N = len(nelly.data["blockchain"].blocks)
# Update difficulty
nelly.updateDifficulty(mode, targetRate = 100.0)
# Quick check that this worked:
if mode == "Nakamoto":
# In this case we take use top block and genesis block
ratio = float(len(nelly.data["blockchain"].blocks)-1)/(listOfTimes[-1] - listOfTimes[0])
ratio = ratio / target
self.assertEqual(nelly.diff, oldDiff*ratio)
print("Hoped for difficulty = " + str(oldDiff*ratio) + ", and computed = " + str(nelly.diff))
elif mode == "vanSaberhagen":
# This case no longer coincides with Nakamoto...
numOutliers = len(nelly.data["blockchain"].blocks)//10
numOutliers = min(numOutliers, 120)
ratio = float(len(nelly.data["blockchain"].blocks) - 2*numOutliers - 1)/(listOfTimes[-numOutliers] - listOfTimes[numOutliers])
ratio = ratio / target
self.assertEqual(nelly.diff, oldDiff*ratio)
elif mode == "MOM:expModGauss":
# With at least 120 blocks of data...
count = 1200
bl = []
bl.append(copy.deepcopy(bc.blocks[lastIdent].discoTimestamp))
parent = bc.blocks[lastIdent].parent
count = count - 1
while count > 0 and parent is not None:
ident = copy.deepcopy(parent)
bl.append(copy.deepcopy(bc.blocks[ident].discoTimestamp))
parent = bc.blocks[ident].parent
count = count-1
if len(bl) > 120:
sk = skew(bl)
va = var(bl)
stdv = sqrt(va)
lam = (1.0/stdv)*(2.0/sk)**(1.0/3.0)
else:
lam = target
ratio = lam/target
self.assertEqual(nelly.diff, oldDiff*ratio)
else:
print("what world are you living in?")
# Sleep a random time
print("Sleeping a random sub-second, working on block " + str(N))
deltaT = deltaT*ratio
time.sleep(deltaT/5.0)
while N < 2400 and not bail:
# Generate new block
name = newIdent(N)
t = time.time()
t += nelly.data["offset"]
s = t+random.random()
oldDiff = copy.deepcopy(nelly.diff)
params = {"ident":name, "disco":t, "arriv":s, "parent":lastIdent, "diff":oldDiff}
newBlock = Block(params)
# Append new block to running list along with creation time
listOfBlocks.append(newBlock)
listOfTimes.append(copy.deepcopy(t))
# Update nelly's blockchain with newBlock
nelly.updateBlockchain({newBlock.ident:newBlock})
lastIdent = name
# Quick check that this worked:
self.assertTrue(name in nelly.data["blockchain"].blocks)
self.assertEqual(len(nelly.data["blockchain"].blocks), N+1)
N = len(nelly.data["blockchain"].blocks)
# Update difficulty
nelly.updateDifficulty(mode, targetRate = 100.0)
# Quick check that this worked:
if mode == "Nakamoto":
# In this case we take use top block and genesis block
ratio = float(len(nelly.data["blockchain"].blocks)-1)/(listOfTimes[-1] - listOfTimes[0])
ratio = ratio / target
self.assertEqual(nelly.diff, oldDiff*ratio)
print("Hoped for difficulty = " + str(oldDiff*ratio) + ", and computed = " + str(nelly.diff))
elif mode == "vanSaberhagen":
# This case no longer coincides with Nakamoto...
numOutliers = len(nelly.data["blockchain"].blocks)//10
numOutliers = min(numOutliers, 120)
ratio = float(len(nelly.data["blockchain"].blocks) - 2*numOutliers - 1)/(listOfTimes[-numOutliers] - listOfTimes[numOutliers])
ratio = ratio / target
self.assertEqual(nelly.diff, ratio*oldDiff)
elif mode == "MOM:expModGauss":
# With at least 120 blocks of data...
count = 1200
bl = []
bl.append(copy.deepcopy(bc.blocks[lastIdent].discoTimestamp))
parent = bc.blocks[lastIdent].parent
count = count - 1
while count > 0 and parent is not None:
ident = copy.deepcopy(parent)
bl.append(copy.deepcopy(bc.blocks[ident].discoTimestamp))
parent = bc.blocks[ident].parent
count = count-1
if len(bl) > 120:
sk = skew(bl)
va = var(bl)
stdv = sqrt(va)
lam = (1.0/stdv)*(2.0/sk)**(1.0/3.0)
else:
lam = targetRate
ratio = lam/targetRate
self.assertEqual(nelly.diff, ratio*oldDiff)
else:
print("what world are you living in?")
# Sleep a random time
print("Sleeping a random sub-second, working on block " + str(N))
deltaT = deltaT*ratio
time.sleep(deltaT/5.0)
while N < 3600 and not bail:
# Generate new block
name = newIdent(N)
t = time.time()
t += nelly.data["offset"]
s = t+random.random()
oldDiff = nelly.diff
params = {"ident":name, "disco":t, "arriv":s, "parent":lastIdent, "diff":oldDiff}
newBlock = Block(params)
# Append new block to running list along with creation time
listOfBlocks.append(newBlock)
listOfTimes.append(copy.deepcopy(t))
# Update nelly's blockchain with newBlock
nelly.updateBlockchain({newBlock.ident:newBlock})
lastIdent = name
# Quick check that this worked:
self.assertTrue(name in nelly.data["blockchain"].blocks)
self.assertEqual(len(nelly.data["blockchain"].blocks), N+1)
N = len(nelly.data["blockchain"].blocks)
# Update difficulty
nelly.updateDifficulty(mode, targetRate = 100.0)
# Quick check that this worked:
if mode == "Nakamoto":
# In this case we take use top block and genesis block
ratio = float(2400)/(listOfTimes[-1] - listOfTimes[-2400])
self.assertEqual(nelly.diff, ratio*oldDiff)
elif mode == "vanSaberhagen":
# This case no longer coincides with Nakamoto...
numOutliers = len(nelly.data["blockchain"].blocks)//10
numOutliers = min(numOutliers, 120)
ratio = float(len(nelly.data["blockchain"].blocks) - 2*numOutliers)/(listOfTimes[-numOutliers] - listOfTimes[numOutliers])
self.assertEqual(nelly.diff, ratio*oldDiff)
elif mode == "MOM:expModGauss":
# With at least 120 blocks of data...
count = 1200
bl = []
bl.append(copy.deepcopy(bc.blocks[lastIdent].discoTimestamp))
parent = bc.blocks[lastIdent].parent
count = count - 1
while count > 0 and parent is not None:
ident = copy.deepcopy(parent)
bl.append(copy.deepcopy(bc.blocks[ident].discoTimestamp))
parent = bc.blocks[ident].parent
count = count-1
if len(bl) > 120:
sk = skew(bl)
va = var(bl)
stdv = sqrt(va)
lam = (1.0/stdv)*(2.0/sk)**(1.0/3.0)
else:
lam = targetRate
ratio = lam/targetRate
self.assertEqual(nelly.diff, ratio*oldDiff)
else:
print("what world are you living in?")
# Sleep a random time
print("Sleeping a random sub-second, working on block " + str(N))
deltaT = deltaT*ratio
time.sleep(deltaT/5.0)'''
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Node)

View file

@ -0,0 +1,46 @@
import unittest, random, time
def newIdent(params):
nonce = params
# Generate new random identity.
return hash(str(nonce) + str(random.random()))
#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####
class Block(object):
'''
Each block has: an identity, a timestamp of discovery (possibly false),
has a timestamp of arrival at the local node (possibly unnecessary), a
parent block's identity, and a difficulty score.
'''
def __init__(self, params={}):
self.ident = None
self.discoTimestamp = None
self.arrivTimestamp = None
self.parent = None
self.diff = None
try:
assert len(params)==5
except AssertionError:
print("Error in Block(): Tried to add a malformed block. We received params = " + str(params) + ", but should have had something of the form {\"ident\":ident, \"disco\":disco, \"arriv\":arriv, \"parent\":parent, \"diff\":diff}.")
self.ident = params["ident"]
self.discoTimestamp = params["disco"]
self.arrivTimestamp = params["arriv"]
self.parent = params["parent"]
self.diff = params["diff"]
class Test_Block(unittest.TestCase):
def test_b(self):
#bill = Block()
name = newIdent(0)
t = time.time()
s = t+1
diff = 1.0
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
bill = Block(params)
self.assertEqual(bill.ident,name)
self.assertEqual(bill.discoTimestamp,t)
self.assertEqual(bill.arrivTimestamp,t+1)
self.assertTrue(bill.parent is None)
self.assertEqual(bill.diff,diff)
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block)
unittest.TextTestRunner(verbosity=1).run(suite)

View file

@ -0,0 +1,46 @@
import unittest, random, time
def newIdent(params):
nonce = params
# Generate new random identity.
return hash(str(nonce) + str(random.random()))
#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####
class Block(object):
'''
Each block has: an identity, a timestamp of discovery (possibly false),
has a timestamp of arrival at the local node (possibly unnecessary), a
parent block's identity, and a difficulty score.
'''
def __init__(self, params={}):
self.ident = None
self.discoTimestamp = None
self.arrivTimestamp = None
self.parent = None
self.diff = None
try:
assert len(params)==5
except AssertionError:
print("Error in Block(): Tried to add a malformed block. We received params = " + str(params) + ", but should have had something of the form {\"ident\":ident, \"disco\":disco, \"arriv\":arriv, \"parent\":parent, \"diff\":diff}.")
self.ident = params["ident"]
self.discoTimestamp = params["disco"]
self.arrivTimestamp = params["arriv"]
self.parent = params["parent"]
self.diff = params["diff"]
class Test_Block(unittest.TestCase):
def test_b(self):
#bill = Block()
name = newIdent(0)
t = time.time()
s = t+1
diff = 1.0
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
bill = Block(params)
self.assertEqual(bill.ident,name)
self.assertEqual(bill.discoTimestamp,t)
self.assertEqual(bill.arrivTimestamp,t+1)
self.assertTrue(bill.parent is None)
self.assertEqual(bill.diff,diff)
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block)
unittest.TextTestRunner(verbosity=1).run(suite)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,116 @@
from Blockchain import *
class Node(object):
'''
Node object. params [identity, blockchain (data), verbosity, difficulty]
'''
def __init__(self, params={}):
try:
assert len(params)==5
except AssertionError:
print("Error, Tried to create malformed node.")
else:
self.ident = params["ident"]
self.data = params["data"]
self.verbose = params["verbose"]
self.edges = {}
self.mode = params["mode"]
self.targetRate = params["targetRate"]
def generateBlock(self, discoTime, nonce):
newName = newIdent(nonce)
t = discoTime
s = t+self.data["offset"]
diff = self.data["blockchain"].diff
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
newBlock = Block(params)
self.data["blockchain"].addBlock(newBlock, mode, tr)
return newName
def updateBlockchain(self, incBlocks):
# incBlocks shall be a dictionary of block identities (as keys) and their associated blocks (as values)
# to be added to the local data. We assume difficulty scores have been reported honestly for now.
tempData = copy.deepcopy(incBlocks)
for key in incBlocks:
if key in self.data["blockchain"].blocks:
del tempData[key]
elif incBlocks[key].parent in self.data["blockchain"].blocks or incBlocks[key].parent is None:
self.data["blockchain"].addBlock(incBlocks[key], self.mode, self.targetRate)
del tempData[key]
incBlocks = copy.deepcopy(tempData)
while len(incBlocks)>0:
for key in incBlocks:
if key in self.data["blockchain"].blocks:
del tempData[key]
elif incBlocks[key].parent in self.data["blockchain"].blocks:
self.data["blockchain"].addBlock(incBlocks[key], self.mode, self.targetRate)
del tempData[key]
incBlocks = copy.deepcopy(tempData)
def propagate(self, timeOfProp, blockIdent):
for edgeIdent in self.edges:
edge = self.edges[edgeIdent]
length = e.data["length"]
timeOfArrival = timeOfProp + length
otherIdent = e.getNeighbor(self.ident)
other = e.nodes[otherIdent]
bc = other.data["blockchain"]
if blockIdent not in bc.blocks:
pB = e.data["pendingBlocks"]
pendingIdent = newIdent(len(pB))
mybc = self.data["blockchain"]
blockToProp = mybc.blocks[blockIdent]
pendingDat = {"timeOfArrival":timeOfArrival, "destIdent":otherIdent, "block":blockToProp}
pB.update({pendingIdent:pendingDat})
class Test_Node(unittest.TestCase):
# TODO test each method separately
def test_all(self):
bill = Blockchain([], verbosity=True)
mode="Nakamoto"
tr = 1.0/600000.0
deltaT = 600000.0
bill.targetRate = tr
name = newIdent(0)
t = 0.0
s = t
diff = 1.0
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
genesis = Block(params)
bill.addBlock(genesis, mode, tr)
parent = genesis.ident
nellyname = newIdent(time.time())
mode = "Nakamoto"
targetRate = 1.0/600000.0
params = {"ident":nellyname, "data":{"offset":0.0, "intensity":1.0, "blockchain":bill}, "verbose":True, "mode":mode, "targetRate":targetRate}
nelly = Node(params)
while len(nelly.data["blockchain"].blocks) < 2015:
name = newIdent(0)
diff = nelly.data["blockchain"].diff
t += deltaT*diff*(2.0*random.random()-1.0)
s = t
params = {"ident":name, "disco":t, "arriv":s, "parent":parent, "diff":diff}
newBlock = Block(params)
bill.addBlock(newBlock, mode, tr)
parent = name
while len(nelly.data["blockchain"].blocks) < 5000:
name = newIdent(0)
diff = nelly.data["blockchain"].diff
t += deltaT*diff
s = t
params = {"ident":name, "disco":t, "arriv":s, "parent":parent, "diff":diff}
newBlock = Block(params)
bill.addBlock(newBlock, mode, tr)
parent = name
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Node)
unittest.TextTestRunner(verbosity=1).run(suite)

View file

@ -0,0 +1,116 @@
from Blockchain import *
class Node(object):
'''
Node object. params [identity, blockchain (data), verbosity, difficulty]
'''
def __init__(self, params={}):
try:
assert len(params)==5
except AssertionError:
print("Error, Tried to create malformed node.")
else:
self.ident = params["ident"]
self.data = params["data"]
self.verbose = params["verbose"]
self.edges = {}
self.mode = params["mode"]
self.targetRate = params["targetRate"]
def generateBlock(self, discoTime, nonce):
newName = newIdent(nonce)
t = discoTime
s = t+self.data["offset"]
diff = self.data["blockchain"].diff
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
newBlock = Block(params)
self.data["blockchain"].addBlock(newBlock, mode, tr)
return newName
def updateBlockchain(self, incBlocks):
# incBlocks shall be a dictionary of block identities (as keys) and their associated blocks (as values)
# to be added to the local data. We assume difficulty scores have been reported honestly for now.
tempData = copy.deepcopy(incBlocks)
for key in incBlocks:
if key in self.data["blockchain"].blocks:
del tempData[key]
elif incBlocks[key].parent in self.data["blockchain"].blocks or incBlocks[key].parent is None:
self.data["blockchain"].addBlock(incBlocks[key], self.mode, self.targetRate)
del tempData[key]
incBlocks = copy.deepcopy(tempData)
while len(incBlocks)>0:
for key in incBlocks:
if key in self.data["blockchain"].blocks:
del tempData[key]
elif incBlocks[key].parent in self.data["blockchain"].blocks:
self.data["blockchain"].addBlock(incBlocks[key], self.mode, self.targetRate)
del tempData[key]
incBlocks = copy.deepcopy(tempData)
def propagate(self, timeOfProp, blockIdent):
for edgeIdent in self.edges:
edge = self.edges[edgeIdent]
length = e.data["length"]
timeOfArrival = timeOfProp + length
otherIdent = e.getNeighbor(self.ident)
other = e.nodes[otherIdent]
bc = other.data["blockchain"]
if blockIdent not in bc.blocks:
pB = e.data["pendingBlocks"]
pendingIdent = newIdent(len(pB))
mybc = self.data["blockchain"]
blockToProp = mybc.blocks[blockIdent]
pendingDat = {"timeOfArrival":timeOfArrival, "destIdent":otherIdent, "block":blockToProp}
pB.update({pendingIdent:pendingDat})
class Test_Node(unittest.TestCase):
# TODO test each method separately
def test_all(self):
bill = Blockchain([], verbosity=True)
mode="Nakamoto"
tr = 1.0/600000.0
deltaT = 600000.0
bill.targetRate = tr
name = newIdent(0)
t = 0.0
s = t
diff = 1.0
params = {"ident":name, "disco":t, "arriv":s, "parent":None, "diff":diff}
genesis = Block(params)
bill.addBlock(genesis, mode, tr)
parent = genesis.ident
nellyname = newIdent(time.time())
mode = "Nakamoto"
targetRate = 1.0/600000.0
params = {"ident":nellyname, "data":{"offset":0.0, "intensity":1.0, "blockchain":bill}, "verbose":True, "mode":mode, "targetRate":targetRate}
nelly = Node(params)
while len(nelly.data["blockchain"].blocks) < 2015:
name = newIdent(0)
diff = nelly.data["blockchain"].diff
t += deltaT*diff*(2.0*random.random()-1.0)
s = t
params = {"ident":name, "disco":t, "arriv":s, "parent":parent, "diff":diff}
newBlock = Block(params)
bill.addBlock(newBlock, mode, tr)
parent = name
while len(nelly.data["blockchain"].blocks) < 5000:
name = newIdent(0)
diff = nelly.data["blockchain"].diff
t += deltaT*diff
s = t
params = {"ident":name, "disco":t, "arriv":s, "parent":parent, "diff":diff}
newBlock = Block(params)
bill.addBlock(newBlock, mode, tr)
parent = name
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Node)
unittest.TextTestRunner(verbosity=1).run(suite)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,602 @@
time,rateConstant,difficulty
0.0,1.0,1.0
1.0,1.0,1.0
2.0,1.0,1.0
3.0,1.0,1.0
4.0,1.0,1.0
5.0,1.0,1.0
6.0,1.0,1.0
7.0,1.0,1.0
8.0,1.0,1.0
9.0,1.0,1.0
10.0,1.0,1.0
11.0,1.0,1.0
12.0,1.0,1.0
13.0,1.0,1.0
14.0,1.0,1.0
15.0,1.0,1.0
16.0,1.0,1.0
17.0,1.0,1.0
18.0,1.0,1.0
19.0,1.0,1.0
20.0,1.0,1.0
21.0,1.0,1.0
22.0,1.0,1.0
23.0,1.0,1.0
24.0,1.0,1.0
25.0,1.0,1.0
26.0,1.0,1.0
27.0,1.0,1.0
28.0,1.0,1.0
29.0,1.0,1.0
30.0,1.0,1.0
31.0,1.0,1.0
32.0,1.0,1.0
33.0,1.0,1.0
34.0,1.0,1.0
35.0,1.0,1.0
36.0,1.0,1.0
37.0,1.0,1.0
38.0,1.0,1.0
39.0,1.0,1.0
40.0,1.0,1.0
41.0,1.0,1.0
42.0,1.0,1.0
43.0,1.0,1.0
44.0,1.0,1.0
45.0,1.0,1.0
46.0,1.0,1.0
47.0,1.0,1.0
48.0,1.0,1.0
49.0,1.0,1.0
50.0,1.0,1.0
51.0,1.0,1.0
52.0,1.0,1.0
53.0,1.0,1.0
54.0,1.0,1.0
55.0,1.0,1.0
56.0,1.0,1.0
57.0,1.0,1.0
58.0,1.0,1.0
59.0,1.0,1.0
60.0,1.0,1.0
61.0,1.0,1.0
62.0,1.0,1.0
63.0,1.0,1.0
64.0,1.0,1.0
65.0,1.0,1.0
66.0,1.0,1.0
67.0,1.0,1.0
68.0,1.0,1.0
69.0,1.0,1.0
70.0,1.0,1.0
71.0,1.0,1.0
72.0,1.0,1.0
73.0,1.0,1.0
74.0,1.0,1.0
75.0,1.0,1.0
76.0,1.0,1.0
77.0,1.0,1.0
78.0,1.0,1.0
79.0,1.0,1.0
80.0,1.0,1.0
81.0,1.0,1.0
82.0,1.0,1.0
83.0,1.0,1.0
84.0,1.0,1.0
85.0,1.0,1.0
86.0,1.0,1.0
87.0,1.0,1.0
88.0,1.0,1.0
89.0,1.0,1.0
90.0,1.0,1.0
91.0,1.0,1.0
92.0,1.0,1.0
93.0,1.0,1.0
94.0,1.0,1.0
95.0,1.0,1.0
96.0,1.0,1.0
97.0,1.0,1.0
98.0,1.0,1.0
99.0,1.0,1.0
100.0,1.0,1.0
101.0,1.0,1.0
102.0,1.0,1.0
103.0,1.0,1.0
104.0,1.0,1.0
105.0,1.0,1.0
106.0,1.0,1.0
107.0,1.0,1.0
108.0,1.0,1.0
109.0,1.0,1.0
110.0,1.0,1.0
111.0,1.0,1.0
112.0,1.0,1.0
113.0,1.0,1.0
114.0,1.0,1.0
115.0,1.0,1.0
116.0,1.0,1.0
117.0,1.0,1.0
118.0,1.0,1.0
119.0,1.0,1.0
120.0,1.0,1.0
121.0,1.0,1.0
122.0,1.0,1.0
123.0,1.0,1.0
124.0,1.0,1.0
125.0,1.0,1.0
126.0,1.0,1.0
127.0,1.0,1.0
128.0,1.0,1.0
129.0,1.0,1.0
130.0,1.0,1.0
131.0,1.0,1.0
132.0,1.0,1.0
133.0,1.0,1.0
134.0,1.0,1.0
135.0,1.0,1.0
136.0,1.0,1.0
137.0,1.0,1.0
138.0,1.0,1.0
139.0,1.0,1.0
140.0,1.0,1.0
141.0,1.0,1.0
142.0,1.0,1.0
143.0,1.0,1.0
144.0,1.0,1.0
145.0,1.0,1.0
146.0,1.0,1.0
147.0,1.0,1.0
148.0,1.0,1.0
149.0,1.0,1.0
150.0,1.0,1.0
151.0,1.0,1.0
152.0,1.0,1.0
153.0,1.0,1.0
154.0,1.0,1.0
155.0,1.0,1.0
156.0,1.0,1.0
157.0,1.0,1.0
158.0,1.0,1.0
159.0,1.0,1.0
160.0,1.0,1.0
161.0,1.0,1.0
162.0,1.0,1.0
163.0,1.0,1.0
164.0,1.0,1.0
165.0,1.0,1.0
166.0,1.0,1.0
167.0,1.0,1.0
168.0,1.0,1.0
169.0,1.0,1.0
170.0,1.0,1.0
171.0,1.0,1.0
172.0,1.0,1.0
173.0,1.0,1.0
174.0,1.0,1.0
175.0,1.0,1.0
176.0,1.0,1.0
177.0,1.0,1.0
178.0,1.0,1.0
179.0,1.0,1.0
180.0,1.0,1.0
181.0,1.0,1.0
182.0,1.0,1.0
183.0,1.0,1.0
184.0,1.0,1.0
185.0,1.0,1.0
186.0,1.0,1.0
187.0,1.0,1.0
188.0,1.0,1.0
189.0,1.0,1.0
190.0,1.0,1.0
191.0,1.0,1.0
192.0,1.0,1.0
193.0,1.0,1.0
194.0,1.0,1.0
195.0,1.0,1.0
196.0,1.0,1.0
197.0,1.0,1.0
198.0,1.0,1.0
199.0,1.0,1.0
200.0,1.0,1.0
201.0,1.0,1.0
202.0,1.0,1.0
203.0,1.0,1.0
204.0,1.0,1.0
205.0,1.0,1.0
206.0,1.0,1.0
207.0,1.0,1.0
208.0,1.0,1.0
209.0,1.0,1.0
210.0,1.0,1.0
211.0,1.0,1.0
212.0,1.0,1.0
213.0,1.0,1.0
214.0,1.0,1.0
215.0,1.0,1.0
216.0,1.0,1.0
217.0,1.0,1.0
218.0,1.0,1.0
219.0,1.0,1.0
220.0,1.0,1.0
221.0,1.0,1.0
222.0,1.0,1.0
223.0,1.0,1.0
224.0,1.0,1.0
225.0,1.0,1.0
226.0,1.0,1.0
227.0,1.0,1.0
228.0,1.0,1.0
229.0,1.0,1.0
230.0,1.0,1.0
231.0,1.0,1.0
232.0,1.0,1.0
233.0,1.0,1.0
234.0,1.0,1.0
235.0,1.0,1.0
236.0,1.0,1.0
237.0,1.0,1.0
238.0,1.0,1.0
239.0,1.0,1.0
240.0,1.0,1.0
241.0,1.0,1.0
242.0,1.0,1.0
243.0,1.0,1.0
244.0,1.0,1.0
245.0,1.0,1.0
246.0,1.0,1.0
247.0,1.0,1.0
248.0,1.0,1.0
249.0,1.0,1.0
250.0,1.0,1.0
251.0,1.0,1.0
252.0,1.0,1.0
253.0,1.0,1.0
254.0,1.0,1.0
255.0,1.0,1.0
256.0,1.0,1.0
257.0,1.0,1.0
258.0,1.0,1.0
259.0,1.0,1.0
260.0,1.0,1.0
261.0,1.0,1.0
262.0,1.0,1.0
263.0,1.0,1.0
264.0,1.0,1.0
265.0,1.0,1.0
266.0,1.0,1.0
267.0,1.0,1.0
268.0,1.0,1.0
269.0,1.0,1.0
270.0,1.0,1.0
271.0,1.0,1.0
272.0,1.0,1.0
273.0,1.0,1.0
274.0,1.0,1.0
275.0,1.0,1.0
276.0,1.0,1.0
277.0,1.0,1.0
278.0,1.0,1.0
279.0,1.0,1.0
280.0,1.0,1.0
281.0,1.0,1.0
282.0,1.0,1.0
283.0,1.0,1.0
284.0,1.0,1.0
285.0,1.0,1.0
286.0,1.0,1.0
287.0,1.0,1.0
288.0,1.0,1.0
289.0,1.0,1.0
290.0,1.0,1.0
291.0,1.0,1.0
292.0,1.0,1.0
293.0,1.0,1.0
294.0,1.0,1.0
295.0,1.0,1.0
296.0,1.0,1.0
297.0,1.0,1.0
298.0,1.0,1.0
299.0,1.0,1.0
300.0,1.0,1.0
301.0,1.0,1.0
302.0,1.0,1.0
303.0,1.0,1.0
304.0,1.0,1.0
305.0,1.0,1.0
306.0,1.0,1.0
307.0,1.0,1.0
308.0,1.0,1.0
309.0,1.0,1.0
310.0,1.0,1.0
311.0,1.0,1.0
312.0,1.0,1.0
313.0,1.0,1.0
314.0,1.0,1.0
315.0,1.0,1.0
316.0,1.0,1.0
317.0,1.0,1.0
318.0,1.0,1.0
319.0,1.0,1.0
320.0,1.0,1.0
321.0,1.0,1.0
322.0,1.0,1.0
323.0,1.0,1.0
324.0,1.0,1.0
325.0,1.0,1.0
326.0,1.0,1.0
327.0,1.0,1.0
328.0,1.0,1.0
329.0,1.0,1.0
330.0,1.0,1.0
331.0,1.0,1.0
332.0,1.0,1.0
333.0,1.0,1.0
334.0,1.0,1.0
335.0,1.0,1.0
336.0,1.0,1.0
337.0,1.0,1.0
338.0,1.0,1.0
339.0,1.0,1.0
340.0,1.0,1.0
341.0,1.0,1.0
342.0,1.0,1.0
343.0,1.0,1.0
344.0,1.0,1.0
345.0,1.0,1.0
346.0,1.0,1.0
347.0,1.0,1.0
348.0,1.0,1.0
349.0,1.0,1.0
350.0,1.0,1.0
351.0,1.0,1.0
352.0,1.0,1.0
353.0,1.0,1.0
354.0,1.0,1.0
355.0,1.0,1.0
356.0,1.0,1.0
357.0,1.0,1.0
358.0,1.0,1.0
359.0,1.0,1.0
360.0,1.0,1.0
361.0,1.0,1.0
362.0,1.0,1.0
363.0,1.0,1.0
364.0,1.0,1.0
365.0,1.0,1.0
366.0,1.0,1.0
367.0,1.0,1.0
368.0,1.0,1.0
369.0,1.0,1.0
370.0,1.0,1.0
371.0,1.0,1.0
372.0,1.0,1.0
373.0,1.0,1.0
374.0,1.0,1.0
375.0,1.0,1.0
376.0,1.0,1.0
377.0,1.0,1.0
378.0,1.0,1.0
379.0,1.0,1.0
380.0,1.0,1.0
381.0,1.0,1.0
382.0,1.0,1.0
383.0,1.0,1.0
384.0,1.0,1.0
385.0,1.0,1.0
386.0,1.0,1.0
387.0,1.0,1.0
388.0,1.0,1.0
389.0,1.0,1.0
390.0,1.0,1.0
391.0,1.0,1.0
392.0,1.0,1.0
393.0,1.0,1.0
394.0,1.0,1.0
395.0,1.0,1.0
396.0,1.0,1.0
397.0,1.0,1.0
398.0,1.0,1.0
399.0,1.0,1.0
400.0,1.0,1.0
401.0,1.0,1.0
402.0,1.0,1.0
403.0,1.0,1.0
404.0,1.0,1.0
405.0,1.0,1.0
406.0,1.0,1.0
407.0,1.0,1.0
408.0,1.0,1.0
409.0,1.0,1.0
410.0,1.0,1.0
411.0,1.0,1.0
412.0,1.0,1.0
413.0,1.0,1.0
414.0,1.0,1.0
415.0,1.0,1.0
416.0,1.0,1.0
417.0,1.0,1.0
418.0,1.0,1.0
419.0,1.0,1.0
420.0,1.0,1.0
421.0,1.0,1.0
422.0,1.0,1.0
423.0,1.0,1.0
424.0,1.0,1.0
425.0,1.0,1.0
426.0,1.0,1.0
427.0,1.0,1.0
428.0,1.0,1.0
429.0,1.0,1.0
430.0,1.0,1.0
431.0,1.0,1.0
432.0,1.0,1.0
433.0,1.0,1.0
434.0,1.0,1.0
435.0,1.0,1.0
436.0,1.0,1.0
437.0,1.0,1.0
438.0,1.0,1.0
439.0,1.0,1.0
440.0,1.0,1.0
441.0,1.0,1.0
442.0,1.0,1.0
443.0,1.0,1.0
444.0,1.0,1.0
445.0,1.0,1.0
446.0,1.0,1.0
447.0,1.0,1.0
448.0,1.0,1.0
449.0,1.0,1.0
450.0,1.0,1.0
451.0,1.0,1.0
452.0,1.0,1.0
453.0,1.0,1.0
454.0,1.0,1.0
455.0,1.0,1.0
456.0,1.0,1.0
457.0,1.0,1.0
458.0,1.0,1.0
459.0,1.0,1.0
460.0,1.0,1.0
461.0,1.0,1.0
462.0,1.0,1.0
463.0,1.0,1.0
464.0,1.0,1.0
465.0,1.0,1.0
466.0,1.0,1.0
467.0,1.0,1.0
468.0,1.0,1.0
469.0,1.0,1.0
470.0,1.0,1.0
471.0,1.0,1.0
472.0,1.0,1.0
473.0,1.0,1.0
474.0,1.0,1.0
475.0,1.0,1.0
476.0,1.0,1.0
477.0,1.0,1.0
478.0,1.0,1.0
479.0,1.0,1.0
480.0,1.0,1.0
481.0,1.0,1.0
482.0,1.0,1.0
483.0,1.0,1.0
484.0,1.0,1.0
485.0,1.0,1.0
486.0,1.0,1.0
487.0,1.0,1.0
488.0,1.0,1.0
489.0,1.0,1.0
490.0,1.0,1.0
491.0,1.0,1.0
492.0,1.0,1.0
493.0,1.0,1.0
494.0,1.0,1.0
495.0,1.0,1.0
496.0,1.0,1.0
497.0,1.0,1.0
498.0,1.0,1.0
499.0,1.0,1.0
500.0,1.0,1.0
501.0,1.0,1.0
502.0,1.0,1.0
503.0,1.0,1.0
504.0,1.0,1.0
505.0,1.0,1.0
506.0,1.0,1.0
507.0,1.0,1.0
508.0,1.0,1.0
509.0,1.0,1.0
510.0,1.0,1.0
511.0,1.0,1.0
512.0,1.0,1.0
513.0,1.0,1.0
514.0,1.0,1.0
515.0,1.0,1.0
516.0,1.0,1.0
517.0,1.0,1.0
518.0,1.0,1.0
519.0,1.0,1.0
520.0,1.0,1.0
521.0,1.0,1.0
522.0,1.0,1.0
523.0,1.0,1.0
524.0,1.0,1.0
525.0,1.0,1.0
526.0,1.0,1.0
527.0,1.0,1.0
528.0,1.0,1.0
529.0,1.0,1.0
530.0,1.0,1.0
531.0,1.0,1.0
532.0,1.0,1.0
533.0,1.0,1.0
534.0,1.0,1.0
535.0,1.0,1.0
536.0,1.0,1.0
537.0,1.0,1.0
538.0,1.0,1.0
539.0,1.0,1.0
540.0,1.0,1.0
541.0,1.0,1.0
542.0,1.0,1.0
543.0,1.0,1.0
544.0,1.0,1.0
545.0,1.0,1.0
546.0,1.0,1.0
547.0,1.0,1.0
548.0,1.0,1.0
549.0,1.0,1.0
550.0,1.0,1.0
551.0,1.0,1.0
552.0,1.0,1.0
553.0,1.0,1.0
554.0,1.0,1.0
555.0,1.0,1.0
556.0,1.0,1.0
557.0,1.0,1.0
558.0,1.0,1.0
559.0,1.0,1.0
560.0,1.0,1.0
561.0,1.0,1.0
562.0,1.0,1.0
563.0,1.0,1.0
564.0,1.0,1.0
565.0,1.0,1.0
566.0,1.0,1.0
567.0,1.0,1.0
568.0,1.0,1.0
569.0,1.0,1.0
570.0,1.0,1.0
571.0,1.0,1.0
572.0,1.0,1.0
573.0,1.0,1.0
574.0,1.0,1.0
575.0,1.0,1.0
576.0,1.0,1.0
577.0,1.0,1.0
578.0,1.0,1.0
579.0,1.0,1.0
580.0,1.0,1.0
581.0,1.0,1.0
582.0,1.0,1.0
583.0,1.0,1.0
584.0,1.0,1.0
585.0,1.0,1.0
586.0,1.0,1.0
587.0,1.0,1.0
588.0,1.0,1.0
589.0,1.0,1.0
590.0,1.0,1.0
591.0,1.0,1.0
592.0,1.0,1.0
593.0,1.0,1.0
594.0,1.0,1.0
595.0,1.0,1.0
596.0,1.0,1.0
597.0,1.0,1.0
598.0,1.0,1.0
599.0,1.0,1.0
600.0,1.0,1.0

View file

@ -0,0 +1,201 @@
time,rateConstant,difficulty
0.0,1.0,1.0
120458.46590012926,1.0,1.0
240605.33373578714,1.0,1.0
360287.44233708055,1.0,1.0
480034.84471731156,1.0,1.0
601021.4072874074,1.0,1.0
720155.9397105722,1.0,1.0
840557.1097845419,1.0,1.0
960142.4715273783,1.0,1.0
1080326.4196690398,1.0,1.0
1200228.3567496322,1.0,1.0
1321393.1769845556,1.0,1.0
1442403.7637959223,1.0,1.0
1563312.6884154421,1.0,1.0
1683650.3562759135,1.0,1.0
1804242.0878089573,1.0,1.0
1923957.6270508477,1.0,1.0
2043603.5193920576,1.0,1.0
2162815.4440224506,1.0,1.0
2282288.929005547,1.0,1.0
2402114.705755926,1.0,1.0
2522303.585101224,1.0,1.0
2643267.2988593285,1.0,1.0
2762617.5338163865,1.0,1.0
2882563.380726947,1.0,1.0
3003489.532699132,1.0,1.0
3124641.562256374,1.0,1.0
3245100.9215427977,1.0,1.0
3365536.420458877,1.0,1.0
3484337.996609595,1.0,1.0
3604694.7847104715,1.0,1.0
3724951.576782264,1.0,1.0
3846026.69613723,1.0,1.0
3967024.7982774274,1.0,1.0
4085911.063711746,1.0,1.0
4205815.028144305,1.0,1.0
4325421.232620401,1.0,1.0
4444827.052513202,1.0,1.0
4565099.154304211,1.0,1.0
4686236.387124661,1.0,1.0
4806734.051850467,1.0,1.0
4926566.647937627,1.0,1.0
5045488.866484466,1.0,1.0
5166502.755345808,1.0,1.0
5287233.653263695,1.0,1.0
5407908.334559678,1.0,1.0
5528786.366282915,1.0,1.0
5647678.3122160155,1.0,1.0
5767244.089580304,1.0,1.0
5886473.651601109,1.0,1.0
6006435.897878854,1.0,1.0
6125341.27352921,1.0,1.0
6245479.785253891,1.0,1.0
6365523.236878065,1.0,1.0
6485749.795878896,1.0,1.0
6605240.444894265,1.0,1.0
6724686.210446132,1.0,1.0
6844155.218750592,1.0,1.0
6963341.181369955,1.0,1.0
7082695.6923123505,1.0,1.0
7203655.141225615,1.0,1.0
7322627.204840391,1.0,1.0
7441799.159546731,1.0,1.0
7562822.49932097,1.0,1.0
7683323.524066307,1.0,1.0
7804477.48899398,1.0,1.0
7923457.857637024,1.0,1.0
8043898.249303401,1.0,1.0
8164187.296374614,1.0,1.0
8283469.107294493,1.0,1.0
8402627.723784521,1.0,1.0
8523745.89200794,1.0,1.0
8643778.9729813,1.0,1.0
8764635.624631569,1.0,1.0
8885538.059417976,1.0,1.0
9006433.79263568,1.0,1.0
9127489.588015186,1.0,1.0
9247569.28782317,1.0,1.0
9367958.084440755,1.0,1.0
9487586.29442678,1.0,1.0
9606427.886281481,1.0,1.0
9725872.946140163,1.0,1.0
9846820.596317865,1.0,1.0
9966080.313457007,1.0,1.0
10085765.108573573,1.0,1.0
10206008.374459436,1.0,1.0
10326807.845790997,1.0,1.0
10447513.423168132,1.0,1.0
10566795.347145824,1.0,1.0
10686179.997466285,1.0,1.0
10805522.808373246,1.0,1.0
10925591.945700001,1.0,1.0
11045387.795286857,1.0,1.0
11165651.717103494,1.0,1.0
11286830.098064246,1.0,1.0
11406728.71699933,1.0,1.0
11525922.789870113,1.0,1.0
11646476.03903497,1.0,1.0
11766081.434268286,1.0,1.0
11886554.737807736,1.0,1.0
12006591.066234503,1.0,1.0
12125475.542528223,1.0,1.0
12245282.608027933,1.0,1.0
12364566.337023206,1.0,1.0
12484001.537563423,1.0,1.0
12604914.58673975,1.0,1.0
12725797.319719713,1.0,1.0
12844665.121160349,1.0,1.0
12964577.111847764,1.0,1.0
13083664.372389417,1.0,1.0
13204397.122438395,1.0,1.0
13324189.80038166,1.0,1.0
13445349.787586372,1.0,1.0
13564667.751049513,1.0,1.0
13684270.141374838,1.0,1.0
13804933.175467426,1.0,1.0
13926075.032552686,1.0,1.0
14045914.739095576,1.0,1.0
14166631.90042476,1.0,1.0
14286319.61550191,1.0,1.0
14406521.722056692,1.0,0.434741782576
14527069.972553544,1.0,0.19008730551
14647876.788742132,1.0,0.0840528337448
14766882.452974409,1.0,0.037302520528
14886464.10335911,1.0,0.0165590754676
15006383.983595744,1.0,0.00734675515331
15125494.942906044,1.0,0.00323569454228
15246686.831929097,1.0,0.00142738653296
15365567.953963466,1.0,0.000625305203658
15485210.071059253,1.0,0.00027137099116
15605744.64157258,1.0,0.00011715461834
15726806.733151415,1.0,5.07010986398e-05
15847473.63409661,1.0,2.20995440742e-05
15967616.71119521,1.0,9.7101015984e-06
16087483.017800437,1.0,4.29503052419e-06
16208328.180223988,1.0,1.92536966438e-06
16328269.772618307,1.0,8.74191715802e-07
16448898.77254663,1.0,4.04296324402e-07
16569068.683560552,1.0,1.90818902578e-07
16689986.260285133,1.0,9.28633545485e-08
16810906.032656457,1.0,4.72143204458e-08
16930289.120968327,1.0,2.49711320542e-08
17050753.779718515,1.0,1.3892256413e-08
17170600.427019596,1.0,8.15844774794e-09
17289713.01359109,1.0,4.99225078962e-09
17410383.421790008,1.0,3.25190780354e-09
17530744.28431112,1.0,2.30869255122e-09
17649863.36025036,1.0,1.75633116174e-09
17771007.11205409,1.0,1.56057621444e-09
17891565.791564044,1.0,2.04725172611e-09
18010728.55130284,1.0,3.28303372549e-09
18131143.8843602,1.0,3.25496154055e-09
18251685.45446652,1.0,2.60736708269e-09
18371507.007102486,1.0,1.85630105501e-09
18491403.948076807,1.0,1.21928659457e-09
18611553.039604228,1.0,7.49792394597e-10
18730744.251551468,1.0,4.45186327526e-10
18849856.685056694,1.0,2.61385070862e-10
18968926.062500622,1.0,1.55149613051e-10
19088067.33909847,1.0,9.51850841053e-11
19207594.13354391,1.0,6.1434055957e-11
19328017.11208744,1.0,4.14815251504e-11
19448814.951942917,1.0,2.86357848006e-11
19567841.28162038,1.0,2.11150220091e-11
19688388.179414563,1.0,1.6406642243e-11
19807627.873820234,1.0,1.43187359575e-11
19928483.04169874,1.0,1.35913238196e-11
20048557.76424465,1.0,1.4331290576e-11
20167664.74191137,1.0,2.37342192382e-11
20287610.467802733,1.0,3.14063325262e-11
20408382.62417472,1.0,3.36556321046e-11
20527817.08243184,1.0,3.00938650991e-11
20646637.59478427,1.0,2.24650389033e-11
20766577.880066067,1.0,1.49080962166e-11
20885785.934438772,1.0,8.89077367161e-12
21005536.07104386,1.0,4.87060433801e-12
21124902.305172637,1.0,2.47052077112e-12
21244885.7076036,1.0,1.17786853388e-12
21364581.43746038,1.0,5.31532928098e-13
21484493.676516064,1.0,2.28757375026e-13
21603916.644479226,1.0,9.40324531004e-14
21723733.860411238,1.0,3.70938898759e-14
21844771.38186735,1.0,1.42162474893e-14
21964794.368862327,1.0,5.30817642226e-15
22085627.151319932,1.0,1.94525471916e-15
22205153.52382764,1.0,6.9852514309e-16
22324878.40351138,1.0,2.45757880538e-16
22444702.60558849,1.0,8.47565473955e-17
22564133.40356277,1.0,2.8611824903e-17
22684916.550748322,1.0,9.50429614301e-18
22804924.811821572,1.0,3.10972949853e-18
22924555.89776567,1.0,1.00142181128e-18
23044673.155832924,1.0,3.17850639386e-19
23164255.219417505,1.0,9.93453168398e-20
23283874.042637054,1.0,3.05585142432e-20
23403159.90787814,1.0,9.23510334381e-21
23522146.490881946,1.0,2.7354446299e-21
23641985.23572208,1.0,7.94854774163e-22
23760806.412928328,1.0,2.26044417743e-22
23881715.30956635,1.0,6.31881915952e-23