diff --git a/sim.py b/sim.py index a9f3981..d218226 100644 --- a/sim.py +++ b/sim.py @@ -1,13 +1,15 @@ -# Time between block number increases (NOT time between PoW solutions) -BLOCKTIME = 50 +# Time between successful PoW solutions +POW_SOLUTION_TIME = 10 # Time for a block to traverse the network TRANSIT_TIME = 50 # Number of required uncles UNCLES = 4 # Uncle block reward (normal block reward = 1) UNCLE_REWARD_COEFF = 0.875 -# Block length to test -BLOCKS = 5000 +# Reward for including uncles +NEPHEW_REWARD_COEFF = 0.01 +# Rounds to test +ROUNDS = 80000 import random import copy @@ -16,7 +18,7 @@ import copy class Miner(): def __init__(self, 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, # we need two genesis blocks plus some genesis uncles) self.blocks = { @@ -25,13 +27,8 @@ class Miner(): 1: {"parent": 0, "uncles": [], "miner": -1, "height": 1, "score": 0, "id": 1, "children": {}} } - # for i in range(2, UNCLES + 2): - # self.blocks[i] = {"parent": 0, "uncles": [], "miner": -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 + # ID of "latest block" + self.head = 1 # Hear about a block def recv(self, block): @@ -42,29 +39,45 @@ class Miner(): if block["parent"] not in self.blocks: addme = False for u in block["uncles"]: - if self.blocks[u]["id"] not in self.blocks: + if u not in self.blocks: addme = False p = self.blocks[block["parent"]] if addme: 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"]: p["children"][block["id"]] = block["id"] + # Check if the new block deserves to be the new head if len(p["children"]) >= 1 + UNCLES: if block["score"] > self.blocks[self.head]["score"]: - self.head = block["parent"] + self.head = block["id"] # Mine a block def mine(self): - h = self.blocks[self.head] - b = sorted(list(h["children"]), key=lambda x: self.blocks[x]["id"]) + h = self.blocks[self.blocks[self.head]["parent"]] + b = sorted(list(h["children"]), key=lambda x: -self.blocks[x]["score"]) p = self.blocks[b[0]] block = {"parent": b[0], "uncles": b[1:], "miner": self.id, "height": h["height"] + 2, "score": p["score"] + len(b), - "id": random.randrange(1000000000), "children": {}} + "id": random.randrange(1000000000000), "children": {}} self.recv(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] miners = [] for p in percentages: @@ -76,12 +89,12 @@ for m in miners: listen_queue = [] -for t in range(BLOCKS * BLOCKTIME): +for t in range(ROUNDS): if t % 5000 == 0: print t for m in miners: - R = random.randrange(BLOCKTIME * sum(percentages) / (UNCLES + 1)) - if R < m.hashpower: + R = random.randrange(POW_SOLUTION_TIME * sum(percentages)) + if R < m.hashpower and t < ROUNDS - TRANSIT_TIME * 3: b = m.mine() listen_queue.append([t + TRANSIT_TIME, b]) 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] profit = {} +total_blocks_in_chain = 0 +length_of_chain = 0 +ZORO = {} print "### PRINTING BLOCKCHAIN ###" while h["id"] > 1: 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"]: + ZORO[u] = True u2 = miners[0].blocks[u] profit[u2["miner"]] = profit.get(u2["miner"], 0) + UNCLE_REWARD_COEFF h = miners[0].blocks[h["parent"]] +print "### PRINTING HEADS ###" + +for m in miners: + print m.head + + print "### PRINTING PROFITS ###" for p in profit: print miner_dict[p].hashpower, profit[p] -print "### PRINTING GROUPINGS ###" +print "### PRINTING RESULTS ###" groupings = {} counts = {} @@ -117,3 +144,11 @@ for p in profit: for c in counts: 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