This commit is contained in:
Vitalik Buterin 2014-07-08 04:05:52 -04:00
parent c806fc409c
commit df679e50cb
1 changed files with 58 additions and 23 deletions

81
sim.py
View File

@ -1,13 +1,15 @@
# Time between block number increases (NOT time between PoW solutions) # Time between successful PoW solutions
BLOCKTIME = 50 POW_SOLUTION_TIME = 10
# Time for a block to traverse the network # Time for a block to traverse the network
TRANSIT_TIME = 50 TRANSIT_TIME = 50
# Number of required uncles # Number of required uncles
UNCLES = 4 UNCLES = 4
# Uncle block reward (normal block reward = 1) # Uncle block reward (normal block reward = 1)
UNCLE_REWARD_COEFF = 0.875 UNCLE_REWARD_COEFF = 0.875
# Block length to test # Reward for including uncles
BLOCKS = 5000 NEPHEW_REWARD_COEFF = 0.01
# Rounds to test
ROUNDS = 80000
import random import random
import copy import copy
@ -16,7 +18,7 @@ import copy
class Miner(): class Miner():
def __init__(self, p): def __init__(self, p):
self.hashpower = p self.hashpower = p
self.id = random.randrange(1000000000) self.id = random.randrange(10000000)
# Set up a few genesis blocks (since the algo is grandpa-dependent, # Set up a few genesis blocks (since the algo is grandpa-dependent,
# we need two genesis blocks plus some genesis uncles) # we need two genesis blocks plus some genesis uncles)
self.blocks = { self.blocks = {
@ -25,13 +27,8 @@ class Miner():
1: {"parent": 0, "uncles": [], "miner": -1, "height": 1, 1: {"parent": 0, "uncles": [], "miner": -1, "height": 1,
"score": 0, "id": 1, "children": {}} "score": 0, "id": 1, "children": {}}
} }
# for i in range(2, UNCLES + 2): # ID of "latest block"
# self.blocks[i] = {"parent": 0, "uncles": [], "miner": -1, self.head = 1
# "height": 1, "id": i, "children": {}}
# ID of parent of "latest block"
# We care about the parent of the latest block because that way
# we can keep track of uncles.
self.head = 0
# Hear about a block # Hear about a block
def recv(self, block): def recv(self, block):
@ -42,29 +39,45 @@ class Miner():
if block["parent"] not in self.blocks: if block["parent"] not in self.blocks:
addme = False addme = False
for u in block["uncles"]: for u in block["uncles"]:
if self.blocks[u]["id"] not in self.blocks: if u not in self.blocks:
addme = False addme = False
p = self.blocks[block["parent"]] p = self.blocks[block["parent"]]
if addme: if addme:
self.blocks[block["id"]] = copy.deepcopy(block) self.blocks[block["id"]] = copy.deepcopy(block)
# Check if the new block's parent deserves to be the new head # Each parent keeps track of its children, to help
# facilitate the rule that a block must have N+ siblings
# to be valid
if block["id"] not in p["children"]: if block["id"] not in p["children"]:
p["children"][block["id"]] = block["id"] p["children"][block["id"]] = block["id"]
# Check if the new block deserves to be the new head
if len(p["children"]) >= 1 + UNCLES: if len(p["children"]) >= 1 + UNCLES:
if block["score"] > self.blocks[self.head]["score"]: if block["score"] > self.blocks[self.head]["score"]:
self.head = block["parent"] self.head = block["id"]
# Mine a block # Mine a block
def mine(self): def mine(self):
h = self.blocks[self.head] h = self.blocks[self.blocks[self.head]["parent"]]
b = sorted(list(h["children"]), key=lambda x: self.blocks[x]["id"]) b = sorted(list(h["children"]), key=lambda x: -self.blocks[x]["score"])
p = self.blocks[b[0]] p = self.blocks[b[0]]
block = {"parent": b[0], "uncles": b[1:], "miner": self.id, block = {"parent": b[0], "uncles": b[1:], "miner": self.id,
"height": h["height"] + 2, "score": p["score"] + len(b), "height": h["height"] + 2, "score": p["score"] + len(b),
"id": random.randrange(1000000000), "children": {}} "id": random.randrange(1000000000000), "children": {}}
self.recv(block) self.recv(block)
return block return block
def cousin_degree(miner, b1, b2):
while miner.blocks[b1]["height"] > miner.blocks[b2]["height"]:
b1 = miner.blocks[b1]["parent"]
while miner.blocks[b2]["height"] > miner.blocks[b1]["height"]:
b2 = miner.blocks[b2]["parent"]
t = 0
while b1 != b2:
b1 = miner.blocks[b1]["parent"]
b2 = miner.blocks[b2]["parent"]
t += 1
return t
percentages = [1]*25 + [5, 5, 5, 5, 5, 10, 15, 25] percentages = [1]*25 + [5, 5, 5, 5, 5, 10, 15, 25]
miners = [] miners = []
for p in percentages: for p in percentages:
@ -76,12 +89,12 @@ for m in miners:
listen_queue = [] listen_queue = []
for t in range(BLOCKS * BLOCKTIME): for t in range(ROUNDS):
if t % 5000 == 0: if t % 5000 == 0:
print t print t
for m in miners: for m in miners:
R = random.randrange(BLOCKTIME * sum(percentages) / (UNCLES + 1)) R = random.randrange(POW_SOLUTION_TIME * sum(percentages))
if R < m.hashpower: if R < m.hashpower and t < ROUNDS - TRANSIT_TIME * 3:
b = m.mine() b = m.mine()
listen_queue.append([t + TRANSIT_TIME, b]) listen_queue.append([t + TRANSIT_TIME, b])
while len(listen_queue) and listen_queue[0][0] <= t: while len(listen_queue) and listen_queue[0][0] <= t:
@ -91,22 +104,36 @@ for t in range(BLOCKS * BLOCKTIME):
h = miners[0].blocks[miners[0].head] h = miners[0].blocks[miners[0].head]
profit = {} profit = {}
total_blocks_in_chain = 0
length_of_chain = 0
ZORO = {}
print "### PRINTING BLOCKCHAIN ###" print "### PRINTING BLOCKCHAIN ###"
while h["id"] > 1: while h["id"] > 1:
print h["miner"], h["height"], h["score"] print h["miner"], h["height"], h["score"]
profit[h["miner"]] = profit.get(h["miner"], 0) + 1 total_blocks_in_chain += 1 + len(h["uncles"])
ZORO[h["id"]] = True
length_of_chain += 1
profit[h["miner"]] = profit.get(h["miner"], 0) + \
1 + NEPHEW_REWARD_COEFF * len(h["uncles"])
for u in h["uncles"]: for u in h["uncles"]:
ZORO[u] = True
u2 = miners[0].blocks[u] u2 = miners[0].blocks[u]
profit[u2["miner"]] = profit.get(u2["miner"], 0) + UNCLE_REWARD_COEFF profit[u2["miner"]] = profit.get(u2["miner"], 0) + UNCLE_REWARD_COEFF
h = miners[0].blocks[h["parent"]] h = miners[0].blocks[h["parent"]]
print "### PRINTING HEADS ###"
for m in miners:
print m.head
print "### PRINTING PROFITS ###" print "### PRINTING PROFITS ###"
for p in profit: for p in profit:
print miner_dict[p].hashpower, profit[p] print miner_dict[p].hashpower, profit[p]
print "### PRINTING GROUPINGS ###" print "### PRINTING RESULTS ###"
groupings = {} groupings = {}
counts = {} counts = {}
@ -117,3 +144,11 @@ for p in profit:
for c in counts: for c in counts:
print c, groupings[c] / counts[c] / (groupings[1] / counts[1]) print c, groupings[c] / counts[c] / (groupings[1] / counts[1])
print " "
print "Total blocks produced: ", len(miners[0].blocks) - 2
print "Total blocks in chain: ", total_blocks_in_chain
print "Efficiency: ", total_blocks_in_chain * 1.0 / (len(miners[0].blocks) - 2)
print "Average uncles: ", total_blocks_in_chain * 1.0 / length_of_chain
print "Length of chain: ", length_of_chain
print "Block time: ", ROUNDS * 1.0 / length_of_chain