105 lines
2.7 KiB
Python
105 lines
2.7 KiB
Python
|
from pyethereum import utils
|
||
|
import random
|
||
|
|
||
|
|
||
|
def sha3(x):
|
||
|
return utils.decode_int(utils.zunpad(utils.sha3(x)))
|
||
|
|
||
|
|
||
|
class SeedObj():
|
||
|
def __init__(self, seed):
|
||
|
self.seed = seed
|
||
|
self.a = 3**160
|
||
|
self.c = 7**80
|
||
|
self.n = 2**256 - 4294968273 # secp256k1n, why not
|
||
|
|
||
|
def rand(self, r):
|
||
|
self.seed = (self.seed * self.a + self.c) % self.n
|
||
|
return self.seed % r
|
||
|
|
||
|
|
||
|
def encode_int(x):
|
||
|
o = ''
|
||
|
for _ in range(8):
|
||
|
o = chr(x % 256) + o
|
||
|
x //= 256
|
||
|
return o
|
||
|
|
||
|
|
||
|
ops = {
|
||
|
"plus": lambda x, y: (x + y) % 2**64,
|
||
|
"times": lambda x, y: (x * y) % 2**64,
|
||
|
"xor": lambda x, y: x ^ y,
|
||
|
"and": lambda x, y: x & y,
|
||
|
"or": lambda x, y: x | y,
|
||
|
"not": lambda x, y: 2**64-1-x,
|
||
|
"nxor": lambda x, y: (2**64-1-x) ^ y,
|
||
|
"rshift": lambda x, y: x >> (y % 64)
|
||
|
}
|
||
|
|
||
|
|
||
|
def gentape(W, H, SEED):
|
||
|
s = SeedObj(SEED)
|
||
|
tape = []
|
||
|
for i in range(H):
|
||
|
op = ops.keys()[s.rand(len(ops))]
|
||
|
r = s.rand(100)
|
||
|
if r < 20 and i > 20:
|
||
|
x1 = tape[-r]["x1"]
|
||
|
else:
|
||
|
x1 = s.rand(W)
|
||
|
x2 = s.rand(W)
|
||
|
tape.append({"op": op, "x1": x1, "x2": x2})
|
||
|
return tape
|
||
|
|
||
|
|
||
|
def runtape(TAPE, SEED, params):
|
||
|
s = SeedObj(SEED)
|
||
|
# Fill up tape inputs
|
||
|
mem = [0] * params["w"]
|
||
|
for i in range(params["w"]):
|
||
|
mem[i] = s.rand(2**64)
|
||
|
# Direction variable (run forwards or backwards)
|
||
|
dir = 1
|
||
|
# Run the tape on the inputs
|
||
|
for i in range(params["h"] // 100):
|
||
|
for j in (range(100) if dir == 1 else range(99, -1, -1)):
|
||
|
t = TAPE[i * 100 + j]
|
||
|
mem[t["x1"]] = ops[t["op"]](mem[t["x1"]], mem[t["x2"]])
|
||
|
# 16% of the time, we flip the order of the next 100 ops;
|
||
|
# this adds in conditional branching
|
||
|
if 2 < mem[t["x1"]] % 37 < 9:
|
||
|
dir *= -1
|
||
|
return sha3(''.join(encode_int(x) for x in mem))
|
||
|
|
||
|
|
||
|
def PoWVerify(header, nonce, params):
|
||
|
tape = gentape(params["w"], params["h"],
|
||
|
sha3(header + encode_int(nonce // params["n"])))
|
||
|
h = runtape(tape, sha3(header + encode_int(nonce)), params)
|
||
|
print h
|
||
|
return h < 2**256 / params["diff"]
|
||
|
|
||
|
|
||
|
def PoWRun(header, params):
|
||
|
# actual randomness, so that miners don't overlap
|
||
|
nonce = random.randrange(2**50) * params["n"]
|
||
|
tape = None
|
||
|
while 1:
|
||
|
print nonce
|
||
|
if nonce % params["n"] == 0:
|
||
|
tape = gentape(params["w"], params["h"],
|
||
|
sha3(header + encode_int(nonce // params["n"])))
|
||
|
h = runtape(tape, sha3(header + encode_int(nonce)), params)
|
||
|
if h < 2**256 / params["diff"]:
|
||
|
return nonce
|
||
|
else:
|
||
|
nonce += 1
|
||
|
|
||
|
params = {
|
||
|
"w": 100,
|
||
|
"h": 15000, # generally, w*log(w) at least
|
||
|
"diff": 2**24, # initial
|
||
|
"n": 1000
|
||
|
}
|