Skip to content

Commit

Permalink
further work on implementing multi miner support
Browse files Browse the repository at this point in the history
  • Loading branch information
m0mchil committed Sep 26, 2012
1 parent f1ce310 commit 48a9787
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 80 deletions.
54 changes: 33 additions & 21 deletions BitcoinMiner.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,49 @@


class BitcoinMiner():
def __init__(self, device, options):
def __init__(self, device_index, options):
self.output_size = 0x100
self.options = options

(self.defines, self.rate_divisor, self.hashspace) = if_else(self.options.vectors, ('-DVECTORS', 500, 0x7FFFFFFF), ('', 1000, 0xFFFFFFFF))
self.defines += (' -DOUTPUT_SIZE=' + str(self.output_size))
self.defines += (' -DOUTPUT_MASK=' + str(self.output_size - 1))

self.device = device
self.options.frames = max(self.options.frames, 3)
self.device_index = device_index
self.device = cl.get_platforms()[options.platform].get_devices()[device_index]
self.device_name = self.device.name.strip('\r\n \x00\t')
self.frames = 30

self.update_time_counter = 1
self.share_count = [0, 0]
self.work_queue = Queue()

self.update = True

self.worksize = self.frameSleep= self.rate = self.estimated_rate = 0
self.vectors = False

if ADL_PRESENT:
self.adapterIndex = self.get_adapter_info()[self.options.device].iAdapterIndex

def id(self):
return str(self.options.platform) + ':' + str(self.device_index) + ':' + self.device_name

def start(self):
self.should_stop = False
Thread(target=self.mining_thread).start()
say_line('started OpenCL miner on platform %d, device %d (%s)', (self.options.platform, self.device_index, self.device_name))

def stop(self, message = None):
if message: print '\n%s' % message
self.should_stop = True

def mining_thread(self):
(self.defines, rate_divisor, hashspace) = if_else(self.vectors, ('-DVECTORS', 500, 0x7FFFFFFF), ('', 1000, 0xFFFFFFFF))
self.defines += (' -DOUTPUT_SIZE=' + str(self.output_size))
self.defines += (' -DOUTPUT_MASK=' + str(self.output_size - 1))

self.load_kernel()
frame = 1.0 / self.options.frames
unit = self.options.worksize * 256
frame = 1.0 / max(self.frames, 3)
unit = self.worksize * 256
global_threads = unit * 10

queue = cl.CommandQueue(self.context)

start_time = last_rated_pace = last_rated = last_n_time = last_temperature = time()
Expand All @@ -69,21 +78,23 @@ def mining_thread(self):
work = None
temperature = 0
while True:
sleep(self.options.frameSleep)
if self.should_stop: return

sleep(self.frameSleep)

if (not work) or (not self.work_queue.empty()):
try:
work = self.work_queue.get(True, 1)
except Empty: continue
else:
if not work: continue
nonces_left = self.hashspace
nonces_left = hashspace
state = work.state
state2 = work.state2
f = work.f

