|
|
|
@ -5,62 +5,66 @@
@@ -5,62 +5,66 @@
|
|
|
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|
|
|
|
# |
|
|
|
|
|
|
|
|
|
import sys |
|
|
|
|
from multiprocessing import Process |
|
|
|
|
import time |
|
|
|
|
import json |
|
|
|
|
import pprint |
|
|
|
|
import hashlib |
|
|
|
|
import struct |
|
|
|
|
import re |
|
|
|
|
import hashlib |
|
|
|
|
import base64 |
|
|
|
|
import re |
|
|
|
|
import httplib |
|
|
|
|
import sys |
|
|
|
|
from multiprocessing import Process |
|
|
|
|
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 |
|
|
|
|
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):
@@ -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:
@@ -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:
@@ -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:
@@ -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:
@@ -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__':
@@ -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.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__':
@@ -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'])) |
|
|
|
|