-
Notifications
You must be signed in to change notification settings - Fork 4
/
excavator_api.py
179 lines (133 loc) · 5.28 KB
/
excavator_api.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
from log import Log
from config import Config
import socket
import json
import re
import sys
import nicehash
import time
class ExcavatorAPIError(Exception):
pass
class ExcavatorAPI(object):
def __init__(self):
self.name = self.__class__.__name__.lower()
self.supported = self.get_supported_algorithms()
def do_command(self, method, params, silent=False):
BUF_SIZE = 1024
command = {
'id': 1,
'method': method,
'params': params
}
timeout = Config().get('miners.%s.timeout' % (self.name))
if not timeout:
timeout = 10
ip = Config().get('miners.%s.ip' % (self.name))
port = Config().get('miners.%s.port' % (self.name))
try:
s = socket.create_connection((ip, port), timeout)
except socket.error:
Log().add('error', "failed to connect to %s miner backend at: %s:%d, make sure it's running" % (self.name, ip, port))
raise ExcavatorAPIError({"error": "connection failed"})
s.sendall((json.dumps(command).replace('\n', '\\n') + '\n').encode())
response = ''
while True:
chunk = s.recv(BUF_SIZE).decode()
if '\n' in chunk:
response += chunk[:chunk.index('\n')]
break
else:
response += chunk
s.close()
response_data = json.loads(response)
if response_data['error'] is None:
return response_data
else:
if not silent:
Log().add('error', '%s: failed to execute method %s: %s' % (self.name, command, response_data['error']))
raise ExcavatorAPIError(response_data)
def list_algorithms(self):
resp = self.do_command('algorithm.list', [])
if resp:
return resp['algorithms']
return []
def start(self, device_id, algorithm, endpoints, username, password, worker_name):
auth = '%s.%s:%s' % (username, worker_name, password)
if "nicehash" in endpoints[0]:
l_stratum = lambda algo: '%s.%s:%s' % (algo, endpoints[0], nicehash.Nicehash().ports[algo])
if "_" in algorithm:
first, second = algorithm.split("_")
params = [algorithm, l_stratum(first), auth, l_stratum(second), auth]
else:
params = [algorithm, l_stratum(algorithm), auth]
else:
if len(endpoints) >1:
params = [algorithm, endpoints[0], auth, endpoints[1]]
else:
params = [algorithm, endpoints[0], auth]
algo_params = Config().get('miners.%s.algo_params.%s' % (self.name, algorithm))
if algo_params:
for key in algo_params.keys():
params.append("%s=%s" % (key, algo_params[key]))
try:
resp = self.do_command('algorithm.list', [])
for algorithm in resp['algorithms']:
if len(algorithm['pools']) >0:
for worker in algorithm['workers']:
if int(worker['device_id']) == device_id:
login = algorithm['pools'][0]['login']
user, password = login.split(':')
if user[-5:] == 'CALIB':
Log().add('warning', "%s: device %d is being used for calibration - not starting worker" % (self.name, device_id))
return False
resp = self.do_command('algorithm.add', params)
if resp["error"] != None:
Log().add('error', '%s: failed to add algorithm: %s' % (self.name, resp['error']))
return
if not "algorithm_id" in resp.keys():
Log().add('error', '%s: missing algorithm_id in response' % (self.name))
Log().add('error', resp)
return
algo_id = resp['algorithm_id']
resp = self.do_command('worker.add', [str(algo_id), str(device_id)])
if resp["error"] != None:
Log().add('error', '%s: failed to add worker: %s' % (self.name, resp['error']))
return
return algo_id
except ExcavatorAPIError as e:
Log().add('error', '%s: failed to start excavator algorithm on device %d' % (self.name, device_id))
return False
def stop(self, device_id, algo_id=None):
found = False
try:
for algorithm in self.list_algorithms():
remove = False
if len(algorithm['workers']) == 0:
remove = True
else:
if algo_id == None or int(algo_id) == int(algorithm['algorithm_id']):
for worker in algorithm['workers']:
if int(worker['device_id']) == device_id:
remove = True
found = True
if remove:
self.do_command('algorithm.remove', [str(algorithm['algorithm_id'])])
break
except ExcavatorAPIError as e:
Log().add('error', '%s: failed to stop on device %d' % (self.name, device_id))
return False
if not found:
Log().add('warning', "%s: request to stop on device %d which isn't mining" % (self.name, device_id))
return True
def supported_algorithms(self):
return self.supported
def update_supported_algorithms(self):
new_supported_algorithms = self.get_supported_algorithms()
for algorithm in new_supported_algorithms:
if algorithm not in self.supported:
Log().add('info', '%s now supports algorithm: %s' % (self.name, algorithm))
self.supported.append(algorithm)
for algorithm in self.supported:
if algorithm not in new_supported_algorithms:
Log().add('info', '%s no longer supports algorithm: %s' % (self.name, algorithm))
self.supported.remove(algorithm)