-
Notifications
You must be signed in to change notification settings - Fork 2
/
proof.py
executable file
·142 lines (108 loc) · 3.89 KB
/
proof.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
141
142
#!/usr/bin/env python
"""
Hash Rate Proof Verification
Slush Pool <[email protected]>
"""
import argparse
import binascii
import hashlib
import json
import os
from StringIO import StringIO
from zipfile import ZipFile
def hex_to_bin(data):
return binascii.unhexlify(data)
def bin_to_hex(data):
return binascii.hexlify(data)
def double_sha256_hash(data):
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
def bin_le_to_int(val):
val_hex = bin_to_hex(val[::-1])
return int(val_hex, 16)
def sha256_digest_to_int(data):
return bin_le_to_int(data)
def difficulty_to_target(difficulty):
BASE_DIFF_TARGET = 0x00000000ffff0000000000000000000000000000000000000000000000000000
return int(BASE_DIFF_TARGET / difficulty)
def validate(hash_hex, difficulty):
"""
Compute double hash of block header and check if the target is lower then the hash rate proof target (computed from
hash rate proof difficulty).
"""
return sha256_digest_to_int(double_sha256_hash(hex_to_bin(hash_hex))) < difficulty_to_target(difficulty)
def hashrate_from_proof(num_hashes, difficulty):
"""
Count hash rate from numbers of hashes and hash rate proof difficulty (all submits above this difficulty are
provided). Hash Rate is in the Gh/s
"""
return (2**32 * difficulty * num_hashes)/float(3600 * 10**9)
def _format_unit_4sig(unit, value):
if value >= 100:
return u'%.1f %s' % (value, unit)
if value >= 10:
return u'%.2f %s' % (value, unit)
return u'%.3f %s' % (value, unit)
def hashrate(variable):
"""
Return hash rate in pretty format.
"""
variable = float(variable)
if abs(variable) < 0.0005:
return '---'
if variable < 1000:
return _format_unit_4sig(u'Gh/s', variable)
if variable < 1000000:
return _format_unit_4sig(u'Th/s', variable / 1000.)
return _format_unit_4sig(u'Ph/s', variable / 1000000.)
def compute_merkle_root(branch, first):
"""
Compute merkle root from merkle branch and coinbase (first).
"""
for s in branch:
first = double_sha256_hash(first + s)
return first
def validate_origin(hash_hex, coinbase_hex, merkle_branch_hex):
"""
Validate if coinbase contains our mark (/slush/) and if merkle root and if the block header contains this merkle
root.
"""
hash_bin = hex_to_bin(hash_hex)
coinbase_bin = hex_to_bin(coinbase_hex)
merkle_branch_bin = map(hex_to_bin, merkle_branch_hex)
merkle_root_bin = compute_merkle_root(merkle_branch_bin, double_sha256_hash(coinbase_bin))
return '/slush/' in coinbase_bin and merkle_root_bin == hash_bin[36:68]
def main(file_path):
with ZipFile(file_path) as z:
file_name = (os.path.basename(file_path).rsplit('.', 1)[0])+'.txt'
f = StringIO(z.read(file_name))
header = json.loads(f.readline())
i = 0
for line in f.readlines():
hash, coinbase, merkle_branch = json.loads(line.replace('\n', ''))
if not validate_origin(hash, coinbase, merkle_branch):
return {
'status': 'ERR',
'err': 'wrong origin (missing /slush/ in coinbase or wrong merkle root)',
'hashrate': None,
}
if not validate(hash, header['difficulty']):
return {
'status': 'ERR',
'err': 'too low difficulty',
'hashrate': None,
}
i += 1
return {
'status': 'OK',
'err': None,
'hashrate': hashrate_from_proof(i, header['difficulty'])
}
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('file', type=str)
args = parser.parse_args()
res = main(args.file)
print 'status:', res['status']
if res['err']:
print 'error:', res['err']
print 'hashrate:', hashrate(res['hashrate'])