wusuowei

python-multithreading-btc-miner

Jul 10th, 2020
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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'])
Add Comment
Please, Sign In to add comment