if temperature < self.options.cutoff_temp:
self.miner.search(queue, (global_threads,), (self.options.worksize,),
self.miner.search(queue, (global_threads,), (self.worksize,),
state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7],
state2[1], state2[2], state2[3], state2[5], state2[6], state2[7],
pack('I', base),
Expand All @@ -109,16 +120,17 @@ def mining_thread(self):

t = now - last_rated_pace
if t > 1:
rate = (threads_run_pace / t) / self.rate_divisor
rate = (threads_run_pace / t) / rate_divisor
last_rated_pace = now; threads_run_pace = 0
r = last_hash_rate / rate
if r < 0.9 or r > 1.1:
global_threads = max(unit * int((rate * frame * self.rate_divisor) / unit), unit)
global_threads = max(unit * int((rate * frame * rate_divisor) / unit), unit)
print self.id(), global_threads
last_hash_rate = rate

t = now - last_rated
if t > self.options.rate:
self.rate = int((threads_run / t) / self.rate_divisor)
self.rate = int((threads_run / t) / rate_divisor)
self.rate = Decimal(self.rate) / 1000
if accept_hist:
LAH = accept_hist.pop()
Expand Down Expand Up @@ -174,7 +186,7 @@ def load_kernel(self):
self.context = cl.Context([self.device], None, None)
if (self.device.extensions.find('cl_amd_media_ops') != -1):
self.defines += ' -DBITALIGN'
if self.device.name.strip('\r\n \x00\t') in ['Cedar',
if self.device_name in ['Cedar',
'Redwood',
'Juniper',
'Cypress',
Expand Down Expand Up @@ -210,13 +222,13 @@ def load_kernel(self):
finally:
if binary: binary.close()

if (self.options.worksize == -1):
self.options.worksize = self.miner.search.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, self.device)
if not self.worksize:
self.worksize = self.miner.search.get_work_group_info(cl.kernel_work_group_info.WORK_GROUP_SIZE, self.device)

def get_temperature(self):
def get_temperature(self):
temperature = ADLTemperature()
temperature.iSize = sizeof(temperature)

if ADL_Overdrive5_Temperature_Get(self.adapterIndex, 0, byref(temperature)) == ADL_OK:
return temperature.iTemperature/1000.0
return 0
Expand Down
21 changes: 9 additions & 12 deletions HttpTransport.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import httplib
import socket
import socks
import traceback
import urlparse


Expand Down Expand Up @@ -58,8 +57,7 @@ def loop(self):
self.servers.send(result, self.send_internal)
sleep(1)
except Exception:
say_line("Unexpected error:")
traceback.print_exc()
say_exception("Unexpected error:")
break

def ensure_connected(self, connection, proto, host):
Expand All @@ -69,12 +67,12 @@ def ensure_connected(self, connection, proto, host):
if proto == 'https': connector = httplib.HTTPSConnection
else: connector = httplib.HTTPConnection

if not self.config.proxy:
if not self.options.proxy:
return connector(host, strict=True), True

host, port = host.split(':')

proxy_proto, user, pwd, proxy_host, name = self.config.proxy
proxy_proto, user, pwd, proxy_host, name = self.options.proxy
proxy_port = 9050
proxy_host = proxy_host.split(':')
if len(proxy_host) > 1:
Expand All @@ -92,8 +90,8 @@ def ensure_connected(self, connection, proto, host):
connection.sock.setproxy(proxy_type, proxy_host, proxy_port, True, user, pwd)
try:
connection.sock.connect((host, int(port)))
except socks.Socks5AuthError as e:
say_line('Proxy error: %s', str(e))
except socks.Socks5AuthError:
say_exception('Proxy error:')
self.stop()
return connection, True

Expand All @@ -118,7 +116,7 @@ def request(self, connection, url, headers, data=None, timeout=0):
self.servers.update_time = bool(response.getheader('X-Roll-NTime', ''))
hostList = response.getheader('X-Host-List', '')
self.stratum_header = response.getheader('x-stratum', '')
if (not self.config.nsf) and hostList: self.servers.add_servers(loads(hostList))
if (not self.options.nsf) and hostList: self.servers.add_servers(loads(hostList))
result = loads(response.read())
if result['error']:
say_line('server error: %s', result['error']['message'])
Expand Down Expand Up @@ -192,13 +190,12 @@ def long_poll_thread(self):
if response:
(self.lp_connection, result) = response
self.queue_work(result['result'])
if self.config.verbose:
if self.options.verbose:
say_line('long poll: new block %s%s', (result['result']['data'][56:64], result['result']['data'][48:56]))
except Exception:
traceback.print_exc()
say_exception()
except (IOError, httplib.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError):
say_line('long poll: IO error')
#traceback.print_exc()
say_exception('long poll IO error')
self.close_lp_connection()
sleep(.5)

Expand Down
31 changes: 16 additions & 15 deletions Servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
import log

class Servers(object):
def __init__(self, config):
def __init__(self, options):
self.lock = RLock()
self.miners = []
self.config = config
self.options = options
self.last_work = 0
self.update_time = True
self.max_update_time = config.max_update_time
self.max_update_time = options.max_update_time

self.backup_server_index = 1
self.errors = 0
Expand All @@ -23,23 +23,24 @@ def __init__(self, config):
self.save_server = None
self.server_map = {}

self.user_agent = 'poclbm/' + config.version
self.user_agent = 'poclbm/' + options.version

self.difficulty = 0
self.true_target = None
self.last_block = ''

self.sent = {}

if self.config.proxy:
self.config.proxy = self.parse_server(self.config.proxy, False)
if self.options.proxy:
self.options.proxy = self.parse_server(self.options.proxy, False)

self.servers = []
for server in self.config.servers:
for server in self.options.servers:
try:
self.servers.append(self.parse_server(server))
except ValueError as e:
say_line(str(e))
except ValueError:
if self.options.verbose:
say_exception()
say_line("Ignored invalid server entry: %s", server)
continue

Expand Down Expand Up @@ -111,9 +112,9 @@ def loop(self):
continue

self.errors += 1
say_line('IO errors - %s, tolerance %s', (self.errors, self.config.tolerance))
say_line('IO errors - %s, tolerance %s', (self.errors, self.options.tolerance))

if self.errors > self.config.tolerance:
if self.errors > self.options.tolerance:
self.errors = 0
if self.backup_server_index >= len(self.servers):
say_line("No more backup pools left. Using primary and starting over.")
Expand Down Expand Up @@ -176,7 +177,7 @@ def send(self, result, send_callback):
if result.nonce[i]:
h = hash(result.state, result.merkle_end, result.time, result.difficulty, result.nonce[i])
if h[7] != 0:
say_line('Verification failed, check hardware!')
say_line('Verification failed, check hardware! (%s)', (result.miner.id()))
else:
self.diff1_found(bytereverse(h[6]), result.target[6])
if belowOrEquals(h[:7], result.target[:7]):
Expand All @@ -187,7 +188,7 @@ def send(self, result, send_callback):
return send_callback(result, result.nonce[i])

def diff1_found(self, hash, target):
if self.config.verbose and target < 0xFFFF0000L:
if self.options.verbose and target < 0xFFFF0000L:
say_line('checking %s <= %s', (hash, target))

def status_updated(self):
Expand All @@ -202,8 +203,8 @@ def report(self, miner, nonce, accepted):
is_block, hash6, hash5 = self.sent[nonce]
miner.share_count[if_else(accepted, 1, 0)] += 1
hash = if_else(is_block, hash6 + hash5, hash6)
if self.config.verbose or is_block:
say_line('%s%s, %s', (if_else(is_block, 'block ', ''), hash, if_else(accepted, 'accepted', '_rejected_')))
if self.options.verbose or is_block:
say_line('%s %s%s, %s', (miner.id(), if_else(is_block, 'block ', ''), hash, if_else(accepted, 'accepted', '_rejected_')))
del self.sent[nonce]

def set_server_by_index(self, server_index):
Expand Down
26 changes: 12 additions & 14 deletions StratumTransport.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import asyncore
import socket
import socks
import traceback
#import ssl


Expand Down Expand Up @@ -58,13 +57,13 @@ def loop(self):
#socket = ssl.wrap_socket(socket)
host = self.server[3]
address, port = host.split(':', 1)


if not self.config.proxy:

if not self.options.proxy:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((address, int(port)))
else:
proxy_proto, user, pwd, proxy_host, name = self.config.proxy
proxy_proto, user, pwd, proxy_host, name = self.options.proxy
proxy_port = 9050
proxy_host = proxy_host.split(':')
if len(proxy_host) > 1:
Expand All @@ -81,8 +80,8 @@ def loop(self):
self.socket.setproxy(proxy_type, proxy_host, proxy_port, True, user, pwd)
try:
self.socket.connect((address, int(port)))
except socks.Socks5AuthError as e:
say_line('Proxy error: %s', str(e))
except socks.Socks5AuthError:
say_exception('Proxy error:')
self.stop()

self.handler = Handler(self.socket, self.channel_map, self)
Expand All @@ -96,8 +95,8 @@ def loop(self):
elif not self.authorize():
self.stop()

except socket.error as e:
say_line(str(e))
except socket.error:
say_exception()
self.stop()
continue

Expand Down Expand Up @@ -206,9 +205,9 @@ def handle_message(self, message):
self.subscribed = True

#check if this is submit confirmation (message id should be in submits dictionary)
#cleanup if necessary
#cleanup if necessary
elif message['id'] in self.submits:
miner, nonce, time = self.submits[message['id']]
miner, nonce, t = self.submits[message['id']]
accepted = message['result']
self.servers.report(miner, nonce, accepted)
del self.submits[message['id']]
Expand Down Expand Up @@ -269,10 +268,9 @@ def send_message(self, message):
return True
except AttributeError:
self.stop()
except Exception as e:
say_line(str(e))
except Exception:
say_exception()
self.stop()


def queue_work(self, work, miner=None):
target = ''.join(list(chunks('%064x' % self.pool_difficulty, 2))[::-1])
Expand All @@ -292,7 +290,7 @@ def handle_close(self):

def handle_error(self):
type, value, trace = sys.exc_info()
say_line('%s', value)
say_exception()
self.parent.stop()

def collect_incoming_data(self, data):
Expand Down
4 changes: 2 additions & 2 deletions Transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ def __init__(self, servers, server):
self.server = server
self.proto, self.user, self.pwd, self.host, self.name = server[:5]
self.result_queue = Queue()
self.config = servers.config
self.options = servers.options

def loop(self):
self.should_stop = False
self.last_failback = time()

def check_failback(self):
if self.servers.server_index != 0 and time() - self.last_failback > self.config.failback:
if self.servers.server_index != 0 and time() - self.last_failback > self.options.failback:
self.stop()
return True
Loading

0 comments on commit 48a9787

Please sign in to comment.