pyminer: Fix memory leak, refactor to be more pythonic, maintainable, and possibly faster

Pull #4483.

Note that pyminer is not currently functional due to the removal of
getwork in cf0c47b.
This commit is contained in:
Clinton Christian 2014-07-07 23:22:23 -04:00 committed by Wladimir J. van der Laan
parent 93659379bd
commit 9192ca9e33
1 changed files with 204 additions and 187 deletions

View File

@ -5,62 +5,66 @@
# 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)
authpair = "{0}:{1}".format(username, password)
self.authhdr = "Basic {0}".format(base64.b64encode(authpair))
self.conn = httplib.HTTPConnection(host, port, strict=False, timeout=30)
def rpc(self, method, params=None):
self.OBJID += 1
obj = { 'version' : '1.1',
self.object_id += 1
obj = {'version' : '1.1',
'method' : method,
'id' : self.OBJID }
if params is None:
obj['params'] = []
else:
obj['params'] = params
'id' : self.object_id,
'params' : params or []}
self.conn.request('POST', '/', json.dumps(obj),
{ 'Authorization' : self.authhdr,
'Content-type' : 'application/json' })
resp = self.conn.getresponse()
if resp is None:
print "JSON-RPC: no response"
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"
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"
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)
@ -73,18 +77,24 @@ def bytereverse(x):
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)
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)
class Miner:
def __init__(self, id):
self.id = id
@ -132,15 +142,16 @@ class Miner:
hash = wordreverse(hash)
hash_str = hash.encode('hex')
l = long(hash_str, 16)
long_hash = long(hash_str, 16)
# proof-of-work test: hash < target
if l < target:
print time.asctime(), "PROOF-OF-WORK found: %064x" % (l,)
if long_hash < target:
print(time.asctime(), "PROOF-OF-WORK found: "
"{0:064x}".format(long_hash))
return (nonce + 1, nonce_bin)
else:
print time.asctime(), "PROOF-OF-WORK false positive %064x" % (l,)
# return (nonce + 1, nonce_bin)
print(time.asctime(), "PROOF-OF-WORK false"
"positive {0:064x}".format(long_hash))
return (nonce + 1, None)
@ -150,13 +161,16 @@ class Miner:
solution = original_data[:152] + nonce + original_data[160:256]
param_arr = [ solution ]
result = rpc.getwork(param_arr)
print time.asctime(), "--> Upstream RPC result:", result
print(time.asctime(), "--> Upstream RPC result:", result)
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
@ -171,13 +185,13 @@ class Miner:
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" % (
self.id, hashes_done,
(hashes_done / 1000.0) / time_diff)
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)
@ -185,22 +199,26 @@ class Miner:
def loop(self):
rpc = BitcoinRPC(settings['host'], settings['port'],
settings['rpcuser'], settings['rpcpass'])
if rpc is None:
return
if rpc is not None:
while True:
self.iterate(rpc)
self.conn.close()
def miner_thread(id):
miner = Miner(id)
miner.loop()
if __name__ == '__main__':
if len(sys.argv) != 2:
print "Usage: pyminer.py CONFIG-FILE"
print("Usage: pyminer.py CONFIG-FILE")
sys.exit(1)
f = open(sys.argv[1])
with open(sys.argv[1]) as f:
for line in f:
# skip comment lines
m = re.search('^\s*#', line)
@ -211,21 +229,17 @@ if __name__ == '__main__':
m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
if m is None:
continue
settings[m.group(1)] = m.group(2)
f.close()
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
settings[m.group(1)] = m.group(2)
settings.setdefault('host', '127.0.0.1')
settings.setdefault('port', 8332)
settings.setdefault('threads', 1)
settings.setdefault('hashmeter', 0)
settings.setdefault('scantime', 30L)
if 'rpcuser' not in settings or 'rpcpass' not in settings:
print "Missing username and/or password in cfg file"
print("Missing username and/or password in cfg file")
sys.exit(1)
settings['port'] = int(settings['port'])
@ -233,20 +247,23 @@ if __name__ == '__main__':
settings['hashmeter'] = int(settings['hashmeter'])
settings['scantime'] = long(settings['scantime'])
thr_list = []
for thr_id in range(settings['threads']):
p = Process(target=miner_thread, args=(thr_id,))
thread_list = []
for thread_id in range(settings['threads']):
p = Process(target=miner_thread, args=(thread_id,))
p.start()
thr_list.append(p)
thread_list.append(p)
time.sleep(1) # stagger threads
print settings['threads'], "mining threads started"
print(settings['threads'], "mining threads started")
print time.asctime(), "Miner Starts - %s:%s" % (settings['host'], settings['port'])
print(time.asctime(), "Miner Starts - {0}:{1}".format(settings['host'],
settings['port']))
try:
for thr_proc in thr_list:
thr_proc.join()
for thread_process in thread_list:
thread_process.join()
except KeyboardInterrupt:
pass
print time.asctime(), "Miner Stops - %s:%s" % (settings['host'], settings['port'])
print(time.asctime(), "Miner Stops - {0}:{1}".format(settings['host'],
settings['port']))