You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pyminer.py 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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. import sys
  8. from multiprocessing import Process
  9. import time
  10. import struct
  11. import hashlib
  12. import base64
  13. import re
  14. import httplib
  15. import json
  16. ERR_SLEEP = 15
  17. MAX_NONCE = 1000000L
  18. settings = {}
  19. class BitcoinRPC:
  20. object_id = 1
  21. def __init__(self, host, port, username, password):
  22. authpair = "{0}:{1}".format(username, password)
  23. self.authhdr = "Basic {0}".format(base64.b64encode(authpair))
  24. self.conn = httplib.HTTPConnection(host, port, strict=False, timeout=30)
  25. def rpc(self, method, params=None):
  26. self.object_id += 1
  27. obj = {'version' : '1.1',
  28. 'method' : method,
  29. 'id' : self.object_id,
  30. 'params' : params or []}
  31. self.conn.request('POST', '/', json.dumps(obj),
  32. { 'Authorization' : self.authhdr,
  33. 'Content-type' : 'application/json' })
  34. resp = self.conn.getresponse()
  35. if resp is None:
  36. print("JSON-RPC: no response")
  37. return None
  38. body = resp.read()
  39. resp_obj = json.loads(body)
  40. if resp_obj is None:
  41. print("JSON-RPC: cannot JSON-decode body")
  42. return None
  43. if 'error' in resp_obj and resp_obj['error'] != None:
  44. return resp_obj['error']
  45. if 'result' not in resp_obj:
  46. print("JSON-RPC: no result in object")
  47. return None
  48. return resp_obj['result']
  49. def getblockcount(self):
  50. return self.rpc('getblockcount')
  51. def getwork(self, data=None):
  52. return self.rpc('getwork', data)
  53. def uint32(x):
  54. return x & 0xffffffffL
  55. def bytereverse(x):
  56. return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) |
  57. (((x) >> 8) & 0x0000ff00) | ((x) >> 24) ))
  58. def bufreverse(in_buf):
  59. out_words = []
  60. for i in range(0, len(in_buf), 4):
  61. word = struct.unpack('@I', in_buf[i:i+4])[0]
  62. out_words.append(struct.pack('@I', bytereverse(word)))
  63. return ''.join(out_words)
  64. def wordreverse(in_buf):
  65. out_words = []
  66. for i in range(0, len(in_buf), 4):
  67. out_words.append(in_buf[i:i+4])
  68. out_words.reverse()
  69. return ''.join(out_words)
  70. class Miner:
  71. def __init__(self, id):
  72. self.id = id
  73. self.max_nonce = MAX_NONCE
  74. def work(self, datastr, targetstr):
  75. # decode work data hex string to binary
  76. static_data = datastr.decode('hex')
  77. static_data = bufreverse(static_data)
  78. # the first 76b of 80b do not change
  79. blk_hdr = static_data[:76]
  80. # decode 256-bit target value
  81. targetbin = targetstr.decode('hex')
  82. targetbin = targetbin[::-1] # byte-swap and dword-swap
  83. targetbin_str = targetbin.encode('hex')
  84. target = long(targetbin_str, 16)
  85. # pre-hash first 76b of block header
  86. static_hash = hashlib.sha256()
  87. static_hash.update(blk_hdr)
  88. for nonce in xrange(self.max_nonce):
  89. # encode 32-bit nonce value
  90. nonce_bin = struct.pack("<I", nonce)
  91. # hash final 4b, the nonce value
  92. hash1_o = static_hash.copy()
  93. hash1_o.update(nonce_bin)
  94. hash1 = hash1_o.digest()
  95. # sha256 hash of sha256 hash
  96. hash_o = hashlib.sha256()
  97. hash_o.update(hash1)
  98. hash = hash_o.digest()
  99. # quick test for winning solution: high 32 bits zero?
  100. if hash[-4:] != '\0\0\0\0':
  101. continue
  102. # convert binary hash to 256-bit Python long
  103. hash = bufreverse(hash)
  104. hash = wordreverse(hash)
  105. hash_str = hash.encode('hex')
  106. long_hash = long(hash_str, 16)
  107. # proof-of-work test: hash < target
  108. if long_hash < target:
  109. print(time.asctime(), "PROOF-OF-WORK found: "
  110. "{0:064x}".format(long_hash))
  111. return (nonce + 1, nonce_bin)
  112. else:
  113. print(time.asctime(), "PROOF-OF-WORK false"
  114. "positive {0:064x}".format(long_hash))
  115. return (nonce + 1, None)
  116. def submit_work(self, rpc, original_data, nonce_bin):
  117. nonce_bin = bufreverse(nonce_bin)
  118. nonce = nonce_bin.encode('hex')
  119. solution = original_data[:152] + nonce + original_data[160:256]
  120. param_arr = [ solution ]
  121. result = rpc.getwork(param_arr)
  122. print(time.asctime(), "--> Upstream RPC result:", result)
  123. def iterate(self, rpc):
  124. work = rpc.getwork()
  125. if work is None:
  126. time.sleep(ERR_SLEEP)
  127. return
  128. if 'data' not in work or 'target' not in work:
  129. time.sleep(ERR_SLEEP)
  130. return
  131. time_start = time.time()
  132. (hashes_done, nonce_bin) = self.work(work['data'],
  133. work['target'])
  134. time_end = time.time()
  135. time_diff = time_end - time_start
  136. self.max_nonce = long(
  137. (hashes_done * settings['scantime']) / time_diff)
  138. if self.max_nonce > 0xfffffffaL:
  139. self.max_nonce = 0xfffffffaL
  140. if settings['hashmeter']:
  141. print("HashMeter({:d}): {:d} hashes, {:.2f} Khash/sec".format(
  142. self.id, hashes_done, (hashes_done / 1000.0) / time_diff))
  143. if nonce_bin is not None:
  144. self.submit_work(rpc, work['data'], nonce_bin)
  145. def loop(self):
  146. rpc = BitcoinRPC(settings['host'], settings['port'],
  147. settings['rpcuser'], settings['rpcpass'])
  148. if rpc is not None:
  149. while True:
  150. self.iterate(rpc)
  151. self.conn.close()
  152. def miner_thread(id):
  153. miner = Miner(id)
  154. miner.loop()
  155. if __name__ == '__main__':
  156. if len(sys.argv) != 2:
  157. print("Usage: pyminer.py CONFIG-FILE")
  158. sys.exit(1)
  159. with open(sys.argv[1]) as f:
  160. for line in f:
  161. # skip comment lines
  162. m = re.search('^\s*#', line)
  163. if m:
  164. continue
  165. # parse key=value lines
  166. m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
  167. if m is None:
  168. continue
  169. settings[m.group(1)] = m.group(2)
  170. settings.setdefault('host', '127.0.0.1')
  171. settings.setdefault('port', 8332)
  172. settings.setdefault('threads', 1)
  173. settings.setdefault('hashmeter', 0)
  174. settings.setdefault('scantime', 30L)
  175. if 'rpcuser' not in settings or 'rpcpass' not in settings:
  176. print("Missing username and/or password in cfg file")
  177. sys.exit(1)
  178. settings['port'] = int(settings['port'])
  179. settings['threads'] = int(settings['threads'])
  180. settings['hashmeter'] = int(settings['hashmeter'])
  181. settings['scantime'] = long(settings['scantime'])
  182. thread_list = []
  183. for thread_id in range(settings['threads']):
  184. p = Process(target=miner_thread, args=(thread_id,))
  185. p.start()
  186. thread_list.append(p)
  187. time.sleep(1) # stagger threads
  188. print(settings['threads'], "mining threads started")
  189. print(time.asctime(), "Miner Starts - {0}:{1}".format(settings['host'],
  190. settings['port']))
  191. try:
  192. for thread_process in thread_list:
  193. thread_process.join()
  194. except KeyboardInterrupt:
  195. pass
  196. print(time.asctime(), "Miner Stops - {0}:{1}".format(settings['host'],
  197. settings['port']))