SpyCoder77 commited on
Commit
a76d8ea
·
verified ·
1 Parent(s): 1d370b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -237
app.py CHANGED
@@ -1,262 +1,87 @@
1
- #!/usr/bin/python
2
- #
3
- # Copyright 2011 Jeff Garzik
4
- #
5
- # This program is free software; you can redistribute it and/or modify
6
- # it under the terms of the GNU General Public License as published by
7
- # the Free Software Foundation.
8
- #
9
- # This program is distributed in the hope that it will be useful,
10
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- # GNU General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU General Public License
15
- # along with this program; see the file COPYING. If not, write to
16
- # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17
- #
18
 
19
- import time
20
  import json
21
- import pprint
22
  import hashlib
23
- import struct
24
- import re
25
- import base64
26
- import httplib
27
- import sys
28
- from multiprocessing import Process
29
-
30
- ERR_SLEEP = 15
31
- MAX_NONCE = 1000000L
32
-
33
- settings = {}
34
- pp = pprint.PrettyPrinter(indent=4)
35
-
36
- class BitcoinRPC:
37
- OBJID = 1
38
-
39
- def __init__(self, host, port, username, password):
40
- authpair = "%s:%s" % (username, password)
41
- self.authhdr = "Basic %s" % (base64.b64encode(authpair))
42
- self.conn = httplib.HTTPConnection(host, port, False, 30)
43
- def rpc(self, method, params=None):
44
- self.OBJID += 1
45
- obj = { 'version' : '1.1',
46
- 'method' : method,
47
- 'id' : self.OBJID }
48
- if params is None:
49
- obj['params'] = []
50
- else:
51
- obj['params'] = params
52
- self.conn.request('POST', '/', json.dumps(obj),
53
- { 'Authorization' : self.authhdr,
54
- 'Content-type' : 'application/json' })
55
-
56
- resp = self.conn.getresponse()
57
- if resp is None:
58
- print "JSON-RPC: no response"
59
- return None
60
-
61
- body = resp.read()
62
- resp_obj = json.loads(body)
63
- if resp_obj is None:
64
- print "JSON-RPC: cannot JSON-decode body"
65
- return None
66
- if 'error' in resp_obj and resp_obj['error'] != None:
67
- return resp_obj['error']
68
- if 'result' not in resp_obj:
69
- print "JSON-RPC: no result in object"
70
- return None
71
-
72
- return resp_obj['result']
73
- def getblockcount(self):
74
- return self.rpc('getblockcount')
75
- def getwork(self, data=None):
76
- return self.rpc('getwork', data)
77
-
78
- def uint32(x):
79
- return x & 0xffffffffL
80
-
81
- def bytereverse(x):
82
- return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) |
83
- (((x) >> 8) & 0x0000ff00) | ((x) >> 24) ))
84
-
85
- def bufreverse(in_buf):
86
- out_words = []
87
- for i in range(0, len(in_buf), 4):
88
- word = struct.unpack('@I', in_buf[i:i+4])[0]
89
- out_words.append(struct.pack('@I', bytereverse(word)))
90
- return ''.join(out_words)
91
-
92
- def wordreverse(in_buf):
93
- out_words = []
94
- for i in range(0, len(in_buf), 4):
95
- out_words.append(in_buf[i:i+4])
96
- out_words.reverse()
97
- return ''.join(out_words)
98
-
99
- class Miner:
100
- def __init__(self, id):
101
- self.id = id
102
- self.max_nonce = MAX_NONCE
103
-
104
- def work(self, datastr, targetstr):
105
- # decode work data hex string to binary
106
- static_data = datastr.decode('hex')
107
- static_data = bufreverse(static_data)
108
-
109
- # the first 76b of 80b do not change
110
- blk_hdr = static_data[:76]
111
-
112
- # decode 256-bit target value
113
- targetbin = targetstr.decode('hex')
114
- targetbin = targetbin[::-1] # byte-swap and dword-swap
115
- targetbin_str = targetbin.encode('hex')
116
- target = long(targetbin_str, 16)
117
-
118
- # pre-hash first 76b of block header
119
- static_hash = hashlib.sha256()
120
- static_hash.update(blk_hdr)
121
-
122
- for nonce in xrange(self.max_nonce):
123
-
124
- # encode 32-bit nonce value
125
- nonce_bin = struct.pack("<I", nonce)
126
-
127
- # hash final 4b, the nonce value
128
- hash1_o = static_hash.copy()
129
- hash1_o.update(nonce_bin)
130
- hash1 = hash1_o.digest()
131
-
132
- # sha256 hash of sha256 hash
133
- hash_o = hashlib.sha256()
134
- hash_o.update(hash1)
135
- hash = hash_o.digest()
136
-
137
- # quick test for winning solution: high 32 bits zero?
138
- if hash[-4:] != '\0\0\0\0':
139
- continue
140
-
141
- # convert binary hash to 256-bit Python long
142
- hash = bufreverse(hash)
143
- hash = wordreverse(hash)
144
 
