1
|
|
|
<?php |
|
|
|
|
2
|
|
|
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; |
3
|
|
|
|
4
|
|
|
class Worker extends Base { |
|
|
|
|
5
|
|
|
protected $table = 'pool_worker'; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* We allow changing the database for shared accounts across pools |
9
|
|
|
* Load the config on construct so we can assign the DB name |
10
|
|
|
* @param config array MPOS configuration |
11
|
|
|
* @return none |
|
|
|
|
12
|
|
|
**/ |
13
|
|
|
public function __construct($config) { |
14
|
|
|
$this->setConfig($config); |
15
|
|
|
$this->table = $this->config['db']['shared']['workers'] . '.' . $this->table; |
|
|
|
|
16
|
|
|
} |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Update worker list for a user |
20
|
|
|
* @param account_id int User ID |
21
|
|
|
* @param data array All workers and their settings |
22
|
|
|
* @return bool |
23
|
|
|
**/ |
24
|
|
|
public function updateWorkers($account_id, $data) { |
25
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
26
|
|
|
if (!is_array($data)) { |
27
|
|
|
$this->setErrorMessage('No workers to update'); |
28
|
|
|
return false; |
29
|
|
|
} |
30
|
|
|
$username = $this->user->getUserName($account_id); |
|
|
|
|
31
|
|
|
$iFailed = 0; |
32
|
|
|
foreach ($data as $key => $value) { |
33
|
|
|
if ('' === $value['username'] || '' === $value['password']) { |
34
|
|
|
$iFailed++; |
35
|
|
|
} else { |
36
|
|
|
// Check worker name first |
37
|
|
|
if (! preg_match("/^[0-9a-zA-Z_\-]*$/", $value['username'])) { |
38
|
|
|
$iFailed++; |
39
|
|
|
continue; |
40
|
|
|
} |
41
|
|
|
// Prefix the WebUser to Worker name |
42
|
|
|
$value['username'] = "$username." . $value['username']; |
43
|
|
|
$stmt = $this->mysqli->prepare("UPDATE $this->table SET password = ?, username = ?, monitor = ? WHERE account_id = ? AND id = ? LIMIT 1"); |
44
|
|
|
if ( ! ( $this->checkStmt($stmt) && $stmt->bind_param('ssiii', $value['password'], $value['username'], $value['monitor'], $account_id, $key) && $stmt->execute()) ) |
45
|
|
|
$iFailed++; |
46
|
|
|
} |
47
|
|
|
} |
48
|
|
|
if ($iFailed == 0) |
49
|
|
|
return true; |
50
|
|
|
return $this->sqlError('E0053', $iFailed); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Fetch all IDLE workers that have monitoring enabled |
55
|
|
|
* @param none |
56
|
|
|
* @return data array Workers in IDLE state and monitoring enabled |
57
|
|
|
**/ |
58
|
|
View Code Duplication |
public function getAllIdleWorkers($interval=600) { |
|
|
|
|
59
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
60
|
|
|
$stmt = $this->mysqli->prepare(" |
61
|
|
|
SELECT w.account_id AS account_id, w.id AS id, w.username AS username |
62
|
|
|
FROM |
63
|
|
|
( |
64
|
|
|
SELECT username AS s_username, MAX(shares_id) AS shares_id, MAX(shares_archive_id) AS shares_archive_id |
65
|
|
|
FROM |
66
|
|
|
( |
67
|
|
|
SELECT |
68
|
|
|
s.username AS username, MAX(s.id) AS shares_id, NULL AS shares_archive_id |
69
|
|
|
FROM . " . $this->share->getTableName() . " AS s |
|
|
|
|
70
|
|
|
WHERE s.time > DATE_SUB(now(), INTERVAL ? SECOND) |
71
|
|
|
AND s.our_result = 'Y' |
72
|
|
|
GROUP BY s.username |
73
|
|
|
UNION |
74
|
|
|
SELECT |
75
|
|
|
sa.username AS username, NULL AS shares_id, MAX(sa.id) AS shares_archive_id |
76
|
|
|
FROM " . $this->share->getArchiveTableName() . " AS sa |
77
|
|
|
WHERE sa.time > DATE_SUB(now(), INTERVAL ? SECOND) |
78
|
|
|
AND sa.our_result = 'Y' |
79
|
|
|
GROUP BY sa.username |
80
|
|
|
) AS derived0 |
81
|
|
|
GROUP BY s_username |
82
|
|
|
) AS derived1 |
83
|
|
|
RIGHT JOIN " . $this->getTableName() . " AS w |
84
|
|
|
ON s_username = w.username |
85
|
|
|
WHERE w.monitor = 1 |
86
|
|
|
AND shares_id IS NULL |
87
|
|
|
AND shares_archive_id IS NULL |
88
|
|
|
"); |
89
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $interval, $interval) && $stmt->execute() && $result = $stmt->get_result()) |
90
|
|
|
return $result->fetch_all(MYSQLI_ASSOC); |
91
|
|
|
return $this->sqlError('E0054'); |
|
|
|
|
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Fetch a specific worker and its status |
96
|
|
|
* @param id int Worker ID |
97
|
|
|
* @return mixed array Worker details |
98
|
|
|
**/ |
99
|
|
|
public function getWorker($id, $interval=600) { |
100
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
101
|
|
|
$stmt = $this->mysqli->prepare(" |
102
|
|
|
SELECT id, username, password, monitor, |
103
|
|
|
( |
104
|
|
|
SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE our_result = 'Y' AND username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
105
|
|
|
) + ( |
106
|
|
|
SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE our_result = 'Y' AND username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
107
|
|
|
) AS count_all, |
108
|
|
|
( |
109
|
|
|
SELECT |
110
|
|
|
IFNULL(SUM(difficulty), 0) |
111
|
|
|
FROM " . $this->share->getTableName() . " |
112
|
|
|
WHERE |
113
|
|
|
username = w.username |
114
|
|
|
AND our_result = 'Y' |
115
|
|
|
AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
116
|
|
|
) + ( |
117
|
|
|
SELECT |
118
|
|
|
IFNULL(SUM(difficulty), 0) |
119
|
|
|
FROM " . $this->share->getArchiveTableName() . " |
120
|
|
|
WHERE |
121
|
|
|
username = w.username |
122
|
|
|
AND our_result = 'Y' |
123
|
|
|
AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
124
|
|
|
) AS shares |
125
|
|
|
FROM $this->table AS w |
126
|
|
|
WHERE id = ?"); |
127
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('iiiii', $interval, $interval, $interval, $interval, $id) && $stmt->execute() && ($result = $stmt->get_result()) && ($row = $result->fetch_assoc())) { |
128
|
|
|
$row['hashrate'] = round($this->coin->calcHashrate($row['shares'], $interval), 2); |
|
|
|
|
129
|
|
|
if ($row['count_all'] > 0) { |
130
|
|
|
$row['difficulty'] = round($row['shares'] / $row['count_all'], 2); |
131
|
|
|
} else { |
132
|
|
|
$row['difficulty'] = 0.00; |
133
|
|
|
} |
134
|
|
|
return $row; |
135
|
|
|
} |
136
|
|
|
return $this->sqlError('E0055'); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Fetch all workers for an account |
141
|
|
|
* @param account_id int User ID |
142
|
|
|
* @return mixed array Workers and their settings or false |
143
|
|
|
**/ |
144
|
|
|
public function getWorkers($account_id, $interval=600) { |
145
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
146
|
|
|
$stmt = $this->mysqli->prepare(" |
147
|
|
|
SELECT id, username, password, monitor, |
148
|
|
|
( |
149
|
|
|
SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE our_result = 'Y' AND username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
150
|
|
|
) + ( |
151
|
|
|
SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE our_result = 'Y' AND username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
152
|
|
|
) AS count_all, |
153
|
|
|
( |
154
|
|
|
SELECT |
155
|
|
|
IFNULL(SUM(difficulty), 0) |
156
|
|
|
FROM " . $this->share->getTableName() . " |
157
|
|
|
WHERE |
158
|
|
|
username = w.username |
159
|
|
|
AND our_result = 'Y' |
160
|
|
|
AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
161
|
|
|
) + ( |
162
|
|
|
SELECT |
163
|
|
|
IFNULL(SUM(difficulty), 0) |
164
|
|
|
FROM " . $this->share->getArchiveTableName() . " |
165
|
|
|
WHERE |
166
|
|
|
username = w.username |
167
|
|
|
AND our_result = 'Y' |
168
|
|
|
AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
169
|
|
|
) AS shares |
170
|
|
|
FROM $this->table AS w |
171
|
|
|
WHERE account_id = ?"); |
172
|
|
View Code Duplication |
if ($this->checkStmt($stmt) && $stmt->bind_param('iiiii', $interval, $interval, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result()) { |
|
|
|
|
173
|
|
|
$aData = array(); |
174
|
|
|
while ($row = $result->fetch_assoc()) { |
175
|
|
|
$row['hashrate'] = round($this->coin->calcHashrate($row['shares'], $interval), 2); |
176
|
|
|
if ($row['count_all'] > 0) { |
177
|
|
|
$row['difficulty'] = round($row['shares'] / $row['count_all'], 2); |
178
|
|
|
} else { |
179
|
|
|
$row['difficulty'] = 0.00; |
180
|
|
|
} |
181
|
|
|
$aData[] = $row; |
182
|
|
|
} |
183
|
|
|
return $aData; |
184
|
|
|
} |
185
|
|
|
return $this->sqlError('E0056'); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Fetch all workers for admin panel |
190
|
|
|
* @param limit int max amount of workers |
191
|
|
|
* @return mixed array Workers and their settings or false |
192
|
|
|
**/ |
193
|
|
|
public function getAllWorkers($iLimit=0, $interval=600, $start=0) { |
194
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
195
|
|
|
$stmt = $this->mysqli->prepare(" |
196
|
|
|
SELECT id, username, password, monitor, |
197
|
|
|
( |
198
|
|
|
SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE our_result = 'Y' AND username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
199
|
|
|
) + ( |
200
|
|
|
SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE our_result = 'Y' AND username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
201
|
|
|
) AS count_all, |
202
|
|
|
IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, |
203
|
|
|
( |
204
|
|
|
SELECT |
205
|
|
|
IFNULL(SUM(difficulty), 0) |
206
|
|
|
FROM " . $this->share->getTableName() . " |
207
|
|
|
WHERE |
208
|
|
|
username = w.username |
209
|
|
|
AND our_result = 'Y' |
210
|
|
|
AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
211
|
|
|
) + ( |
212
|
|
|
SELECT |
213
|
|
|
IFNULL(SUM(difficulty), 0) |
214
|
|
|
FROM " . $this->share->getArchiveTableName() . " |
215
|
|
|
WHERE |
216
|
|
|
username = w.username |
217
|
|
|
AND our_result = 'Y' |
218
|
|
|
AND time > DATE_SUB(now(), INTERVAL ? SECOND) |
219
|
|
|
) AS shares |
220
|
|
|
FROM $this->table AS w |
221
|
|
|
ORDER BY shares DESC LIMIT ?,?"); |
222
|
|
View Code Duplication |
if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiii', $interval, $interval, $interval, $interval, $start, $iLimit) && $stmt->execute() && $result = $stmt->get_result()) { |
|
|
|
|
223
|
|
|
$aData = array(); |
224
|
|
|
while ($row = $result->fetch_assoc()) { |
225
|
|
|
$row['hashrate'] = round($this->coin->calcHashrate($row['shares'], $interval), 2); |
226
|
|
|
if ($row['count_all'] > 0) { |
227
|
|
|
$row['avg_difficulty'] = round($row['shares'] / $row['count_all'], 2); |
228
|
|
|
} else { |
229
|
|
|
$row['avg_difficulty'] = 0.00; |
230
|
|
|
} |
231
|
|
|
$aData[] = $row; |
232
|
|
|
} |
233
|
|
|
return $aData; |
234
|
|
|
} |
235
|
|
|
return $this->sqlError('E0057'); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Get all currently active workers in the past 2 minutes |
240
|
|
|
* @param none |
241
|
|
|
* @return data mixed int count if any workers are active, false otherwise |
242
|
|
|
**/ |
243
|
|
View Code Duplication |
public function getCountAllActiveWorkers($interval=120) { |
|
|
|
|
244
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
245
|
|
|
if ($data = $this->memcache->get(__FUNCTION__)) return $data; |
|
|
|
|
246
|
|
|
$stmt = $this->mysqli->prepare(" |
247
|
|
|
SELECT COUNT(DISTINCT(username)) AS total |
248
|
|
|
FROM " . $this->share->getTableName() . " |
249
|
|
|
WHERE our_result = 'Y' |
250
|
|
|
AND time > DATE_SUB(now(), INTERVAL ? SECOND)"); |
251
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $interval) && $stmt->execute() && $result = $stmt->get_result()) |
252
|
|
|
return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->total); |
253
|
|
|
return $this->sqlError(); |
|
|
|
|
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Add new worker to an existing web account |
258
|
|
|
* The webuser name is prefixed to the worker name |
259
|
|
|
* Passwords are plain text for pushpoold |
260
|
|
|
* @param account_id int User ID |
261
|
|
|
* @param workerName string Worker name |
262
|
|
|
* @param workerPassword string Worker password |
263
|
|
|
* @return bool |
264
|
|
|
**/ |
265
|
|
|
public function addWorker($account_id, $workerName, $workerPassword) { |
266
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
267
|
|
|
if ('' === $workerName || '' === $workerPassword) { |
268
|
|
|
$this->setErrorMessage($this->getErrorMsg('E0058')); |
269
|
|
|
return false; |
270
|
|
|
} |
271
|
|
|
if (!preg_match("/^[0-9a-zA-Z_\-]*$/", $workerName)) { |
272
|
|
|
$this->setErrorMessage($this->getErrorMsg('E0072')); |
273
|
|
|
return false; |
274
|
|
|
} |
275
|
|
|
$username = $this->user->getUserName($account_id); |
276
|
|
|
$workerName = "$username.$workerName"; |
277
|
|
|
if (strlen($workerName) > 50) { |
278
|
|
|
$this->setErrorMessage($this->getErrorMsg('E0073')); |
279
|
|
|
return false; |
280
|
|
|
} |
281
|
|
|
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, username, password) VALUES(?, ?, ?)"); |
282
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('iss', $account_id, $workerName, $workerPassword)) { |
283
|
|
|
if (!$stmt->execute()) { |
284
|
|
|
if ($stmt->sqlstate == '23000') return $this->sqlError('E0059'); |
285
|
|
|
} else { |
286
|
|
|
return true; |
287
|
|
|
} |
288
|
|
|
} |
289
|
|
|
return $this->sqlError('E0060'); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Delete existing worker from account |
294
|
|
|
* @param account_id int User ID |
295
|
|
|
* @param id int Worker ID |
296
|
|
|
* @return bool |
297
|
|
|
**/ |
298
|
|
View Code Duplication |
public function deleteWorker($account_id, $id) { |
|
|
|
|
299
|
|
|
$this->debug->append("STA " . __METHOD__, 4); |
300
|
|
|
$stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE account_id = ? AND id = ? LIMIT 1"); |
301
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $account_id, $id) && $stmt->execute() && $stmt->affected_rows == 1) |
302
|
|
|
return true; |
303
|
|
|
return $this->sqlError('E0061'); |
304
|
|
|
} |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
$worker = new Worker($config); |
308
|
|
|
$worker->setDebug($debug); |
309
|
|
|
$worker->setMysql($mysqli); |
310
|
|
|
$worker->setMemcache($memcache); |
311
|
|
|
$worker->setShare($share); |
312
|
|
|
$worker->setUser($user); |
313
|
|
|
$worker->setErrorCodes($aErrorCodes); |
314
|
|
|
$worker->setCoin($coin); |
315
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.