-
Notifications
You must be signed in to change notification settings - Fork 0
/
check_proof_of_work.py
executable file
·140 lines (121 loc) · 4.53 KB
/
check_proof_of_work.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python3
import json, hashlib, pyprimes, sys, time
def sha256(d):
m = hashlib.sha256()
m.update(d)
return m.digest()
def get_hash(block, for_pow=False):
"""
original memory layout:
int nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
bitsType nBits;
int64 nTime;
offsetType nOffset;
"""
b = bytearray(
(b"" if for_pow else bytes.fromhex(block["nOffset"]))
+ block["time"].to_bytes(byteorder="big", length=8)
+ bytes.fromhex(block["bits"])
+ bytes.fromhex(block["merkleroot"])
+ (bytes.fromhex(block["previousblockhash"]) if "previousblockhash" in block else b"\x00" * 32)
+ block["version"].to_bytes(byteorder="big", length=4)
)
b.reverse()
b = sha256(sha256(b))
b = b[::-1]
return b
ZEROES_BEFORE_HASH_IN_PRIME = 8 # main.h
def set_compact(x):
"""
The "compact" format is a representation of a whole
number N using an unsigned 32bit number similar to a
floating point format.
The most significant 8 bits are the unsigned exponent of base 256.
This exponent can be thought of as "number of bytes of N".
The lower 23 bits are the mantissa.
Bit number 24 (0x800000) represents the sign of N.
N = (-1^sign) * mantissa * 256^(exponent-3)
"""
size = x >> 24
size = 3 - size if size <= 3 else size - 3
return (x & 0x7fffff) >> (size * 8)
def generate_prime_base(has, compact_bits):
target = 1
target <<= ZEROES_BEFORE_HASH_IN_PRIME
for _ in range(256):
target = (target << 1) + (has & 1)
has >>= 1
significant_digits = 1 + ZEROES_BEFORE_HASH_IN_PRIME + 256
trailing_zeros = set_compact(compact_bits)
if trailing_zeros < significant_digits:
return target, 0
trailing_zeros -= significant_digits
return target, trailing_zeros
def is_prime(p, nchecks, do_trail_division):
""" signature of http://linux.die.net/man/3/bn_is_prime_fasttest """
#print(p, nchecks, do_trail_division)
return pyprimes.miller_rabin(p)
FACTORIZE = False #not DONT_CHECK
def check_proof_of_work(pow_hash, compact_bits, delta, DONT_CHECK):
#if pow_hash == genesis_pow_hash: return True
target, trailing_zeros = generate_prime_base(pow_hash, compact_bits)
factorization = []
copy = target
if FACTORIZE:
yield "n = "
t = time.time()
for i, j in pyprimes.factorise(target):
yield "({} ^ {}) * ".format(i,j)
copy //= i ** j
factorization.append((i, j))
if time.time() - t > 3: break
yield "{} * 2 ^ {} + {}\n".format(copy, trailing_zeros, delta)
factorization.append((copy, 1))
factorization.append((2, trailing_zeros))
target <<= trailing_zeros
if trailing_zeros < 256:
if delta >= 1 << trailing_zeros:
raise Exception("candidate larger than allowed")
target += delta
if target % 210 != 97:
raise Exception("not valid pow")
def check(remaining):
offset, nchecks_before, nchecks_after = remaining[0]
if not is_prime(target + offset, nchecks_before, True):
raise Exception("n+{0} not prime".format(offset))
if len(remaining) > 1:
yield from check(remaining[1:])
if nchecks_after is not None and not is_prime(target + offset, nchecks_after, False):
raise Exception("n+{0} not prime".format(offset))
yield "n+{0} = {1}\n".format(offset, target + offset)
constellation = [(0, 1, 9), (4, 1, 9), (6, 1, 9), (10, 1, 9), (12, 1, 9), (16, 10, None)]
if DONT_CHECK:
yield [(factorization, delta), [(x[0], target + x[0]) for x in constellation]]
else:
yield from check(constellation)
return True
def get_primes_from_block(block, DONT_CHECK = True):
yield from check_proof_of_work(
int.from_bytes(get_hash(block, for_pow=True), byteorder="big"),
int(block["bits"], 16),
int(block["nOffset"], 16),
DONT_CHECK
)
def main():
DONT_CHECK = len(sys.argv) > 1
if not DONT_CHECK:
sys.stdout.buffer.write(b"reading from stdin...\n")
sys.stdout.buffer.flush()
try:
block = json.loads(sys.stdin.read())
except ValueError:
print("Usage: riecoind getblock $(riecoind getblockhash <block number>) | python3 " + sys.argv[0])
return
primes = get_primes_from_block(block, DONT_CHECK)
for prime in primes:
sys.stdout.buffer.write(str(prime).encode("utf-8"))
sys.stdout.buffer.flush()
if __name__ == "__main__":
main()