forked from YoRyan/nuxhash
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nuxhashd.py
executable file
·259 lines (221 loc) · 10.2 KB
/
nuxhashd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#!/usr/bin/env python2
from miners.miner import MinerNotRunning
import devices.nvidia
import download.downloads
import miners.excavator
import nicehash
import settings
import utils
from collections import defaultdict
from pathlib2 import Path
from ssl import SSLError
from threading import Event
from time import sleep
from urllib2 import HTTPError, URLError
import argparse
import logging
import os
import readline
import signal
import socket
import sys
BENCHMARK_SECS = 90
def main():
# parse commmand-line arguments
argp = argparse.ArgumentParser(description='Sell GPU hash power on the NiceHash market.')
argp_benchmark = argp.add_mutually_exclusive_group()
argp_benchmark.add_argument('--benchmark-all', action='store_true',
help='benchmark all algorithms on all devices')
argp_benchmark.add_argument('--benchmark-missing', action='store_true',
help='benchmark algorithm-device combinations not measured')
argp.add_argument('--list-devices', action='store_true',
help='list all devices')
argp.add_argument('-v', '--verbose', action='store_true',
help='print more information to the console log')
argp.add_argument('--show-mining', action='store_true',
help='print output from mining processes, implies --verbose')
argp.add_argument('-c', '--configdir', nargs=1, default=[settings.DEFAULT_CONFIGDIR],
help='directory for configuration and benchmark files (default: ~/.config/nuxhash/)')
args = argp.parse_args()
config_dir = Path(args.configdir[0])
# initiate logging
if args.benchmark_all:
log_level = logging.ERROR
elif args.show_mining:
log_level = logging.DEBUG
elif args.verbose:
log_level = logging.INFO
else:
log_level = logging.WARN
logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=log_level)
# probe graphics cards
nvidia_devices = devices.nvidia.enumerate_devices()
all_devices = nvidia_devices
# load from config directory
nx_settings, nx_benchmarks = settings.load_persistent_data(config_dir, all_devices)
# if no wallet configured, do initial setup prompts
if nx_settings['nicehash']['wallet'] == '':
wallet, workername, region = initial_setup()
nx_settings['nicehash']['wallet'] = wallet
nx_settings['nicehash']['workername'] = workername
nx_settings['nicehash']['region'] = region
# download and initialize miners
downloadables = download.downloads.make_miners(config_dir)
for d in downloadables:
if not d.verify():
logging.info('Downloading %s' % d.name)
d.download()
nx_miners = [miners.excavator.Excavator(config_dir, nx_settings)]
if args.benchmark_all:
nx_benchmarks = run_missing_benchmarks(nx_miners, nx_settings, all_devices,
defaultdict(lambda: {}))
elif args.benchmark_missing:
nx_benchmarks = run_missing_benchmarks(nx_miners, nx_settings, all_devices,
nx_benchmarks)
elif args.list_devices:
list_devices(all_devices)
else:
do_mining(nx_miners, nx_settings, nx_benchmarks, all_devices)
# save to config directory
settings.save_persistent_data(config_dir, nx_settings, nx_benchmarks)
def initial_setup():
print 'nuxhashd initial setup'
wallet = raw_input('Wallet address: ')
workername = raw_input('Worker name: ')
region = raw_input('Region (eu/usa/hk/jp/in/br): ')
print
return wallet, workername, region
def run_missing_benchmarks(miners, settings, devices, old_benchmarks):
stratums = nicehash.simplemultialgo_info(settings)[1]
algorithms = sum([miner.algorithms for miner in miners], [])
algorithm = lambda name: next((a for a in algorithms if a.name == name), None)
for miner in miners:
miner.stratums = stratums
miner.load()
done = sum([[(device, algorithm(algorithm_name)) for algorithm_name in benchmarks.keys()]
for device, benchmarks in old_benchmarks.iteritems()], [])
all_targets = sum([[(device, algorithm) for algorithm in algorithms]
for device in devices], [])
benchmarks = run_benchmarks(set(all_targets) - set(done))
for miner in miners:
miner.unload()
for d in benchmarks:
old_benchmarks[d].update(benchmarks[d])
return old_benchmarks
def run_benchmarks(targets):
if len(targets) == 0:
print 'Nothing to benchmark.'
return []
benchmarks = defaultdict(lambda: {})
last_device = None
for device, algorithm in sorted(targets, key=lambda t: str(t[0])):
if device != last_device:
if isinstance(device, devices.nvidia.NvidiaDevice):
print '\nCUDA device: %s (%s)' % (device.name, device.uuid)
last_device = device
try:
benchmarks[device][algorithm.name] = run_benchmark(device, algorithm)
except MinerNotRunning:
print ' %s: failed to complete benchmark ' % algorithm.name
benchmarks[device][algorithm.name] = [0]*len(algorithm.algorithms)
except KeyboardInterrupt:
print 'Benchmarking aborted (completed benchmarks saved).'
break
return benchmarks
def run_benchmark(device, algorithm):
status_dot = [-1]
def report_speeds(sample, secs_remaining):
status_dot[0] = (status_dot[0] + 1) % 3
status_line = ''.join(['.' if i == status_dot[0] else ' ' for i in range(3)])
if secs_remaining < 0:
print (' %s %s %s (warming up, %s)\r' %
(algorithm.name, status_line, utils.format_speeds(sample),
utils.format_time(-secs_remaining))),
else:
print (' %s %s %s (sampling, %s) \r' %
(algorithm.name, status_line, utils.format_speeds(sample),
utils.format_time(secs_remaining))),
sys.stdout.flush()
speeds = utils.run_benchmark(algorithm, device,
algorithm.warmup_secs, BENCHMARK_SECS,
sample_callback=report_speeds)
print ' %s: %s ' % (algorithm.name,
utils.format_speeds(speeds))
return speeds
def list_devices(nx_devices):
for d in sorted(nx_devices, key=str):
if isinstance(d, devices.nvidia.NvidiaDevice):
print 'CUDA device: %s (%s)' % (d.name, d.uuid)
def do_mining(nx_miners, nx_settings, nx_benchmarks, nx_devices):
# get algorithm -> port information for stratum URLs
logging.info('Querying NiceHash for miner connection information...')
mbtc_per_hash = stratums = None
while mbtc_per_hash is None:
try:
mbtc_per_hash, stratums = nicehash.simplemultialgo_info(nx_settings)
except (HTTPError, URLError, socket.error, socket.timeout):
pass
# initialize miners
for miner in nx_miners:
miner.stratums = stratums
algorithms = sum([miner.algorithms for miner in nx_miners], [])
# quit signal
quit = Event()
signal.signal(signal.SIGINT, lambda signum, frame: quit.set())
current_algorithm = {d: None for d in nx_devices}
while not quit.is_set():
# calculate most profitable algorithm for each device
for device in nx_devices:
def mbtc_per_day(algorithm):
device_benchmarks = nx_benchmarks[device]
if algorithm.name in device_benchmarks:
mbtc_per_day_multi = [device_benchmarks[algorithm.name][i]*
mbtc_per_hash[algorithm.algorithms[i]]*(24*60*60)
for i in range(len(algorithm.algorithms))]
return sum(mbtc_per_day_multi)
else:
return 0
current = current_algorithm[device]
maximum = max(algorithms, key=lambda a: mbtc_per_day(a))
if current is None:
logging.info('Assigning %s to %s (%.3f mBTC/day)' %
(device, maximum.name, mbtc_per_day(maximum)))
current_algorithm[device] = maximum
elif current != maximum:
current_revenue = mbtc_per_day(current)
maximum_revenue = mbtc_per_day(maximum)
min_factor = 1.0 + nx_settings['switching']['threshold']
if current_revenue != 0 and maximum_revenue/current_revenue >= min_factor:
logging.info('Switching %s from %s to %s (%.3f -> %.3f mBTC/day)' %
(device, current.name, maximum.name,
current_revenue, maximum_revenue))
current_algorithm[device] = maximum
# attach devices to respective algorithms atomically
for algorithm in algorithms:
my_devices = [d for d, a in current_algorithm.items() if a == algorithm]
algorithm.set_devices(my_devices)
# wait for specified interval
quit.wait(nx_settings['switching']['interval'])
# probe miner status
for algorithm in current_algorithm.values():
if not algorithm.parent.is_running():
logging.error('Detected %s crash, restarting miner' % algorithm.name)
algorithm.parent.reload()
# query nicehash profitability data again
try:
mbtc_per_hash = nicehash.simplemultialgo_info(nx_settings)[0]
except URLError as err:
logging.warning('Failed to retrieve NiceHash profitability stats: %s' %
err.reason)
except HTTPError as err:
logging.warning('Failed to retrieve NiceHash profitability stats: %s %s' %
(err.code, err.reason))
except (socket.timeout, SSLError):
logging.warning('Failed to retrieve NiceHash profitability stats: timed out')
except (ValueError, KeyError):
logging.warning('Failed to retrieve NiceHash profitability stats: bad response')
logging.info('Cleaning up')
for miner in nx_miners:
miner.unload()
if __name__ == '__main__':
main()