SHARE
TWEET

simpleminer.py

jcomeau_ictx Dec 25th, 2013 234 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/python -OO
  2. 'adaptation of simpleminer for scrypt'
  3. import sys, os, time, json, hashlib, struct, re, httplib, ast, base64
  4. # if fails import of scrypt, you need to: sudo pip install scrypt
  5. import multiprocessing, select, random, scrypt
  6. TIMEOUT = 120  # seconds to wait for server response
  7. START_TIME = time.time()
  8. PERSISTENT = {'solved': False}  # global for storing settings
  9. CONFIGFILE = os.getenv('SIMPLEMINER_CONFIG', None) or \
  10.  os.path.expanduser('~/.litecoin/litecoin.conf')
  11. MULTIPLIER = int(os.getenv('SIMPLEMINER_MULTIPLIER', '1'))
  12. THREADS = multiprocessing.cpu_count() * MULTIPLIER
  13. INT_SIZE = 4
  14. HEX_INT_SIZE = INT_SIZE * 2
  15. HEADER_SIZE = 80
  16. HEX_HEADER_SIZE = HEADER_SIZE * 2
  17. MAX_SECONDS = 60 if __debug__ else 20
  18. MAX_GETWORK_FAILS = 5
  19. ALWAYS = True
  20. # TEST from litecoin block 29255, see https://litecoin.info/Scrypt
  21. TEST_HEADER = \
  22.  '01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3b10b9bbcc' \
  23.  '30639f279fcaa86746e1ef52d3edb3c4ad8259920d509bd073605c9bf1d59983' \
  24.  '752a6b06b817bb4ea78e011d012d59d4'
  25. TEST_TARGET = \
  26.  '000000018ea70000000000000000000000000000000000000000000000000000'
  27. TEST_NONCE = 3562614017
  28. def key_value(line):
  29.  'parse key and value from configuration line'
  30.  match = re.match('^(\w+)\s*=\s*(\S+)', line)
  31.  return match.groups() if match else None
  32. def parse_config(config_file):
  33.  input = open(config_file)
  34.  settings = dict(filter(None, map(key_value, input.readlines())))
  35.  input.close()
  36.  debug('settings: %s' % settings)
  37.  return settings
  38. def debug(message, always = False):
  39.  if __debug__ or always:
  40.   if message:
  41.    print >>sys.stderr, message
  42. def establish_connection():
  43.  PERSISTENT['rpcserver'] = httplib.HTTPConnection(
  44.   PERSISTENT['settings']['rpcconnect'],
  45.   PERSISTENT['settings']['rpcport'], False, TIMEOUT)
  46.  PERSISTENT['rpcserver'].set_debuglevel(__debug__ or 0)
  47. def init():
  48.  PERSISTENT['settings'] = parse_config(CONFIGFILE)
  49.  PERSISTENT['authorization'] = base64.b64encode('%s:%s' % (
  50.   PERSISTENT['settings']['rpcuser'], PERSISTENT['settings']['rpcpassword']))
  51.  debug('settings now: %s' % PERSISTENT)
  52. def rpc(method, parameters = []):
  53.  debug('making rpc call with parameters = %s' % parameters)
  54.  rpc_call = {'version': '1.1', 'method': method, 'id': 0, 'params': parameters}
  55.  try:
  56.   establish_connection()
  57.   PERSISTENT['rpcserver'].request('POST', '/', json.dumps(rpc_call),
  58.    {'Authorization': 'Basic %s' % PERSISTENT['authorization'],
  59.     'Content-type': 'application/json'})
  60.   response = PERSISTENT['rpcserver'].getresponse()
  61.   message = response.read()
  62.   response_object = json.loads(message)
  63.   response.close()
  64.  except:
  65.   response_object = {'error': 'No response or null response', 'result': None}
  66.   if __debug__: raise
  67.  debug(response_object.get('error', None))
  68.  return response_object
  69. def getwork(data = None):
  70.  if os.getenv('SIMPLEMINER_FAKE_DATA', False):
  71.   if not data:
  72.    debug('***WARNING*** this is static test data, not from server!')
  73.    work = {'result': {
  74.     'data': bufreverse(pad(TEST_HEADER.decode('hex'))).encode('hex'),
  75.     'target': TEST_TARGET.decode('hex')[::-1].encode('hex'),
  76.     'algorithm': 'scrypt:1024,1,1',
  77.    }}
  78.   else:
  79.    debug('getwork called with data %s' % repr(data))
  80.    work = {}
  81.  else:
  82.   work = rpc('getwork', data)
  83.   debug('result of getwork(): %s' % work, ALWAYS)
  84.  return work.get('result', None)
  85. def miner_thread(thread_id, work, pipe):
  86.  hashes, found = 0, False
  87.  thread_start = time.time()
  88.  debug('thread %d running bruteforce for %d seconds with random nonces' % (
  89.   thread_id, MAX_SECONDS))
  90.  while time.time() < thread_start + MAX_SECONDS:
  91.   nonce = random.getrandbits(32)
  92.   data = work + struct.pack('<I', nonce)
  93.   hashed = PERSISTENT['hash'](data)
  94.   hashes += 1
  95.   if hashed.endswith(PERSISTENT['check_for']):
  96.    found = True
  97.    pipe.send(nonce)
  98.    debug('thread %d found possible nonce 0x%08x after %d reps' % (
  99.     thread_id, nonce, hashes), ALWAYS)
  100.  pipe.send((hashes, thread_id))
  101.  return
  102. def bufreverse(data = None):
  103.  '''\
  104.  reverse groups of 4 bytes in arbitrary string of bits
  105.  
  106.  >>> bufreverse('123423453456456756786789')
  107.  '432154326543765487659876'
  108.  '''
  109.  if data is None:
  110.   return None
  111.  length = len(data) / INT_SIZE
  112.  return struct.pack('>%dI' % length, *(struct.unpack('<%dI' % length, data)))
  113. def sha256d_hash(data):
  114.  'return block hash as a little-endian 256-bit number encoded as a bitstring'
  115.  hashed = hashlib.sha256(hashlib.sha256(data).digest()).digest()
  116.  return hashed
  117. def scrypt_hash(data):
  118.  hashed = scrypt.hash(data, data, 1024, 1, 1, 32)
  119.  return hashed
  120. def check_hash(data = TEST_HEADER.decode('hex'), target = None, nonce = None):
  121.  if nonce is None:
  122.   nonce = struct.unpack('<I', data[-INT_SIZE:])[0]
  123.  else:
  124.   data = data[:HEADER_SIZE - INT_SIZE] + struct.pack('<I', nonce)
  125.  if target and PERSISTENT.get('hash', None):
  126.   check_hash = PERSISTENT['hash'](data)[::-1]  # convert to big-endian
  127.   debug('comparing:\n %s nonce 0x%08x to\n %s' % (
  128.    check_hash.encode('hex'), nonce, target.encode('hex')), ALWAYS)
  129.   return check_hash < target
  130.  else:
  131.   print 'header: %s, nonce: 0x%08x (%d)' % (data.encode('hex'), nonce, nonce)
  132.   print 'sha256: %s' % sha256d_hash(data)[::-1].encode('hex')
  133.   print 'scrypt: %s' % scrypt_hash(data)[::-1].encode('hex')
  134. def simpleminer():
  135.  if THREADS < 4:
  136.   raise Exception('must have at least four threads for this to work')
  137.  init()
  138.  consecutive_errors = 0
  139.  while True:
  140.   start_time = time.time()
  141.   work = getwork()
  142.   if not work:
  143.    consecutive_errors += 1
  144.    if consecutive_errors == MAX_GETWORK_FAILS:
  145.     raise Exception('too many getwork() errors, has daemon crashed?')
  146.    else:
  147.     print >>sys.stderr, 'waiting for work'
  148.     time.sleep(5)
  149.     continue
  150.   else:
  151.    consecutive_errors = 0
  152.   data = bufreverse(work['data'].decode('hex'))[:HEADER_SIZE - INT_SIZE]
  153.   target = work['target'].decode('hex')[::-1]
  154.   algorithm = work.get('algorithm', 'sha256d')
  155.   if algorithm == 'scrypt:1024,1,1':
  156.    PERSISTENT['hash'] = scrypt_hash
  157.    PERSISTENT['check_for'] = '\0\0\0'
  158.   elif algorithm == 'sha256d':
  159.    PERSISTENT['hash'] = sha256d_hash
  160.    PERSISTENT['check_for'] = '\0\0\0\0'
  161.   else:
  162.    raise Exception('unknown algorithm: %s' % algorithm)
  163.   debug('work: %s' % data.encode('hex'), ALWAYS)
  164.   debug('target: %s' % target.encode('hex'), ALWAYS)
  165.   pipe_list = []
  166.   total_hashes, done = 0, 0
  167.   for thread_id in range(THREADS):
  168.    parent_end, child_end = multiprocessing.Pipe()
  169.    thread = multiprocessing.Process(
  170.     target = miner_thread, args = (thread_id, data, child_end))
  171.    thread.start()
  172.    pipe_list.append(parent_end)
  173.   debug('%d mining threads started' % THREADS)
  174.   while done < THREADS:
  175.    readable = select.select(pipe_list, [], [])[0]
  176.    for pipe in readable:
  177.     nonce = pipe.recv()
  178.     debug('received: %s' % repr(nonce))
  179.     if type(nonce) in (int, long):
  180.      debug('checking hash for nonce 0x%08x' % nonce)
  181.      if check_hash(data, target, nonce):
  182.       PERSISTENT['solved'] = True
  183.       getwork([work['data'][:HEX_HEADER_SIZE - HEX_INT_SIZE] + \
  184.        struct.pack('>I', nonce).encode('hex') + \
  185.        work['data'][HEX_HEADER_SIZE:]])
  186.      else:
  187.       debug('nonce %08x failed threshold' % nonce)
  188.     else:
  189.      hashes, thread_id = nonce
  190.      total_hashes += hashes
  191.      debug('thread %d finished' % thread_id)
  192.      done += 1
  193.   debug('threads finished')
  194.   delta_time = time.time() - start_time
  195.   debug('Combined HashMeter: %d hashes in %.2f sec, %d Khash/sec' % (
  196.     total_hashes, delta_time, (total_hashes / 1000) / delta_time), ALWAYS)
  197.   while multiprocessing.active_children():
  198.    time.sleep(0.1)  # joins finished processes
  199.   if os.getenv('SIMPLEMINER_FAKE_DATA', False) and PERSISTENT['solved']:
  200.    break  # for timing and/or profiling
  201.  return 'done'
  202. def pad(message = ''):
  203.  '''
  204.  pad a message out to 512 bits (64 bytes)
  205.  
  206.  append the bit '1' to the message
  207.  append k bits '0', where k is the minimum number >= 0 such that the
  208.  resulting message length (in bits) is 448 (modulo 512).
  209.  append length of message (before pre-processing), in bits, as 64-bit
  210.  big-endian integer
  211.  >>> len(pad('x' * (64 - 9)))
  212.  64
  213.  >>> len(pad('x' * (64 - 8)))
  214.  128
  215.  '''
  216.  length = len(message)
  217.  # 64 bytes is 512 bits; 9 is minimum padding we need for count plus 1-bit
  218.  padding_needed = 64 - (length % 64)
  219.  padding_needed += 64 * (padding_needed < 9)
  220.  bit_length = length * 8
  221.  packed_length = struct.pack('>2I', bit_length / 0x100000000, bit_length)
  222.  padding = '\x80' + '\0' * (padding_needed - 9)
  223.  padding += packed_length
  224.  return message + padding
  225. if __name__ == '__main__':
  226.  command = os.path.splitext(os.path.basename(sys.argv[0]))[0]
  227.  print 'exit status: %s' % eval(command)(*sys.argv[1:])
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top