derjanb

Untitled

Jun 29th, 2011
326
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.85 KB | None | 0 0
  1. import sys
  2. import socket
  3. import httplib
  4. import traceback
  5.  
  6. import pyopencl as cl
  7.  
  8. from sha256 import *
  9. from hashlib import md5
  10. from base64 import b64encode
  11. from time import sleep, time
  12. from json import dumps, loads
  13. from datetime import datetime
  14. from urlparse import urlsplit
  15. from Queue import Queue, Empty
  16. from struct import pack, unpack, error
  17. from threading import Thread, RLock
  18.  
  19. # Socket wrapper to enable socket.TCP_NODELAY and KEEPALIVE
  20. realsocket = socket.socket
  21. def socketwrap(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
  22.     sockobj = realsocket(family, type, proto)
  23.     sockobj.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
  24.     sockobj.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
  25.     return sockobj
  26. socket.socket = socketwrap
  27.  
  28.  
  29. VERSION = '2011.beta4.derjanb.v1'
  30.  
  31. USER_AGENT = 'poclbm/' + VERSION
  32.  
  33. TIME_FORMAT = '%d/%m/%Y %H:%M:%S'
  34.  
  35. TIMEOUT = 10
  36.  
  37. RETRIES = 3
  38.  
  39. LONG_POLL_TIMEOUT = 3600
  40.  
  41. LONG_POLL_MAX_ASKRATE = 60 - TIMEOUT
  42.  
  43. MAX_REDIRECTS = 3
  44.  
  45. OUTPUT_SIZE = 0x100
  46.  
  47.  
  48. def belowOrEquals(hash, target):
  49.     for i in range(len(hash) - 1, -1, -1):
  50.         reversed = bytereverse(hash[i])
  51.         if reversed < target[i]:
  52.             return True
  53.         elif reversed > target[i]:
  54.             return False
  55.     return True
  56.  
  57. def if_else(condition, trueVal, falseVal):
  58.     if condition:
  59.         return trueVal
  60.     else:
  61.         return falseVal
  62.  
  63. def patch(data):
  64.     pos = data.find('\x7fELF', 1)
  65.     if pos != -1 and data.find('\x7fELF', pos+1) == -1:
  66.         data2 = data[pos:]
  67.         try:
  68.             (id, a, b, c, d, e, f, offset, g, h, i, j, entrySize, count, index) = unpack('QQHHIIIIIHHHHHH', data2[:52])
  69.             if id == 0x64010101464c457f and offset != 0:
  70.                 (a, b, c, d, nameTableOffset, size, e, f, g, h) = unpack('IIIIIIIIII', data2[offset+index * entrySize : offset+(index+1) * entrySize])
  71.                 header = data2[offset : offset+count * entrySize]
  72.                 firstText = True
  73.                 for i in xrange(count):
  74.                     entry = header[i * entrySize : (i+1) * entrySize]
  75.                     (nameIndex, a, b, c, offset, size, d, e, f, g) = unpack('IIIIIIIIII', entry)
  76.                     nameOffset = nameTableOffset + nameIndex
  77.                     name = data2[nameOffset : data2.find('\x00', nameOffset)]
  78.                     if name == '.text':
  79.                         if firstText: firstText = False
  80.                         else:
  81.                             data2 = data2[offset : offset + size]
  82.                             patched = ''
  83.                             for i in xrange(len(data2) / 8):
  84.                                 instruction, = unpack('Q', data2[i * 8 : i * 8 + 8])
  85.                                 if (instruction&0x9003f00002001000) == 0x0001a00000000000:
  86.                                     instruction ^= (0x0001a00000000000 ^ 0x0000c00000000000)
  87.                                 patched += pack('Q', instruction)
  88.                             return ''.join([data[:pos+offset], patched, data[pos + offset + size:]])
  89.         except error:
  90.             pass
  91.     return data
  92.  
  93. class NotAuthorized(Exception): pass
  94. class RPCError(Exception): pass
  95.  
  96. class BitcoinMiner():
  97.     def __init__(self, device, backup, tolerance, failback, host, user, password, port=8332, frames=30, rate=1, askrate=5, worksize=-1, vectors=False, verbose=False, frameSleep=0):
  98.         (self.defines, self.rateDivisor, self.hashspace) = if_else(vectors, ('-DVECTORS', 500, 0x7FFFFFFF), ('', 1000, 0xFFFFFFFF))
  99.         self.defines += (' -DOUTPUT_SIZE=' + str(OUTPUT_SIZE))
  100.         self.defines += (' -DOUTPUT_MASK=' + str(OUTPUT_SIZE - 1))
  101.  
  102.         self.device = device
  103.         self.rate = max(float(rate), 0.1)
  104.         self.askrate = max(int(askrate), 1)
  105.         self.askrate = min(self.askrate, 10)
  106.         self.worksize = int(worksize)
  107.         self.frames = max(int(frames), 3)
  108.         self.verbose = verbose
  109.         self.frameSleep = frameSleep
  110.         self.longPollActive = self.stop = False
  111.         self.update = True
  112.         self.lock = RLock()
  113.         self.outputLock = RLock()
  114.         self.lastWork = 0
  115.         self.lastBlock = self.updateTime = self.longPollURL = ''
  116.  
  117.         self.workQueue = Queue()
  118.         self.resultQueue = Queue()
  119.  
  120.         self.backup_pool_index = 0
  121.         self.errors = 0
  122.         self.tolerance = tolerance
  123.         self.failback = failback
  124.         self.failback_getwork_count = 0
  125.         self.failback_attempt_count = 0
  126.         self.pool = None
  127.         self.currentrate = 0
  128.  
  129.         host = '%s:%s' % (host.replace('http://', ''), port)
  130.         self.primary = (user, password, host)
  131.         self.setpool(self.primary)
  132.  
  133.         self.postdata = {'method': 'getwork', 'id': 'json'}
  134.         self.connection = None
  135.  
  136.         self.backup = []
  137.         if backup:
  138.             for pool in backup.split(','):
  139.                 try:
  140.                     user, temp = pool.split(':', 1)
  141.                     pwd, host = temp.split('@')
  142.                     self.backup.append((user, pwd, host))
  143.                 except ValueError:
  144.                     self.sayLine('Ignored invalid backup pool: %s', pool)
  145.                     continue
  146.         self.sayLine(' running %s', USER_AGENT)
  147.  
  148.     def say(self, format, args=()):
  149.         with self.outputLock:
  150.             p = format % args
  151.             if self.verbose:
  152.                 print '%s,' % datetime.now().strftime(TIME_FORMAT), p
  153.             else:
  154.                 pool = self.pool[2]+' ' if self.pool else ''
  155.                 sys.stdout.write('\r%s\r%s%s' % (" "*len(p), pool, p))
  156.             sys.stdout.flush()
  157.  
  158.     def sayLine(self, format, args=()):
  159.         if not self.verbose:
  160.             format = '%s, %s\n' % (datetime.now().strftime(TIME_FORMAT), format)
  161.         self.say(format, args)
  162.  
  163.     def exit(self):
  164.         self.stop = True
  165.  
  166.     def hashrate(self, rate):
  167.         self.currentrate = (self.currentrate + rate) / 2
  168.         self.say('%s khash/s', rate)
  169.  
  170.     def failure(self, message):
  171.         print '\n%s' % message
  172.         self.exit()
  173.  
  174.     def diff1Found(self, hash, target):
  175.         if self.verbose and target < 0xFFFF0000L:
  176.             self.sayLine('checking %s <= %s', (hash, target))
  177.  
  178.     def blockFound(self, hash, accepted):
  179.         self.sayLine('%s, %s', (hash, if_else(accepted, 'accepted', 'invalid or stale')))
  180.  
  181.     def mine(self):
  182.         self.stop = False
  183.         longPollThread = Thread(target=self.longPollThread)
  184.         longPollThread.daemon = True
  185.         longPollThread.start()
  186.         Thread(target=self.miningThread).start()
  187.  
  188.         while True:
  189.             if self.stop: return
  190.             try:
  191.                 with self.lock:
  192.                     update = self.update = (self.update or time() - self.lastWork > if_else(self.longPollActive, LONG_POLL_MAX_ASKRATE, self.askrate))
  193.                 if update:
  194.                     work = self.getwork()
  195.                     with self.lock:
  196.                         if self.update:
  197.                             self.queueWork(work)
  198.  
  199.                 with self.lock:
  200.                     if not self.resultQueue.empty():
  201.                         self.sendResult(self.resultQueue.get(False))
  202.                 sleep(1)
  203.             except Exception:
  204.                 self.sayLine("Unexpected error:")
  205.                 traceback.print_exc()
  206.  
  207.     def queueWork(self, work):
  208.         with self.lock:
  209.             self.workQueue.put(work)
  210.             if work:
  211.                 self.update = False; self.lastWork = time()
  212.                 if self.lastBlock != work['data'][48:56]:
  213.                     self.lastBlock = work['data'][48:56]
  214.                     while not self.resultQueue.empty():
  215.                         self.resultQueue.get(False)
  216.  
  217.     def sendResult(self, result):
  218.         for i in xrange(OUTPUT_SIZE):
  219.             if result['output'][i]:
  220.                 h = hash(result['state'], result['data'][0], result['data'][1], result['data'][2], result['output'][i])
  221.                 if h[7] != 0:
  222.                     self.failure('Verification failed, check hardware!')
  223.                 else:
  224.                     self.diff1Found(bytereverse(h[6]), result['target'][6])
  225.                     if belowOrEquals(h[:7], result['target'][:7]):
  226.                         d = result['work']['data']
  227.                         d = ''.join([d[:136], pack('I', long(result['data'][1])).encode('hex'), d[144:152], pack('I', long(result['output'][i])).encode('hex'), d[160:]])
  228.                         accepted = self.getwork(d)
  229.                         if accepted != None:
  230.                             self.blockFound(pack('I', long(h[6])).encode('hex'), accepted)
  231.  
  232.     def getAscii(self, n):
  233.         cnt = n % 4
  234.         if cnt == 0:
  235.             return '|'
  236.         if cnt == 1:
  237.             return '/'
  238.         if cnt == 2:
  239.             return '-'
  240.         if cnt == 3:
  241.             return '\\'
  242.         return ' '
  243.  
  244.     def getwork(self, data=None):
  245.         max = if_else(data, 2*RETRIES, RETRIES)
  246.         for n in range(1, max):
  247.             save_pool = None
  248.             try:
  249.                 if self.pool != self.primary and self.failback > 0:
  250.                     if self.failback_getwork_count >= self.failback:
  251.                         save_pool = self.pool
  252.                         self.setpool(self.primary)
  253.                         self.connection = None
  254.                         self.sayLine("Attempting to fail back to primary pool")
  255.                     self.failback_getwork_count += 1
  256.                 if not self.connection:
  257.                     self.connection = httplib.HTTPConnection(self.host, strict=True, timeout=TIMEOUT)
  258.                 self.postdata['params'] = if_else(data, [data], [])
  259.                 (self.connection, result) = self.request(self.connection, '/', self.headers, dumps(self.postdata))
  260.                 self.errors = 0
  261.                 if self.pool == self.primary:
  262.                     self.backup_pool_index = 0
  263.                     self.failback_getwork_count = 0
  264.                     self.failback_attempt_count = 0
  265.                 return result['result']
  266.             except NotAuthorized:
  267.                 self.failure('Wrong username or password')
  268.                 return
  269.             except RPCError as e:
  270.                 self.say('%s', e)
  271.             except (IOError, httplib.HTTPException, ValueError):
  272.                 if save_pool:
  273.                     self.failback_attempt_count += 1
  274.                     self.setpool(save_pool)
  275.                     self.sayLine('Still unable to reconnect to primary pool (attempt %s), failing over', self.failback_attempt_count)
  276.                     self.failback_getwork_count = 0
  277.                     return
  278.                 if n == max:
  279.                     self.say('Problems communicating with bitcoin RPC %s %s', (self.errors, self.tolerance))
  280.                     self.errors += 1
  281.                 if self.errors > self.tolerance+1:
  282.                     self.errors = 0
  283.                     if self.backup_pool_index >= len(self.backup):
  284.                         self.sayLine("No more backup pools left. Using primary and starting over.")
  285.                         pool = self.primary
  286.                         self.backup_pool_index = 0
  287.                     else:
  288.                         pool = self.backup[self.backup_pool_index]
  289.                         self.backup_pool_index += 1
  290.                     self.setpool(pool)
  291.         if (self.connection):
  292.             self.connection.close()
  293.             self.connection = None
  294.         self.say('Please wait: %s', self.getAscii(n))
  295.         sleep(0.50)
  296.  
  297.     def setpool(self, pool):
  298.         self.pool = pool
  299.         user, pwd, host = pool
  300.         self.host = host
  301.         self.sayLine('Setting pool %s @ %s', (user, host))
  302.         self.headers = {"User-Agent": USER_AGENT, "Authorization": 'Basic ' + b64encode('%s:%s' % (user, pwd))}
  303.         self.connection = None
  304.  
  305.     def request(self, connection, url, headers, data=None):
  306.         result = response = None
  307.         try:
  308.             if data: connection.request('POST', url, data, headers)
  309.             else: connection.request('GET', url, headers=headers)
  310.             response = connection.getresponse()
  311.             if response.status == httplib.UNAUTHORIZED: raise NotAuthorized()
  312.             r = MAX_REDIRECTS
  313.             while response.status == httplib.TEMPORARY_REDIRECT:
  314.                 response.read()
  315.                 url = response.getheader('Location', '')
  316.                 if r == 0 or url == '': raise HTTPException('Too much or bad redirects')
  317.                 connection.request('GET', url, headers=headers)
  318.                 response = connection.getresponse();
  319.                 r -= 1
  320.             self.longPollURL = response.getheader('X-Long-Polling', '')
  321.             self.updateTime = response.getheader('X-Roll-NTime', '')
  322.             result = loads(response.read())
  323.             if result['error']: raise RPCError(result['error']['message'])
  324.             return (connection, result)
  325.         finally:
  326.             if not result or not response or (response.version == 10 and response.getheader('connection', '') != 'keep-alive') or response.getheader('connection', '') == 'close':
  327.                 connection.close()
  328.                 connection = None
  329.  
  330.     def longPollThread(self):
  331.         connection = None
  332.         last_url = None
  333.         while True:
  334.             if self.stop: return
  335.             sleep(1)
  336.             url = self.longPollURL
  337.             if url != '':
  338.                 host = self.host
  339.                 parsedUrl = urlsplit(url)
  340.                 if parsedUrl.netloc != '':
  341.                     host = parsedUrl.netloc
  342.                     url = url[url.find(host)+len(host):]
  343.                     if url == '': url = '/'
  344.                 try:
  345.                     if self.longPollURL != last_url:
  346.                         self.sayLine("Using new LP URL %s", url)
  347.                         connection = None
  348.                     if not connection:
  349.                         self.sayLine("LP connected to %s", host)
  350.                         connection = httplib.HTTPConnection(host, timeout=LONG_POLL_TIMEOUT)
  351.                     self.longPollActive = True
  352.                     (connection, result) = self.request(connection, url, self.headers)
  353.                     self.longPollActive = False
  354.                     self.queueWork(result['result'])
  355.                     self.sayLine('long poll: new block %s%s', (result['result']['data'][56:64], result['result']['data'][48:56]))
  356.                     last_url = self.longPollURL
  357.                 except NotAuthorized:
  358.                     self.sayLine('long poll: Wrong username or password')
  359.                 except RPCError as e:
  360.                     self.sayLine('long poll: %s', e)
  361.                 except (IOError, httplib.HTTPException, ValueError):
  362.                     self.sayLine('long poll exception:')
  363.                     traceback.print_exc()
  364.  
  365.     def miningThread(self):
  366.         self.loadKernel()
  367.         frame = 1.0 / self.frames
  368.         unit = self.worksize * 256
  369.         globalThreads = unit * 10
  370.        
  371.         queue = cl.CommandQueue(self.context)
  372.  
  373.         lastRatedPace = lastRated = lastNTime = time()
  374.         base = lastHashRate = threadsRunPace = threadsRun = 0
  375.         f = np.zeros(8, np.uint32)
  376.         output = np.zeros(OUTPUT_SIZE+1, np.uint32)
  377.         output_buf = cl.Buffer(self.context, cl.mem_flags.WRITE_ONLY | cl.mem_flags.USE_HOST_PTR, hostbuf=output)
  378.  
  379.         work = None
  380.         while True:
  381.                 sleep(self.frameSleep)
  382.             if self.stop: return
  383.             if (not work) or (not self.workQueue.empty()):
  384.                 try:
  385.                     work = self.workQueue.get(True, 1)
  386.                 except Empty: continue
  387.                 else:
  388.                     if not work: continue
  389.  
  390.                     noncesLeft = self.hashspace
  391.                     data   = np.array(unpack('IIIIIIIIIIIIIIII', work['data'][128:].decode('hex')), dtype=np.uint32)
  392.                     state  = np.array(unpack('IIIIIIII',         work['midstate'].decode('hex')),   dtype=np.uint32)
  393.                     target = np.array(unpack('IIIIIIII',         work['target'].decode('hex')),     dtype=np.uint32)
  394.                     state2 = partial(state, data, f)
  395.  
  396.             self.miner.search(  queue, (globalThreads, ), (self.worksize, ),
  397.                                 state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
  398.                                 state2[1], state2[2], state2[3], state2[5], state2[6], state2[7],
  399.                                 pack('I', base),
  400.                                 f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7],
  401.                                 output_buf)
  402.             cl.enqueue_read_buffer(queue, output_buf, output)
  403.  
  404.             noncesLeft -= globalThreads
  405.             threadsRunPace += globalThreads
  406.             threadsRun += globalThreads
  407.             base = uint32(base + globalThreads)
  408.  
  409.             now = time()
  410.             t = now - lastRatedPace
  411.             if (t > 1):
  412.                 rate = (threadsRunPace / t) / self.rateDivisor
  413.                 lastRatedPace = now; threadsRunPace = 0
  414.                 r = lastHashRate / rate
  415.                 if r < 0.9 or r > 1.1:
  416.                     globalThreads = max(unit * int((rate * frame * self.rateDivisor) / unit), unit)
  417.                     lastHashRate = rate
  418.             t = now - lastRated
  419.             if (t > self.rate):
  420.                 self.hashrate(int((threadsRun / t) / self.rateDivisor))
  421.                 lastRated = now; threadsRun = 0
  422.  
  423.             queue.finish()
  424.  
  425.             if output[OUTPUT_SIZE]:
  426.                 result = {}
  427.                 result['work'] = work
  428.                 result['data'] = np.array(data)
  429.                 result['state'] = np.array(state)
  430.                 result['target'] = target
  431.                 result['output'] = np.array(output)
  432.                 self.resultQueue.put(result)
  433.                 output.fill(0)
  434.                 cl.enqueue_write_buffer(queue, output_buf, output)
  435.  
  436.             if self.updateTime == '':
  437.                 if noncesLeft < (TIMEOUT+1) * self.currentrate * 1000:
  438.                     self.update = True
  439.                     noncesLeft += 0xFFFFFFFFFFFF
  440.                 elif 0xFFFFFFFFFFF < noncesLeft < 0xFFFFFFFFFFFF:
  441.                     self.sayLine('warning: job finished, miner is idle')
  442.                     work = None
  443.             elif now - lastNTime > 1:
  444.                 data[1] = bytereverse(bytereverse(data[1]) + 1)
  445.                 state2 = partial(state, data, f)
  446.                 lastNTime = now
  447.  
  448.     def loadKernel(self):
  449.         self.context = cl.Context([self.device], None, None)
  450.         if (self.device.extensions.find('cl_amd_media_ops') != -1):
  451.             self.defines += ' -DBITALIGN'
  452.  
  453.         kernelFile = open('BitcoinMiner.cl', 'r')
  454.         kernel = kernelFile.read()
  455.         kernelFile.close()
  456.         m = md5(); m.update(''.join([self.device.platform.name, self.device.platform.version, self.device.name, self.defines, kernel]))
  457.         cacheName = '%s.elf' % m.hexdigest()
  458.         binary = None
  459.         try:
  460.             binary = open(cacheName, 'rb')
  461.             self.miner = cl.Program(self.context, [self.device], [binary.read()]).build(self.defines)
  462.         except (IOError, cl.LogicError):
  463.             self.miner = cl.Program(self.context, kernel).build(self.defines)
  464.             if (self.defines.find('-DBITALIGN') != -1):
  465.                 patchedBinary = patch(self.miner.binaries[0])
  466.                 self.miner = cl.Program(self.context, [self.device], [patchedBinary]).build(self.defines)
  467.             binaryW = open(cacheName, 'wb')
  468.             binaryW.write(self.miner.binaries[0])
  469.             binaryW.close()
  470.         finally:
  471.             if binary: binary.close()
  472.  
  473.         if (self.worksize == -1):
  474.             self.worksize = self.miner.search.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, self.device)
Advertisement
Add Comment
Please, Sign In to add comment