View difference between Paste ID: eDqjy7Jq and 5MguLDSL
SHOW: | | - or go back to the newest paste.
1
#!/usr/bin/python
2
#
3
# Copyright (c) 2011 The Bitcoin developers
4
# Distributed under the MIT/X11 software license, see the accompanying
5
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
6
#
7
8
import time
9
import json
10
import pprint
11
import hashlib
12
import struct
13
import re
14
import base64
15
import httplib
16
import sys
17
from multiprocessing import Process
18
19
ERR_SLEEP = 15
20
MAX_NONCE = 1000000L
21
22
settings = {}
23
pp = pprint.PrettyPrinter(indent=4)
24
25
class BitcoinRPC:
26
	OBJID = 1
27
28
	def __init__(self, host, port, username, password):
29
		authpair = "%s:%s" % (username, password)
30
		self.authhdr = "Basic %s" % (base64.b64encode(authpair))
31
		self.conn = httplib.HTTPConnection(host, port, False, 30)
32
	def rpc(self, method, params=None):
33
		self.OBJID += 1
34
		obj = { 'version' : '1.1',
35
			'method' : method,
36
			'id' : self.OBJID }
37
		if params is None:
38
			obj['params'] = []
39
		else:
40
			obj['params'] = params
41
		self.conn.request('POST', '/', json.dumps(obj),
42
			{ 'Authorization' : self.authhdr,
43
			  'Content-type' : 'application/json' })
44
45
		resp = self.conn.getresponse()
46
		if resp is None:
47
			print "JSON-RPC: no response"
48
			return None
49
50
		body = resp.read()
51
		resp_obj = json.loads(body)
52
		if resp_obj is None:
53
			print "JSON-RPC: cannot JSON-decode body"
54
			return None
55
		if 'error' in resp_obj and resp_obj['error'] != None:
56
			return resp_obj['error']
57
		if 'result' not in resp_obj:
58
			print "JSON-RPC: no result in object"
59
			return None
60
61
		return resp_obj['result']
62
	def getblockcount(self):
63
		return self.rpc('getblockcount')
64
	def getwork(self, data=None):
65
		return self.rpc('getwork', data)
66
67
def uint32(x):
68
	return x & 0xffffffffL
69
70
def bytereverse(x):
71
	return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) |
72
			(((x) >> 8) & 0x0000ff00) | ((x) >> 24) ))
73
74
def bufreverse(in_buf):
75
	out_words = []
76
	for i in range(0, len(in_buf), 4):
77
		word = struct.unpack('@I', in_buf[i:i+4])[0]
78
		out_words.append(struct.pack('@I', bytereverse(word)))
79
	return ''.join(out_words)
80
81
def wordreverse(in_buf):
82
	out_words = []
83
	for i in range(0, len(in_buf), 4):
84
		out_words.append(in_buf[i:i+4])
85
	out_words.reverse()
86
	return ''.join(out_words)
87
88
class Miner:
89
	def __init__(self, id):
90
		self.id = id
91
		self.max_nonce = MAX_NONCE
92
93
	def work(self, datastr, targetstr):
94
		# decode work data hex string to binary
95
		static_data = datastr.decode('hex')
96
		static_data = bufreverse(static_data)
97
98
		# the first 76b of 80b do not change
99
		blk_hdr = static_data[:76]
100
101
		# decode 256-bit target value
102
		targetbin = targetstr.decode('hex')
103
		targetbin = targetbin[::-1]	# byte-swap and dword-swap
104
		targetbin_str = targetbin.encode('hex')
105
		target = long(targetbin_str, 16)
106
107
		# pre-hash first 76b of block header
108
		static_hash = hashlib.sha256()
109
		static_hash.update(blk_hdr)
110
111
		for nonce in xrange(self.max_nonce):
112
113
			# encode 32-bit nonce value
114
			nonce_bin = struct.pack("<I", nonce)
115
116
			# hash final 4b, the nonce value
117
			hash1_o = static_hash.copy()
118
			hash1_o.update(nonce_bin)
119
			hash1 = hash1_o.digest()
120
121
			# sha256 hash of sha256 hash
122
			hash_o = hashlib.sha256()
123
			hash_o.update(hash1)
124
			hash = hash_o.digest()
125
126
			# quick test for winning solution: high 32 bits zero?
127
			if hash[-4:] != '\0\0\0\0':
128
				continue
