Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
revoxhere
GitHub Repository: revoxhere/duino-coin
Path: blob/master/ESP_Code/MiningJob.h
925 views
1
#pragma GCC optimize("-Ofast")
2
3
#ifndef MINING_JOB_H
4
#define MINING_JOB_H
5
6
#include <Arduino.h>
7
#include <assert.h>
8
#include <string.h>
9
#include <Ticker.h>
10
#include <WiFiClient.h>
11
12
#include "DSHA1.h"
13
#include "Counter.h"
14
#include "Settings.h"
15
16
// https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TypeConversion.cpp
17
const char base36Chars[36] PROGMEM = {
18
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
19
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
20
};
21
22
const uint8_t base36CharValues[75] PROGMEM{
23
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 9
24
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, 0, 0, 0, 0, 0, 0, // Upper case letters
25
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 // Lower case letters
26
};
27
28
#define SPC_TOKEN ' '
29
#define END_TOKEN '\n'
30
#define SEP_TOKEN ','
31
#define IOT_TOKEN '@'
32
33
struct MiningConfig {
34
String host = "";
35
int port = 0;
36
String DUCO_USER = "";
37
String RIG_IDENTIFIER = "";
38
String MINER_KEY = "";
39
String MINER_VER = SOFTWARE_VERSION;
40
#if defined(ESP8266)
41
// "High-band" 8266 diff
42
String START_DIFF = "ESP8266H";
43
#elif defined(CONFIG_FREERTOS_UNICORE)
44
// Single core 32 diff
45
String START_DIFF = "ESP32S";
46
#else
47
// Normal 32 diff
48
String START_DIFF = "ESP32";
49
#endif
50
51
MiningConfig(String DUCO_USER, String RIG_IDENTIFIER, String MINER_KEY)
52
: DUCO_USER(DUCO_USER), RIG_IDENTIFIER(RIG_IDENTIFIER), MINER_KEY(MINER_KEY) {}
53
};
54
55
class MiningJob {
56
57
public:
58
MiningConfig *config;
59
int core = 0;
60
61
MiningJob(int core, MiningConfig *config) {
62
this->core = core;
63
this->config = config;
64
this->client_buffer = "";
65
dsha1 = new DSHA1();
66
dsha1->warmup();
67
generateRigIdentifier();
68
}
69
70
void blink(uint8_t count, uint8_t pin = LED_BUILTIN) {
71
#if defined(LED_BLINKING)
72
uint8_t state = HIGH;
73
74
for (int x = 0; x < (count << 1); ++x) {
75
digitalWrite(pin, state ^= HIGH);
76
delay(50);
77
}
78
#else
79
digitalWrite(LED_BUILTIN, HIGH);
80
#endif
81
}
82
83
bool max_micros_elapsed(unsigned long current, unsigned long max_elapsed) {
84
static unsigned long _start = 0;
85
86
if ((current - _start) > max_elapsed) {
87
_start = current;
88
return true;
89
}
90
return false;
91
}
92
93
void handleSystemEvents(void) {
94
#if defined(ESP32) && CORE == 2
95
esp_task_wdt_reset();
96
#endif
97
delay(10); // Required vTaskDelay by ESP-IDF
98
yield();
99
ArduinoOTA.handle();
100
}
101
102
void mine() {
103
connectToNode();
104
askForJob();
105
106
dsha1->reset().write((const unsigned char *)getLastBlockHash().c_str(), getLastBlockHash().length());
107
108
int start_time = micros();
109
max_micros_elapsed(start_time, 0);
110
#if defined(LED_BLINKING)
111
#if defined(BLUSHYBOX)
112
for (int i = 0; i < 72; i++) {
113
analogWrite(LED_BUILTIN, i);
114
delay(1);
115
}
116
#else
117
digitalWrite(LED_BUILTIN, LOW);
118
#endif
119
#endif
120
for (Counter<10> counter; counter < difficulty; ++counter) {
121
DSHA1 ctx = *dsha1;
122
ctx.write((const unsigned char *)counter.c_str(), counter.strlen()).finalize(hashArray);
123
124
#ifndef CONFIG_FREERTOS_UNICORE
125
#if defined(ESP32)
126
#define SYSTEM_TIMEOUT 100000 // 10ms for esp32 looks like the lowest value without false watchdog triggers
127
#else
128
#define SYSTEM_TIMEOUT 500000 // 50ms for 8266 for same reason as above
129
#endif
130
if (max_micros_elapsed(micros(), SYSTEM_TIMEOUT)) {
131
handleSystemEvents();
132
}
133
#endif
134
135
if (memcmp(getExpectedHash(), hashArray, 20) == 0) {
136
unsigned long elapsed_time = micros() - start_time;
137
float elapsed_time_s = elapsed_time * .000001f;
138
share_count++;
139
140
#if defined(LED_BLINKING)
141
#if defined(BLUSHYBOX)
142
for (int i = 72; i > 0; i--) {
143
analogWrite(LED_BUILTIN, i);
144
delay(1);
145
}
146
#else
147
digitalWrite(LED_BUILTIN, HIGH);
148
#endif
149
#endif
150
151
if (String(core) == "0") {
152
hashrate = counter / elapsed_time_s;
153
submit(counter, hashrate, elapsed_time_s);
154
} else {
155
hashrate_core_two = counter / elapsed_time_s;
156
submit(counter, hashrate_core_two, elapsed_time_s);
157
}
158
159
#if defined(BLUSHYBOX)
160
gauge_set(hashrate + hashrate_core_two);
161
#endif
162
163
break;
164
}
165
}
166
}
167
168
private:
169
String client_buffer;
170
uint8_t hashArray[20];
171
String last_block_hash;
172
String expected_hash_str;
173
uint8_t expected_hash[20];
174
DSHA1 *dsha1;
175
WiFiClient client;
176
String chipID = "";
177
178
#if defined(ESP8266)
179
#if defined(BLUSHYBOX)
180
String MINER_BANNER = "Official BlushyBox Miner (ESP8266)";
181
#else
182
String MINER_BANNER = "Official ESP8266 Miner";
183
#endif
184
#elif defined(CONFIG_FREERTOS_UNICORE)
185
String MINER_BANNER = "Official ESP32-S2 Miner";
186
#else
187
#if defined(BLUSHYBOX)
188
String MINER_BANNER = "Official BlushyBox Miner (ESP32)";
189
#else
190
String MINER_BANNER = "Official ESP32 Miner";
191
#endif
192
#endif
193
194
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength) {
195
assert(hexString.length() >= arrayLength * 2);
196
const char *hexChars = hexString.c_str();
197
for (uint32_t i = 0; i < arrayLength; ++i) {
198
uint8Array[i] = (pgm_read_byte(base36CharValues + hexChars[i * 2] - '0') << 4) + pgm_read_byte(base36CharValues + hexChars[i * 2 + 1] - '0');
199
}
200
return uint8Array;
201
}
202
203
void generateRigIdentifier() {
204
String AutoRigName = "";
205
206
#if defined(ESP8266)
207
chipID = String(ESP.getChipId(), HEX);
208
209
if (strcmp(config->RIG_IDENTIFIER.c_str(), "Auto") != 0)
210
return;
211
212
AutoRigName = "ESP8266-" + chipID;
213
AutoRigName.toUpperCase();
214
config->RIG_IDENTIFIER = AutoRigName.c_str();
215
#else
216
uint64_t chip_id = ESP.getEfuseMac();
217
uint16_t chip = (uint16_t)(chip_id >> 32); // Prepare to print a 64 bit value into a char array
218
char fullChip[23];
219
snprintf(fullChip, 23, "%04X%08X", chip,
220
(uint32_t)chip_id); // Store the (actually) 48 bit chip_id into a char array
221
222
chipID = String(fullChip);
223
224
if (strcmp(config->RIG_IDENTIFIER.c_str(), "Auto") != 0)
225
return;
226
// Autogenerate ID if required
227
AutoRigName = "ESP32-" + String(fullChip);
228
AutoRigName.toUpperCase();
229
config->RIG_IDENTIFIER = AutoRigName.c_str();
230
#endif
231
#if defined(SERIAL_PRINTING)
232
Serial.println("Core [" + String(core) + "] - Rig identifier: "
233
+ config->RIG_IDENTIFIER);
234
#endif
235
}
236
237
void connectToNode() {
238
if (client.connected()) return;
239
240
unsigned int stopWatch = millis();
241
#if defined(SERIAL_PRINTING)
242
Serial.println("Core [" + String(core) + "] - Connecting to a Duino-Coin node...");
243
#endif
244
while (!client.connect(config->host.c_str(), config->port)) {
245
if (max_micros_elapsed(micros(), 100000)) {
246
handleSystemEvents();
247
}
248
if (millis()-stopWatch>100000) ESP.restart();
249
}
250
251
waitForClientData();
252
#if defined(SERIAL_PRINTING)
253
Serial.println("Core [" + String(core) + "] - Connected. Node reported version: "
254
+ client_buffer);
255
#endif
256
257
blink(BLINK_CLIENT_CONNECT);
258
259
/* client.print("MOTD" + END_TOKEN);
260
waitForClientData();
261
#if defined(SERIAL_PRINTING)
262
Serial.println("Core [" + String(core) + "] - MOTD: "
263
+ client_buffer);
264
#endif */
265
}
266
267
void waitForClientData() {
268
client_buffer = "";
269
unsigned int stopWatch = millis();
270
while (client.connected()) {
271
if (client.available()) {
272
client_buffer = client.readStringUntil(END_TOKEN);
273
if (client_buffer.length() == 1 && client_buffer[0] == END_TOKEN)
274
client_buffer = "???\n"; // NOTE: Should never happen
275
break;
276
}
277
if (max_micros_elapsed(micros(), 100000)) {
278
handleSystemEvents();
279
}
280
if (millis()-stopWatch>120000) {
281
Serial.println("Timeout after 120s. Forced restart..");
282
ESP.restart();
283
}
284
}
285
}
286
287
void submit(unsigned long counter, float hashrate, float elapsed_time_s) {
288
client.print(String(counter) +
289
SEP_TOKEN + String(hashrate) +
290
SEP_TOKEN + MINER_BANNER +
291
SPC_TOKEN + config->MINER_VER +
292
SEP_TOKEN + config->RIG_IDENTIFIER +
293
SEP_TOKEN + "DUCOID" + String(chipID) +
294
SEP_TOKEN + String(WALLET_ID) +
295
END_TOKEN);
296
297
unsigned long ping_start = millis();
298
waitForClientData();
299
ping = millis() - ping_start;
300
301
if (client_buffer == "GOOD") {
302
accepted_share_count++;
303
}
304
305
#if defined(SERIAL_PRINTING)
306
Serial.println("Core [" + String(core) + "] - " +
307
client_buffer +
308
" share #" + String(share_count) +
309
" (" + String(counter) + ")" +
310
" hashrate: " + String(hashrate / 1000, 2) + " kH/s (" +
311
String(elapsed_time_s) + "s) " +
312
"Ping: " + String(ping) + "ms " +
313
"(" + node_id + ")\n");
314
#endif
315
}
316
317
bool parse() {
318
// Create a non-constant copy of the input string
319
char *job_str_copy = strdup(client_buffer.c_str());
320
321
if (job_str_copy) {
322
String tokens[3];
323
char *token = strtok(job_str_copy, ",");
324
for (int i = 0; token != NULL && i < 3; i++) {
325
tokens[i] = token;
326
token = strtok(NULL, ",");
327
}
328
329
last_block_hash = tokens[0];
330
expected_hash_str = tokens[1];
331
hexStringToUint8Array(expected_hash_str, expected_hash, 20);
332
difficulty = tokens[2].toInt() * 100 + 1;
333
334
// Free the memory allocated by strdup
335
free(job_str_copy);
336
337
return true;
338
}
339
else {
340
// Handle memory allocation failure
341
return false;
342
}
343
}
344
345
void askForJob() {
346
Serial.println("Core [" + String(core) + "] - Asking for a new job for user: "
347
+ String(config->DUCO_USER));
348
349
#if defined(USE_DS18B20)
350
sensors.requestTemperatures();
351
float temp = sensors.getTempCByIndex(0);
352
#if defined(SERIAL_PRINTING)
353
Serial.println("DS18B20 reading: " + String(temp) + "°C");
354
#endif
355
356
client.print("JOB," +
357
String(config->DUCO_USER) +
358
SEP_TOKEN + config->START_DIFF +
359
SEP_TOKEN + String(config->MINER_KEY) +
360
SEP_TOKEN + "Temp:" + String(temp) + "*C" +
361
END_TOKEN);
362
#elif defined(USE_DHT)
363
float temp = dht.readTemperature();
364
float hum = dht.readHumidity();
365
#if defined(SERIAL_PRINTING)
366
Serial.println("DHT reading: " + String(temp) + "°C");
367
Serial.println("DHT reading: " + String(hum) + "%");
368
#endif
369
370
client.print("JOB," +
371
String(config->DUCO_USER) +
372
SEP_TOKEN + config->START_DIFF +
373
SEP_TOKEN + String(config->MINER_KEY) +
374
SEP_TOKEN + "Temp:" + String(temp) + "*C" +
375
IOT_TOKEN + "Hum:" + String(hum) + "%" +
376
END_TOKEN);
377
#elif defined(USE_HSU07M)
378
float temp = read_hsu07m();
379
#if defined(SERIAL_PRINTING)
380
Serial.println("HSU reading: " + String(temp) + "°C");
381
#endif
382
383
client.print("JOB," +
384
String(config->DUCO_USER) +
385
SEP_TOKEN + config->START_DIFF +
386
SEP_TOKEN + String(config->MINER_KEY) +
387
SEP_TOKEN + "Temp:" + String(temp) + "*C" +
388
END_TOKEN);
389
#elif defined(USE_INTERNAL_SENSOR)
390
float temp = 0;
391
temp_sensor_read_celsius(&temp);
392
#if defined(SERIAL_PRINTING)
393
Serial.println("Internal temp sensor reading: " + String(temp) + "°C");
394
#endif
395
396
client.print("JOB," +
397
String(config->DUCO_USER) +
398
SEP_TOKEN + config->START_DIFF +
399
SEP_TOKEN + String(config->MINER_KEY) +
400
SEP_TOKEN + "CPU Temp:" + String(temp) + "*C" +
401
END_TOKEN);
402
#else
403
client.print("JOB," +
404
String(config->DUCO_USER) +
405
SEP_TOKEN + config->START_DIFF +
406
SEP_TOKEN + String(config->MINER_KEY) +
407
END_TOKEN);
408
#endif
409
410
waitForClientData();
411
#if defined(SERIAL_PRINTING)
412
Serial.println("Core [" + String(core) + "] - Received job with size of "
413
+ String(client_buffer.length())
414
+ " bytes " + client_buffer);
415
#endif
416
417
parse();
418
#if defined(SERIAL_PRINTING)
419
Serial.println("Core [" + String(core) + "] - Parsed job: "
420
+ getLastBlockHash() + " "
421
+ getExpectedHashStr() + " "
422
+ String(getDifficulty()));
423
#endif
424
}
425
426
const String &getLastBlockHash() const { return last_block_hash; }
427
const String &getExpectedHashStr() const { return expected_hash_str; }
428
const uint8_t *getExpectedHash() const { return expected_hash; }
429
unsigned int getDifficulty() const { return difficulty; }
430
};
431
432
#endif
433
434