diff --git a/contrib/pyminer/pyminer.py b/contrib/pyminer/pyminer.py index 0a2932d66..706a10b39 100755 --- a/contrib/pyminer/pyminer.py +++ b/contrib/pyminer/pyminer.py @@ -5,248 +5,265 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -import time -import json -import pprint -import hashlib -import struct -import re -import base64 -import httplib import sys from multiprocessing import Process +import time +import struct +import hashlib +import base64 +import re +import httplib +import json ERR_SLEEP = 15 MAX_NONCE = 1000000L settings = {} -pp = pprint.PrettyPrinter(indent=4) + class BitcoinRPC: - OBJID = 1 + object_id = 1 - def __init__(self, host, port, username, password): - authpair = "%s:%s" % (username, password) - self.authhdr = "Basic %s" % (base64.b64encode(authpair)) - self.conn = httplib.HTTPConnection(host, port, False, 30) - def rpc(self, method, params=None): - self.OBJID += 1 - obj = { 'version' : '1.1', - 'method' : method, - 'id' : self.OBJID } - if params is None: - obj['params'] = [] - else: - obj['params'] = params - self.conn.request('POST', '/', json.dumps(obj), - { 'Authorization' : self.authhdr, - 'Content-type' : 'application/json' }) + def __init__(self, host, port, username, password): + authpair = "{0}:{1}".format(username, password) + self.authhdr = "Basic {0}".format(base64.b64encode(authpair)) + self.conn = httplib.HTTPConnection(host, port, strict=False, timeout=30) - resp = self.conn.getresponse() - if resp is None: - print "JSON-RPC: no response" - return None + def rpc(self, method, params=None): + self.object_id += 1 + obj = {'version' : '1.1', + 'method' : method, + 'id' : self.object_id, + 'params' : params or []} - body = resp.read() - resp_obj = json.loads(body) - if resp_obj is None: - print "JSON-RPC: cannot JSON-decode body" - return None - if 'error' in resp_obj and resp_obj['error'] != None: - return resp_obj['error'] - if 'result' not in resp_obj: - print "JSON-RPC: no result in object" - return None + self.conn.request('POST', '/', json.dumps(obj), + { 'Authorization' : self.authhdr, + 'Content-type' : 'application/json' }) - return resp_obj['result'] - def getblockcount(self): - return self.rpc('getblockcount') - def getwork(self, data=None): - return self.rpc('getwork', data) + resp = self.conn.getresponse() + + if resp is None: + print("JSON-RPC: no response") + return None + + body = resp.read() + resp_obj = json.loads(body) + + if resp_obj is None: + print("JSON-RPC: cannot JSON-decode body") + return None + + if 'error' in resp_obj and resp_obj['error'] != None: + return resp_obj['error'] + + if 'result' not in resp_obj: + print("JSON-RPC: no result in object") + return None + + return resp_obj['result'] + + def getblockcount(self): + return self.rpc('getblockcount') + + def getwork(self, data=None): + return self.rpc('getwork', data) def uint32(x): - return x & 0xffffffffL + return x & 0xffffffffL def bytereverse(x): - return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) | - (((x) >> 8) & 0x0000ff00) | ((x) >> 24) )) + return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) | + (((x) >> 8) & 0x0000ff00) | ((x) >> 24) )) def bufreverse(in_buf): - out_words = [] - for i in range(0, len(in_buf), 4): - word = struct.unpack('@I', in_buf[i:i+4])[0] - out_words.append(struct.pack('@I', bytereverse(word))) - return ''.join(out_words) + out_words = [] + + for i in range(0, len(in_buf), 4): + word = struct.unpack('@I', in_buf[i:i+4])[0] + out_words.append(struct.pack('@I', bytereverse(word))) + + return ''.join(out_words) def wordreverse(in_buf): - out_words = [] - for i in range(0, len(in_buf), 4): - out_words.append(in_buf[i:i+4]) - out_words.reverse() - return ''.join(out_words) + out_words = [] + + for i in range(0, len(in_buf), 4): + out_words.append(in_buf[i:i+4]) + + out_words.reverse() + + return ''.join(out_words) + class Miner: - def __init__(self, id): - self.id = id - self.max_nonce = MAX_NONCE + def __init__(self, id): + self.id = id + self.max_nonce = MAX_NONCE - def work(self, datastr, targetstr): - # decode work data hex string to binary - static_data = datastr.decode('hex') - static_data = bufreverse(static_data) + def work(self, datastr, targetstr): + # decode work data hex string to binary + static_data = datastr.decode('hex') + static_data = bufreverse(static_data) - # the first 76b of 80b do not change - blk_hdr = static_data[:76] + # the first 76b of 80b do not change + blk_hdr = static_data[:76] - # decode 256-bit target value - targetbin = targetstr.decode('hex') - targetbin = targetbin[::-1] # byte-swap and dword-swap - targetbin_str = targetbin.encode('hex') - target = long(targetbin_str, 16) + # decode 256-bit target value + targetbin = targetstr.decode('hex') + targetbin = targetbin[::-1] # byte-swap and dword-swap + targetbin_str = targetbin.encode('hex') + target = long(targetbin_str, 16) - # pre-hash first 76b of block header - static_hash = hashlib.sha256() - static_hash.update(blk_hdr) + # pre-hash first 76b of block header + static_hash = hashlib.sha256() + static_hash.update(blk_hdr) - for nonce in xrange(self.max_nonce): + for nonce in xrange(self.max_nonce): - # encode 32-bit nonce value - nonce_bin = struct.pack(" Upstream RPC result:", result + def submit_work(self, rpc, original_data, nonce_bin): + nonce_bin = bufreverse(nonce_bin) + nonce = nonce_bin.encode('hex') + solution = original_data[:152] + nonce + original_data[160:256] + param_arr = [ solution ] + result = rpc.getwork(param_arr) - def iterate(self, rpc): - work = rpc.getwork() - if work is None: - time.sleep(ERR_SLEEP) - return - if 'data' not in work or 'target' not in work: - time.sleep(ERR_SLEEP) - return + print(time.asctime(), "--> Upstream RPC result:", result) - time_start = time.time() + def iterate(self, rpc): + work = rpc.getwork() - (hashes_done, nonce_bin) = self.work(work['data'], - work['target']) + if work is None: + time.sleep(ERR_SLEEP) + return - time_end = time.time() - time_diff = time_end - time_start + if 'data' not in work or 'target' not in work: + time.sleep(ERR_SLEEP) + return - self.max_nonce = long( - (hashes_done * settings['scantime']) / time_diff) - if self.max_nonce > 0xfffffffaL: - self.max_nonce = 0xfffffffaL + time_start = time.time() - if settings['hashmeter']: - print "HashMeter(%d): %d hashes, %.2f Khash/sec" % ( - self.id, hashes_done, - (hashes_done / 1000.0) / time_diff) + (hashes_done, nonce_bin) = self.work(work['data'], + work['target']) - if nonce_bin is not None: - self.submit_work(rpc, work['data'], nonce_bin) + time_end = time.time() + time_diff = time_end - time_start - def loop(self): - rpc = BitcoinRPC(settings['host'], settings['port'], - settings['rpcuser'], settings['rpcpass']) - if rpc is None: - return + self.max_nonce = long( + (hashes_done * settings['scantime']) / time_diff) + + if self.max_nonce > 0xfffffffaL: + self.max_nonce = 0xfffffffaL + + if settings['hashmeter']: + print("HashMeter({:d}): {:d} hashes, {:.2f} Khash/sec".format( + self.id, hashes_done, (hashes_done / 1000.0) / time_diff)) + + if nonce_bin is not None: + self.submit_work(rpc, work['data'], nonce_bin) + + def loop(self): + rpc = BitcoinRPC(settings['host'], settings['port'], + settings['rpcuser'], settings['rpcpass']) + + if rpc is not None: + + while True: + self.iterate(rpc) + + self.conn.close() - while True: - self.iterate(rpc) def miner_thread(id): - miner = Miner(id) - miner.loop() + miner = Miner(id) + miner.loop() if __name__ == '__main__': - if len(sys.argv) != 2: - print "Usage: pyminer.py CONFIG-FILE" - sys.exit(1) + if len(sys.argv) != 2: + print("Usage: pyminer.py CONFIG-FILE") + sys.exit(1) - f = open(sys.argv[1]) - for line in f: - # skip comment lines - m = re.search('^\s*#', line) - if m: - continue + with open(sys.argv[1]) as f: - # parse key=value lines - m = re.search('^(\w+)\s*=\s*(\S.*)$', line) - if m is None: - continue - settings[m.group(1)] = m.group(2) - f.close() + for line in f: + # skip comment lines + m = re.search('^\s*#', line) + if m: + continue - if 'host' not in settings: - settings['host'] = '127.0.0.1' - if 'port' not in settings: - settings['port'] = 8332 - if 'threads' not in settings: - settings['threads'] = 1 - if 'hashmeter' not in settings: - settings['hashmeter'] = 0 - if 'scantime' not in settings: - settings['scantime'] = 30L - if 'rpcuser' not in settings or 'rpcpass' not in settings: - print "Missing username and/or password in cfg file" - sys.exit(1) + # parse key=value lines + m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + if m is None: + continue - settings['port'] = int(settings['port']) - settings['threads'] = int(settings['threads']) - settings['hashmeter'] = int(settings['hashmeter']) - settings['scantime'] = long(settings['scantime']) + settings[m.group(1)] = m.group(2) - thr_list = [] - for thr_id in range(settings['threads']): - p = Process(target=miner_thread, args=(thr_id,)) - p.start() - thr_list.append(p) - time.sleep(1) # stagger threads + settings.setdefault('host', '127.0.0.1') + settings.setdefault('port', 8332) + settings.setdefault('threads', 1) + settings.setdefault('hashmeter', 0) + settings.setdefault('scantime', 30L) - print settings['threads'], "mining threads started" + if 'rpcuser' not in settings or 'rpcpass' not in settings: + print("Missing username and/or password in cfg file") + sys.exit(1) - print time.asctime(), "Miner Starts - %s:%s" % (settings['host'], settings['port']) - try: - for thr_proc in thr_list: - thr_proc.join() - except KeyboardInterrupt: - pass - print time.asctime(), "Miner Stops - %s:%s" % (settings['host'], settings['port']) + settings['port'] = int(settings['port']) + settings['threads'] = int(settings['threads']) + settings['hashmeter'] = int(settings['hashmeter']) + settings['scantime'] = long(settings['scantime']) + thread_list = [] + + for thread_id in range(settings['threads']): + p = Process(target=miner_thread, args=(thread_id,)) + p.start() + thread_list.append(p) + time.sleep(1) # stagger threads + + print(settings['threads'], "mining threads started") + + print(time.asctime(), "Miner Starts - {0}:{1}".format(settings['host'], + settings['port'])) + try: + for thread_process in thread_list: + thread_process.join() + except KeyboardInterrupt: + pass + + print(time.asctime(), "Miner Stops - {0}:{1}".format(settings['host'], + settings['port']))