Merge branch 'master' of https://github.com/ethereum/economic-modeling
This commit is contained in:
commit
59bf21c44c
|
@ -0,0 +1,173 @@
|
|||
# Time between successful PoW solutions
|
||||
POW_SOLUTION_TIME = 6
|
||||
# Time for a block to traverse the network
|
||||
TRANSIT_TIME = 12
|
||||
# Max uncle depth
|
||||
UNCLE_DEPTH = 4
|
||||
# Uncle block reward (normal block reward = 1)
|
||||
UNCLE_REWARD_COEFF = 15/16.
|
||||
# Reward for including uncles
|
||||
NEPHEW_REWARD_COEFF = 1/32.
|
||||
# Rounds to test
|
||||
ROUNDS = 500000
|
||||
|
||||
import random
|
||||
|
||||
all_miners = {}
|
||||
|
||||
|
||||
class Miner():
|
||||
def __init__(self, p):
|
||||
self.hashpower = p
|
||||
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 = {}
|
||||
self.children = {}
|
||||
for i in range(UNCLE_DEPTH + 2):
|
||||
self.blocks[i] = \
|
||||
{"parent": i-1, "uncles": {}, "miner": -1, "height": i,
|
||||
"score": i, "id": i}
|
||||
self.children[i-1] = {i: True}
|
||||
# ID of "latest block"
|
||||
self.head = UNCLE_DEPTH + 1
|
||||
|
||||
# Hear about a block
|
||||
def recv(self, block):
|
||||
# Add the block to the set if it's valid
|
||||
addme = True
|
||||
if block["id"] in self.blocks:
|
||||
addme = False
|
||||
if block["parent"] not in self.blocks:
|
||||
addme = False
|
||||
if addme:
|
||||
self.blocks[block["id"]] = block
|
||||
if block["parent"] not in self.children:
|
||||
self.children[block["parent"]] = {}
|
||||
if block["id"] not in self.children[block["parent"]]:
|
||||
self.children[block["parent"]][block["id"]] = block["id"]
|
||||
if block["score"] > self.blocks[self.head]["score"]:
|
||||
self.head = block["id"]
|
||||
|
||||
# Mine a block
|
||||
def mine(self):
|
||||
HEAD = self.blocks[self.head]
|
||||
H = HEAD
|
||||
h = self.blocks[self.blocks[self.head]["parent"]]
|
||||
# Select the uncles. The valid set of uncles for a block consists
|
||||
# of the children of the 2nd to N+1th order grandparents minus
|
||||
# the parent and said grandparents themselves and blocks that were
|
||||
# uncles of those previous blocks
|
||||
u = {}
|
||||
notu = {}
|
||||
for i in range(UNCLE_DEPTH):
|
||||
for c in self.children.get(h["id"], {}):
|
||||
u[c] = True
|
||||
notu[H["id"]] = True
|
||||
for c in H["uncles"]:
|
||||
notu[c] = True
|
||||
H = h
|
||||
h = self.blocks[h["parent"]]
|
||||
for i in notu:
|
||||
if i in u:
|
||||
del u[i]
|
||||
block = {"parent": self.head, "uncles": u, "miner": self.id,
|
||||
"height": HEAD["height"] + 1, "score": HEAD["score"]+1+len(u),
|
||||
"id": random.randrange(1000000000000)}
|
||||
self.recv(block)
|
||||
global all_miners
|
||||
all_miners[block["id"]] = block
|
||||
return block
|
||||
|
||||
|
||||
# If b1 is the n-th degree grandchild and b2 is the m-th degree grandchild
|
||||
# of nearest common ancestor C, returns min(m, n)
|
||||
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
|
||||
|
||||
# Set hashpower percentages here
|
||||
percentages = [1]*25 + [5, 5, 5, 5, 5, 10, 15, 25]
|
||||
miners = []
|
||||
for p in percentages:
|
||||
miners.append(Miner(p))
|
||||
|
||||
miner_dict = {}
|
||||
for m in miners:
|
||||
miner_dict[m.id] = m
|
||||
|
||||
listen_queue = []
|
||||
|
||||
for t in range(ROUNDS):
|
||||
if t % 5000 == 0:
|
||||
print t
|
||||
for m in miners:
|
||||
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:
|
||||
t, b = listen_queue.pop(0)
|
||||
for m in miners:
|
||||
m.recv(b)
|
||||
|
||||
h = miners[0].blocks[miners[0].head]
|
||||
profit = {}
|
||||
total_blocks_in_chain = 0
|
||||
length_of_chain = 0
|
||||
ZORO = {}
|
||||
print "### PRINTING BLOCKCHAIN ###"
|
||||
|
||||
while h["id"] > UNCLE_DEPTH + 2:
|
||||
# print h["id"], h["miner"], h["height"], h["score"]
|
||||
# print "Uncles: ", list(h["uncles"])
|
||||
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 RESULTS ###"
|
||||
|
||||
groupings = {}
|
||||
counts = {}
|
||||
for p in profit:
|
||||
h = miner_dict[p].hashpower
|
||||
counts[h] = counts.get(h, 0) + 1
|
||||
groupings[h] = groupings.get(h, 0) + profit[p]
|
||||
|
||||
for c in counts:
|
||||
print c, groupings[c] / counts[c] / (groupings[1] / counts[1])
|
||||
|
||||
print " "
|
||||
print "Total blocks produced: ", len(all_miners) - UNCLE_DEPTH
|
||||
print "Total blocks in chain: ", total_blocks_in_chain
|
||||
print "Efficiency: ", \
|
||||
total_blocks_in_chain * 1.0 / (len(all_miners) - UNCLE_DEPTH)
|
||||
print "Average uncles: ", total_blocks_in_chain * 1.0 / length_of_chain - 1
|
||||
print "Length of chain: ", length_of_chain
|
||||
print "Block time: ", ROUNDS * 1.0 / length_of_chain
|
|
@ -0,0 +1,95 @@
|
|||
import pyethereum
|
||||
u = pyethereum.utils
|
||||
import time
|
||||
|
||||
#These are the operations that will end up in the tape
|
||||
ops = [
|
||||
lambda x, y: x+y % 2**256,
|
||||
lambda x, y: x*y % 2**256,
|
||||
lambda x, y: x % y if y > 0 else x+y,
|
||||
lambda x, y: x & y,
|
||||
lambda x, y: x | y,
|
||||
lambda x, y: x ^ y
|
||||
]
|
||||
|
||||
|
||||
'''
|
||||
the tape will be 'w' wide and 'd' operations deep
|
||||
it is a list of triples [i, j, op], later used
|
||||
in the tape's execution: xi = xi op xj
|
||||
'''
|
||||
def gen_tape(seed, w, d):
|
||||
tape = []
|
||||
h = 0
|
||||
#Getting as much entropy out of a hash as possible
|
||||
for i in range(d):
|
||||
if h < 2**32:
|
||||
h = u.big_endian_to_int(u.sha3(seed+str(i)))
|
||||
v1 = h % w
|
||||
h /= w
|
||||
v2 = h % w
|
||||
h /= w
|
||||
op = ops[h % len(ops)]
|
||||
h /= len(ops)
|
||||
tape.append([v1, v2, op])
|
||||
return tape
|
||||
|
||||
|
||||
def lshift(n):
|
||||
return 2**255 * (n % 2) + (n / 2)
|
||||
|
||||
#Generates the inputs to and evaluates the tape, the mining nonce can be taken to be in the seed
|
||||
def gen_inputs(seed,w):
|
||||
#generating the tape's inputs
|
||||
v = []
|
||||
h = 0
|
||||
for i in range(w):
|
||||
if i % 1 == 0:
|
||||
h = u.big_endian_to_int(u.sha3(seed+str(i)))
|
||||
else:
|
||||
h = lshift(h)
|
||||
v.append(h)
|
||||
return v
|
||||
|
||||
#Evaluate tape on inputs (incorrect dimension of v is an unhandled exception)
|
||||
def run_tape(v, tape):
|
||||
for t in tape:
|
||||
v[t[0]] = t[2](v[t[0]], v[t[1]])
|
||||
#Implemented in a blockchain, any additional hashes or timestamp would be added in the sha
|
||||
return str(v)
|
||||
|
||||
|
||||
# This times the various parts of the hashing function - you can make the tape longer to make tape evaluation dominate
|
||||
#num_iterations is the number of tapes that are used
|
||||
#num_tape_evals is the number of nonces allowed, per tape
|
||||
#tape_w is the width of the tape
|
||||
#tape_d is the depth of the tape
|
||||
|
||||
def test(num_iterations = 10, num_tape_evals = 1000, tape_w = 100, tape_d = 1000):
|
||||
time_generating_tape = 0.
|
||||
time_generating_inputs = 0.
|
||||
time_evaluating_tape = 0.
|
||||
time_sha_capping = 0.
|
||||
for i in range(num_iterations):
|
||||
t = time.time()
|
||||
tape = gen_tape(str(i), tape_w, tape_d)
|
||||
time_generating_tape += time.time() - t
|
||||
|
||||
for j in xrange(num_tape_evals):
|
||||
t = time.time()
|
||||
v = gen_inputs(str(j), tape_w)
|
||||
time_generating_inputs += time.time() - t
|
||||
|
||||
t = time.time()
|
||||
x = run_tape(v,tape)
|
||||
time_evaluating_tape += time.time() - t
|
||||
|
||||
t = time.time()
|
||||
h = u.sha3(x)
|
||||
time_sha_capping += time.time() - t
|
||||
|
||||
total_time = time_generating_tape + time_generating_inputs + time_evaluating_tape + time_sha_capping
|
||||
print "% of time generating tape:", time_generating_tape/total_time
|
||||
print "% of time generating inputs:", time_generating_inputs/total_time
|
||||
print "% of time evaluating tape:", time_evaluating_tape/total_time
|
||||
print "% of time sha-capping:", time_sha_capping/total_time
|
|
@ -0,0 +1,346 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"encoding/hex"
|
||||
"github.com/obscuren/sha3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//For use in benchmarking
|
||||
const tree_depth = 5
|
||||
const tape_width = 32 //int(math.Pow(2,tree_depth)) < would be nice, but go won't recognize as const
|
||||
const tape_depth = 500
|
||||
|
||||
const Report_Zeros = true
|
||||
|
||||
//Number of operations that are drawn from to form the tape and the tree
|
||||
const num_ops = 7
|
||||
//This is the number of times the PoW algorithm is run
|
||||
const sample_size = 100
|
||||
|
||||
func Bytes2Hex(d []byte) string {
|
||||
return hex.EncodeToString(d)
|
||||
}
|
||||
|
||||
func Hex2Bytes(str string) []byte {
|
||||
h, _ := hex.DecodeString(str)
|
||||
return h
|
||||
}
|
||||
|
||||
func plus(z *big.Int, x *big.Int, y *big.Int) *big.Int {
|
||||
var lim big.Int
|
||||
lim.Exp(big.NewInt(2),big.NewInt(256), big.NewInt(0))
|
||||
z.Add(x,y)
|
||||
return z.Mod(z,&lim)
|
||||
|
||||
}
|
||||
|
||||
func times(z *big.Int, x *big.Int, y *big.Int) *big.Int {
|
||||
var lim, x1, y1, z1 big.Int
|
||||
lim.Exp(big.NewInt(2),big.NewInt(256), big.NewInt(0))
|
||||
x1.Set(x)
|
||||
y1.Set(y)
|
||||
z.Mul(x,y)
|
||||
z.Mod(z,&lim)
|
||||
z1 = *big.NewInt(0)
|
||||
if Report_Zeros {
|
||||
if (z.Cmp(&z1) == 0 && x1.Cmp(&z1) != 0 && y1.Cmp(&z1) != 0) {
|
||||
fmt.Printf("-------getting zero---------\n")
|
||||
fmt.Println(Bytes2Hex(x1.Bytes()))
|
||||
fmt.Println(Bytes2Hex(y1.Bytes()))
|
||||
fmt.Println(Bytes2Hex(z.Bytes()))
|
||||
}
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
func mod(z *big.Int, x *big.Int, y *big.Int) *big.Int {
|
||||
|
||||
if (x.Cmp(big.NewInt(0)) == 0 || y.Cmp(big.NewInt(0)) == 0) {
|
||||
return big.NewInt(0)
|
||||
}
|
||||
if x.Cmp(y) == -1 { //if x < y
|
||||
z.Mod(y,x)
|
||||
} else if x.Cmp(y) == 1 { //if x > y
|
||||
z.Mod(x,y)
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
func xor(z *big.Int, x *big.Int, y *big.Int) *big.Int {return z.Xor(x,y)}
|
||||
func and(z *big.Int, x *big.Int, y *big.Int) *big.Int {return z.And(x,y)}
|
||||
func or(z *big.Int, x *big.Int, y *big.Int) *big.Int {return z.Or(x,y)}
|
||||
|
||||
func rshift(z *big.Int, x *big.Int, y *big.Int) *big.Int {
|
||||
var lim big.Int
|
||||
lim.Exp(big.NewInt(2),big.NewInt(256), big.NewInt(0))
|
||||
z.Rsh(x,7)
|
||||
return z.Mod(z,&lim)
|
||||
}
|
||||
|
||||
|
||||
func GetOp(i int) func(z *big.Int, x *big.Int, y *big.Int) *big.Int{
|
||||
switch i {
|
||||
case 0:
|
||||
return plus
|
||||
case 1:
|
||||
return rshift
|
||||
case 2:
|
||||
return mod
|
||||
case 3:
|
||||
return xor
|
||||
case 4:
|
||||
return and
|
||||
case 5:
|
||||
return or
|
||||
case 6:
|
||||
return times
|
||||
}
|
||||
return plus
|
||||
}
|
||||
|
||||
type Tapelink struct {
|
||||
I int64
|
||||
J int64
|
||||
op func(z *big.Int, x *big.Int, y *big.Int) *big.Int
|
||||
}
|
||||
|
||||
func Sha3Bin(data []byte) []byte {
|
||||
d := sha3.NewKeccak256()
|
||||
d.Write(data)
|
||||
return d.Sum(nil)
|
||||
}
|
||||
|
||||
func BenchmarkSha3Bin(b *testing.B){
|
||||
for i:= 0; i < b.N; i++ {
|
||||
Sha3Bin([]byte(string(i)))
|
||||
}
|
||||
}
|
||||
|
||||
//generates a tape of operations that is w*d long
|
||||
func gen_tape(seed int64, w int, d int) []Tapelink {
|
||||
var X = big.NewInt(0)
|
||||
var Y = big.NewInt(int64(math.Pow(2,32)))
|
||||
var M = big.NewInt(0)
|
||||
var T []Tapelink
|
||||
for i := 0; i < w*d; i++{
|
||||
// add empty link to tape
|
||||
T = append(T, *new(Tapelink))
|
||||
|
||||
// generate new entropy as needed
|
||||
if (int64(X.Cmp(Y)) == -1) {X = X.SetBytes(Sha3Bin([]byte(string(seed + int64(i)))))}
|
||||
|
||||
// Pick random index I
|
||||
T[i].I = M.Mod(X,big.NewInt(int64(w))).Int64()
|
||||
M = big.NewInt(int64(w))
|
||||
X = X.Div(X,M)
|
||||
|
||||
// Pick random index J
|
||||
mm := big.NewInt(M.Mod(X,big.NewInt(int64(w-1))).Int64() + int64(1) + T[i].I)
|
||||
T[i].J = M.Mod(mm, big.NewInt(int64(w))).Int64()
|
||||
M = big.NewInt(int64(w-1))
|
||||
X = X.Div(X,M)
|
||||
|
||||
// Pick random operation
|
||||
T[i].op = GetOp(int(M.Mod(X, big.NewInt(int64(num_ops))).Int64()))
|
||||
M = big.NewInt(int64(num_ops))
|
||||
X = X.Div(X,M)
|
||||
}
|
||||
return T
|
||||
}
|
||||
|
||||
func BenchmarkGen_tape(b *testing.B){
|
||||
var X big.Int
|
||||
var s int64
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
X.SetBytes(Sha3Bin([]byte(string(i))))
|
||||
s = X.Int64()
|
||||
b.StartTimer()
|
||||
gen_tape(s, tape_width, tape_depth)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func gen_inputs(seed int64, w int) []big.Int {
|
||||
var A []big.Int
|
||||
X := big.NewInt(0)
|
||||
for i := 0; i < w; i++ {
|
||||
var B big.Int
|
||||
A = append(A,B)
|
||||
if (i % 256 == 0) {
|
||||
A[i].Set(X.SetBytes(Sha3Bin([]byte(string(seed + int64(i))))))
|
||||
} else {
|
||||
A[i].Set(X.Lsh(X, 1))
|
||||
}
|
||||
}
|
||||
return A
|
||||
}
|
||||
|
||||
func BenchmarkGen_inputs(b *testing.B){
|
||||
var X big.Int
|
||||
var s int64
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
X.SetBytes(Sha3Bin([]byte(string(i))))
|
||||
s = X.Int64()
|
||||
b.StartTimer()
|
||||
gen_inputs(s, tape_width)
|
||||
}
|
||||
}
|
||||
|
||||
//this changes the inputs as it goes through a tape with d links
|
||||
func run_tape(tape []Tapelink, inputs []big.Int, d int) {
|
||||
var X *big.Int
|
||||
X = big.NewInt(0)
|
||||
for i := 0; i < d; i++ {
|
||||
X = tape[i].op(X, &inputs[tape[i].I], &inputs[tape[i].J])
|
||||
inputs[tape[i].I].Set(X)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRun_tape(b *testing.B){
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
T := gen_tape(int64(i), tape_width, tape_depth)
|
||||
I := gen_inputs(int64(i), tape_width)
|
||||
b.StartTimer()
|
||||
run_tape(T,I,tape_width*tape_depth)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//returns a 2^d - 1 length tape of operations (no I or J's in the Tapelinks)
|
||||
func gen_tree(seed int64, d int) []Tapelink {
|
||||
M := big.NewInt(0) // dummy variable
|
||||
X := big.NewInt(0) // entropy variable
|
||||
Y := big.NewInt(int64(math.Pow(2,32))) // entropy buffer size
|
||||
var T []Tapelink // the tree will be stored here
|
||||
|
||||
for i := 0; i < int(math.Pow(2, float64(d))) - 1; i++{
|
||||
|
||||
T = append(T, *new(Tapelink))
|
||||
|
||||
//giving it more entropy, if X < 2^32
|
||||
if (X.Cmp(Y) == -1) {X = X.SetBytes(Sha3Bin([]byte(string(seed + int64(i)))))}
|
||||
|
||||
//filling the tape with random ops
|
||||
T[i].op = GetOp(int(M.Mod(X, big.NewInt(num_ops)).Int64()))
|
||||
M = big.NewInt(num_ops)
|
||||
X = X.Div(X,M)
|
||||
}
|
||||
return T
|
||||
}
|
||||
|
||||
func BenchmarkGen_tree(b *testing.B) {
|
||||
var X big.Int
|
||||
var s int64
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
X.SetBytes(Sha3Bin([]byte(string(i))))
|
||||
s = X.Int64()
|
||||
b.StartTimer()
|
||||
gen_tree(s, tree_depth)
|
||||
}
|
||||
}
|
||||
|
||||
//there should be 2^d inputs and 2^d - 1 links in the tape. not complying is an unhandled exception
|
||||
func run_tree(inputs []big.Int, tree []Tapelink, d int) *big.Int {
|
||||
X := big.NewInt(0)
|
||||
counter := 0
|
||||
for j := 0; j < d; j++ {
|
||||
for i := 0; i < int(math.Pow(2,float64(d - j - 1))); i++ {
|
||||
X = tree[counter].op(X, &inputs[2*i], &inputs[2*i + 1])
|
||||
inputs[i].Set(X)
|
||||
counter += 1
|
||||
}
|
||||
}
|
||||
|
||||
return &inputs[0]
|
||||
}
|
||||
|
||||
func BenchmarkRun_tree(b *testing.B) {
|
||||
var X big.Int
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
var inputs []big.Int
|
||||
for j := 0; j < tape_width; j++ {
|
||||
X.SetBytes(Sha3Bin([]byte(string(j + i*tape_width))))
|
||||
inputs = append(inputs, X)
|
||||
}
|
||||
tree := gen_tree(X.Int64(), tree_depth)
|
||||
b.StartTimer()
|
||||
run_tree(inputs, tree, tree_depth)
|
||||
}
|
||||
}
|
||||
|
||||
func sha_cap(s []string, n int) []byte{
|
||||
var feed string
|
||||
feed = ""
|
||||
for i := 0; i < n; i++ {
|
||||
feed += s[i]
|
||||
}
|
||||
return Sha3Bin([]byte(feed))
|
||||
}
|
||||
|
||||
|
||||
//benchmarked with only one string in the slice, s
|
||||
func BenchmarkSha_cap(b *testing.B){
|
||||
var X big.Int
|
||||
var s []string
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
X.SetBytes(Sha3Bin([]byte(string(i))))
|
||||
s = append(s, X.String())
|
||||
b.StartTimer()
|
||||
sha_cap(s,1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
func main(){
|
||||
var seed int64
|
||||
|
||||
var sample []big.Int
|
||||
for i := 0; i < sample_size; i++ {
|
||||
|
||||
if i%5 == 0 {
|
||||
fmt.Printf("i: %d\n",i)
|
||||
}
|
||||
|
||||
seed = int64(i)
|
||||
T := gen_tape(seed, tape_width, tape_depth)
|
||||
I := gen_inputs(seed, tape_width)
|
||||
run_tape(T,I, tape_depth*tape_width)
|
||||
T = gen_tree(seed, tree_depth)
|
||||
var output big.Int = *run_tree(I,T,tree_depth)
|
||||
var blockhashes []string
|
||||
blockhashes = append(blockhashes, output.String())
|
||||
H := sha_cap(blockhashes,1)
|
||||
|
||||
output.SetBytes(H)
|
||||
sample = append(sample, output)
|
||||
}
|
||||
|
||||
var collisions int = 0
|
||||
for i := 0; i < sample_size; i++ {
|
||||
for j:=0; j < i; j++ {
|
||||
if(sample[i].Cmp(&sample[j]) == 0) {
|
||||
collisions += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("Collisions: %d out of %d\n", (1+int(math.Pow(float64(1+8*collisions),0.5)))/2 - 1, sample_size)
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
# 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
|
||||
# Reward for including uncles
|
||||
NEPHEW_REWARD_COEFF = 0.01
|
||||
# Rounds to test
|
||||
ROUNDS = 80000
|
||||
|
||||
import random
|
||||
import copy
|
||||
|
||||
|
||||
class Miner():
|
||||
def __init__(self, p):
|
||||
self.hashpower = p
|
||||
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 = {
|
||||
0: {"parent": -1, "uncles": [], "miner": -1, "height": 0,
|
||||
"score": 0, "id": 0, "children": {1: 1}},
|
||||
1: {"parent": 0, "uncles": [], "miner": -1, "height": 1,
|
||||
"score": 0, "id": 1, "children": {}}
|
||||
}
|
||||
# ID of "latest block"
|
||||
self.head = 1
|
||||
|
||||
# Hear about a block
|
||||
def recv(self, block):
|
||||
# Add the block to the set if it's valid
|
||||
addme = True
|
||||
if block["id"] in self.blocks:
|
||||
addme = False
|
||||
if block["parent"] not in self.blocks:
|
||||
addme = False
|
||||
for u in block["uncles"]:
|
||||
if u not in self.blocks:
|
||||
addme = False
|
||||
p = self.blocks[block["parent"]]
|
||||
if addme:
|
||||
self.blocks[block["id"]] = copy.deepcopy(block)
|
||||
# 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:
|
||||
for c in p["children"]:
|
||||
newblock = self.blocks[c]
|
||||
if newblock["score"] > self.blocks[self.head]["score"]:
|
||||
self.head = newblock["id"]
|
||||
|
||||
# Mine a block
|
||||
def mine(self):
|
||||
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(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:
|
||||
miners.append(Miner(p))
|
||||
|
||||
miner_dict = {}
|
||||
for m in miners:
|
||||
miner_dict[m.id] = m
|
||||
|
||||
listen_queue = []
|
||||
|
||||
for t in range(ROUNDS):
|
||||
if t % 5000 == 0:
|
||||
print t
|
||||
for m in miners:
|
||||
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:
|
||||
t, b = listen_queue.pop(0)
|
||||
for m in miners:
|
||||
m.recv(b)
|
||||
|
||||
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"]
|
||||
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 RESULTS ###"
|
||||
|
||||
groupings = {}
|
||||
counts = {}
|
||||
for p in profit:
|
||||
h = miner_dict[p].hashpower
|
||||
counts[h] = counts.get(h, 0) + 1
|
||||
groupings[h] = groupings.get(h, 0) + profit[p]
|
||||
|
||||
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
|
Loading…
Reference in New Issue