145
- hash_str = hash.encode('hex')
146
- l = long(hash_str, 16)
147
 
148
- # proof-of-work test: hash < target
149
- if l < target:
150
- print time.asctime(), "PROOF-OF-WORK found: %064x" % (l,)
151
- return (nonce + 1, nonce_bin)
152
- else:
153
- print time.asctime(), "PROOF-OF-WORK false positive %064x" % (l,)
154
- # return (nonce + 1, nonce_bin)
155
 
156
- return (nonce + 1, None)
 
157
 
158
- def submit_work(self, rpc, original_data, nonce_bin):
159
- nonce_bin = bufreverse(nonce_bin)
160
- nonce = nonce_bin.encode('hex')
161
- solution = original_data[:152] + nonce + original_data[160:256]
162
- param_arr = [ solution ]
163
- result = rpc.getwork(param_arr)
164
- print time.asctime(), "--> Upstream RPC result:", result
165
 
166
- def iterate(self, rpc):
167
- work = rpc.getwork()
168
- if work is None:
169
- time.sleep(ERR_SLEEP)
170
- return
171
- if 'data' not in work or 'target' not in work:
172
- time.sleep(ERR_SLEEP)
173
- return
174
 
175
- time_start = time.time()
 
176
 
177
- (hashes_done, nonce_bin) = self.work(work['data'],
178
- work['target'])
 
 
179
 
180
- time_end = time.time()
181
- time_diff = time_end - time_start
182
 
183
- self.max_nonce = long(
184
- (hashes_done * settings['scantime']) / time_diff)
185
- if self.max_nonce > 0xfffffffaL:
186
- self.max_nonce = 0xfffffffaL
187
 
188
- if settings['hashmeter']:
189
- print "HashMeter(%d): %d hashes, %.2f Khash/sec" % (
190
- self.id, hashes_done,
191
- (hashes_done / 1000.0) / time_diff)
192
 
193
- if nonce_bin is not None:
194
- self.submit_work(rpc, work['data'], nonce_bin)
 
195
 
196
- def loop(self):
197
- rpc = BitcoinRPC(settings['host'], settings['port'],
198
- settings['rpcuser'], settings['rpcpass'])
199
- if rpc is None:
200
- return
201
 
202
- while True:
203
- self.iterate(rpc)
204
 
205
- def miner_thread(id):
206
- miner = Miner(id)
207
- miner.loop()
 
208
 
209
- if __name__ == '__main__':
210
- if len(sys.argv) != 2:
211
- print "Usage: pyminer.py CONFIG-FILE"
212
- sys.exit(1)
213
 
214
- f = open(sys.argv[1])
215
- for line in f:
216
- # skip comment lines
217
- m = re.search('^\s*#', line)
218
- if m:
219
- continue
220
 
221
- # parse key=value lines
222
- m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
223
- if m is None:
224
- continue
225
- settings[m.group(1)] = m.group(2)
226
- f.close()
227
 
228
- if 'host' not in settings:
229
- settings['host'] = '127.0.0.1'
230
- if 'port' not in settings:
231
- settings['port'] = 8332
232
- if 'threads' not in settings:
233
- settings['threads'] = 1
234
- if 'hashmeter' not in settings:
235
- settings['hashmeter'] = 0
236
- if 'scantime' not in settings:
237
- settings['scantime'] = 30L
238
- if 'rpcuser' not in settings or 'rpcpass' not in settings:
239
- print "Missing username and/or password in cfg file"
240
- sys.exit(1)
241
 
242
- settings['port'] = int(settings['port'])
243
- settings['threads'] = int(settings['threads'])
244
- settings['hashmeter'] = int(settings['hashmeter'])
245
- settings['scantime'] = long(settings['scantime'])
246
 
247
- thr_list = []
248
- for thr_id in range(settings['threads']):
249
- p = Process(target=miner_thread, args=(thr_id,))
250
- p.start()
251
- thr_list.append(p)
252
- time.sleep(1) # stagger threads
253
 
254
- print settings['threads'], "mining threads started"
 
 
 
 
 
 
 
255
 