129
130
			# convert binary hash to 256-bit Python long
131
			hash = bufreverse(hash)
132
			hash = wordreverse(hash)
133
134
			hash_str = hash.encode('hex')
135
			l = long(hash_str, 16)
136
137
			# proof-of-work test:  hash < target
138
			if l < target:
139
				print time.asctime(), "PROOF-OF-WORK found: %064x" % (l,)
140
				return (nonce + 1, nonce_bin)
141
			else:
142
				print time.asctime(), "PROOF-OF-WORK false positive %064x" % (l,)
143
#				return (nonce + 1, nonce_bin)
144
145
		return (nonce + 1, None)
146
147
	def submit_work(self, rpc, original_data, nonce_bin):
148
		nonce_bin = bufreverse(nonce_bin)
149
		nonce = nonce_bin.encode('hex')
150
		solution = original_data[:152] + nonce + original_data[160:256]
151
		param_arr = [ solution ]
152
		result = rpc.getwork(param_arr)
153
		print time.asctime(), "--> Upstream RPC result:", result
154
155
	def iterate(self, rpc):
156
		work = rpc.getwork()
157
		if work is None:
158
			time.sleep(ERR_SLEEP)
159
			return
160
		if 'data' not in work or 'target' not in work:
161
			time.sleep(ERR_SLEEP)
162
			return
163
164
		time_start = time.time()
165
166
		(hashes_done, nonce_bin) = self.work(work['data'],
167
						     work['target'])
168
169
		time_end = time.time()
170
		time_diff = time_end - time_start
171
172
		self.max_nonce = long(
173
			(hashes_done * settings['scantime']) / time_diff)
174
		if self.max_nonce > 0xfffffffaL:
175
			self.max_nonce = 0xfffffffaL
176
177
		if settings['hashmeter']:
178
			print "HashMeter(%d): %d hashes, %.2f Khash/sec" % (
179
			      self.id, hashes_done,
180
			      (hashes_done / 1000.0) / time_diff)
181
182
		if nonce_bin is not None:
183
			self.submit_work(rpc, work['data'], nonce_bin)
184
185
	def loop(self):
186
		rpc = BitcoinRPC(settings['host'], settings['port'],
187
				 settings['rpcuser'], settings['rpcpass'])
188
		if rpc is None:
189
			return
190
191
		while True:
192
			self.iterate(rpc)
193
194
def miner_thread(id):
195
	miner = Miner(id)
196
	miner.loop()
197
198
if __name__ == '__main__':
199
	if len(sys.argv) != 2:
200
		print "Usage: pyminer.py CONFIG-FILE"
201
		sys.exit(1)
202
203
	f = open(sys.argv[1])
204
	for line in f:
205
		# skip comment lines
206
		m = re.search('^\s*#', line)
207
		if m:
208
			continue
209
210
		# parse key=value lines
211
		m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
212
		if m is None:
213
			continue
214
		settings[m.group(1)] = m.group(2)
215
	f.close()
216
217
	if 'host' not in settings:
218
		settings['host'] = '127.0.0.1'
219
	if 'port' not in settings:
220
		settings['port'] = 8332
221
	if 'threads' not in settings:
222
		settings['threads'] = 1
223
	if 'hashmeter' not in settings:
224
		settings['hashmeter'] = 0
225
	if 'scantime' not in settings:
226
		settings['scantime'] = 30L
227
	if 'rpcuser' not in settings or 'rpcpass' not in settings:
228
		print "Missing username and/or password in cfg file"
229
		sys.exit(1)
230
231
	settings['port'] = int(settings['port'])
232
	settings['threads'] = int(settings['threads'])
233
	settings['hashmeter'] = int(settings['hashmeter'])
234
	settings['scantime'] = long(settings['scantime'])
235
236
	thr_list = []
237
	for thr_id in range(settings['threads']):
238
		p = Process(target=miner_thread, args=(thr_id,))
239
		p.start()
240
		thr_list.append(p)
241
		time.sleep(1)			# stagger threads
242
243
	print settings['threads'], "mining threads started"
244
245
	print time.asctime(), "Miner Starts - %s:%s" % (settings['host'], settings['port'])
246
	try:
247
		for thr_proc in thr_list:
248
			thr_proc.join()
249
	except KeyboardInterrupt:
250
		pass
251
	print time.asctime(), "Miner Stops - %s:%s" % (settings['host'], settings['port'])