256
- print time.asctime(), "Miner Starts - %s:%s" % (settings['host'], settings['port'])
257
- try:
258
- for thr_proc in thr_list:
259
- thr_proc.join()
260
- except KeyboardInterrupt:
261
- pass
262
- print time.asctime(), "Miner Stops - %s:%s" % (settings['host'], settings['port'])
 
1
+ #A stratum compatible miniminer
2
+ #based in the documentation
3
+ #https://slushpool.com/help/#!/manual/stratum-protocol
4
+ #2017-2019 Martin Nadal https://martinnadal.eu
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
+ import socket
7
  import json
 
8
  import hashlib
9
+ import binascii
10
+ from pprint import pprint
11
+ import time
12
+ import random
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ address = '1GvSP13YjQAu9VAa8J1Hvbc4n3N8kUE3Ch'
15
+ nonce = hex(random.randint(0,2**32-1))[2:].zfill(8)
16
 
17
+ host = 'solo.ckpool.org'
18
+ port = 3333
 
 
 
 
 
19
 
20
+ print("address:{} nonce:{}".format(address,nonce))
21
+ print("host:{} port:{}".format(host,port))
22
 
23
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
24
+ sock.connect((host,port))
 
 
 
 
 
25
 
26
+ #server connection
27
+ sock.sendall(b'{"id": 1, "method": "mining.subscribe", "params": []}\n')
28
+ lines = sock.recv(1024).decode().split('\n')
29
+ response = json.loads(lines[0])
30
+ sub_details,extranonce1,extranonce2_size = response['result']
 
 
 
31
 
32
+ #authorize workers
33
+ sock.sendall(b'{"params": ["'+address.encode()+b'", "password"], "id": 2, "method": "mining.authorize"}\n')
34
 
35
+ #we read until 'mining.notify' is reached
36
+ response = b''
37
+ while response.count(b'\n') < 4 and not(b'mining.notify' in response):
38
+ response += sock.recv(1024)
39
 
 
 
40
 
41
+ #get rid of empty lines
42
+ responses = [json.loads(res) for res in response.decode().split('\n') if len(res.strip())>0 and 'mining.notify' in res]
43
+ pprint(responses)
 
44
 
45
+ job_id,prevhash,coinb1,coinb2,merkle_branch,version,nbits,ntime,clean_jobs \
46
+ = responses[0]['params']
 
 
47
 
48
+ #target https://bitcoin.stackexchange.com/a/36228/44319
49
+ target = (nbits[2:]+'00'*(int(nbits[:2],16) - 3)).zfill(64)
50
+ print('nbits:{} target:{}\n'.format(nbits,target))
51
 
52
+ extranonce2 = '00'*extranonce2_size
 
 
 
 
53
 
54
+ coinbase = coinb1 + extranonce1 + extranonce2 + coinb2
55
+ coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest()
56
 
57
+ print('coinbase:\n{}\n\ncoinbase hash:{}\n'.format(coinbase,binascii.hexlify(coinbase_hash_bin)))
58
+ merkle_root = coinbase_hash_bin
59
+ for h in merkle_branch:
60
+ merkle_root = hashlib.sha256(hashlib.sha256(merkle_root + binascii.unhexlify(h)).digest()).digest()
61
 
62
+ merkle_root = binascii.hexlify(merkle_root).decode()
 
 
 
63
 
64
+ #little endian
65
+ merkle_root = ''.join([merkle_root[i]+merkle_root[i+1] for i in range(0,len(merkle_root),2)][::-1])
 
 
 
 
66
 
67
+ print('merkle_root:{}\n'.format(merkle_root))
 
 
 
 
 
68
 
69
+ blockheader = version + prevhash + merkle_root + nbits + ntime + nonce +\
70
+ '000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000'
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ print('blockheader:\n{}\n'.format(blockheader))
 
 
 
73
 
74
+ hash = hashlib.sha256(hashlib.sha256(binascii.unhexlify(blockheader)).digest()).digest()
75
+ hash = binascii.hexlify(hash).decode()
76
+ print('hash: {}'.format(hash))
 
 
 
77
 
78
+ if hash < target :
79
+ print('success!!')
80
+ payload = '{"params": ["'+address+'", "'+job_id+'", "'+extranonce2 \
81
+ +'", "'+ntime+'", "'+nonce+'"], "id": 1, "method": "mining.submit"}\n'
82
+ sock.sendall(payload)
83
+ print(sock.recv(1024))
84
+ else:
85
+ print('failed mine, hash is greater than target')
86
 
87
+ sock.close()