From 8884bb58d70f6120b8843e5dccf8704c1d047e6f Mon Sep 17 00:00:00 2001 From: "Alfonso C. Alvarez" Date: Wed, 3 May 2023 21:09:37 +0200 Subject: [PATCH] Merge branch 'master' of https://github.com/alcar21/NerdMiner_v2 into feature/add_platformio_support_and_improvements --- .gitignore | 1 + platformio.ini | 8 +- src/NerdMinerV2.ino.cpp | 6 +- src/mining.cpp | 257 ++++++++++++++++++++++++---------------- src/mining.h | 11 +- 5 files changed, 167 insertions(+), 116 deletions(-) diff --git a/.gitignore b/.gitignore index 5ca6960..6396bf6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .pio .vscode NerdMinerLog.txt +platformio-device-monitor*.log diff --git a/platformio.ini b/platformio.ini index 0b0cec5..910b64c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,22 +18,20 @@ framework = arduino monitor_filters = esp32_exception_decoder time + log2file board_build.arduino.memory_type = qio_opi monitor_speed = 115200 upload_speed = 115200 -# 2 x 4.5MB app, 6.875MB SPIFFS -;board_build.partitions = large_spiffs_16MB.csv -;board_build.partitions = default_8MB.csv board_build.partitions = huge_app.csv -;board_build.partitions = default.csv build_flags = -D LV_LVGL_H_INCLUDE_SIMPLE -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 - ;-D DEBUG_MINING=1 + -D DEBUG_MINING=1 + ;-D DEBUG_MINING_SHARE lib_deps = https://github.com/takkaO/OpenFontRender bblanchon/ArduinoJson@^6.21.2 diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index f6c2750..b921a25 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -79,7 +79,6 @@ void setup() // Higher prio monitor task Serial.println(""); Serial.println("Initiating tasks..."); - xTaskCreate(runMonitor, "Monitor", 5000, NULL, 4, NULL); /******** CREATE MINER TASKS *****/ for (size_t i = 0; i < THREADS; i++) { @@ -88,8 +87,10 @@ void setup() // Start mining tasks BaseType_t res = xTaskCreate(runWorker, name, 30000, (void*)name, 1, NULL); - Serial.printf("Starting %s %s!\n", name, res == pdPASS? "successful":"failed"); + Serial.printf("Starting miner %s %s!\n", name, res == pdPASS? "successful":"failed"); } + + Serial.println("NerdMiner v2 started......"); } void app_error_fault_handler(void *arg) { @@ -123,4 +124,5 @@ void loop() { //Run miner on main core when there is time --Currently on test runMiner(); + runMonitor(); } \ No newline at end of file diff --git a/src/mining.cpp b/src/mining.cpp index 1fa30ac..581ec22 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -3,6 +3,7 @@ #include #include #include // Graphics and font library for ILI9341 driver chip +#include #include "media/Free_Fonts.h" #include "media/images.h" #include "mbedtls/md.h" @@ -11,6 +12,7 @@ #include "mining.h" #define TARGET_BUFFER_SIZE 64 +#define BUFFER_JSON_DOC 1024 unsigned long templates = 0; unsigned long hashes= 0; @@ -29,8 +31,6 @@ extern char btcString[80]; extern OpenFontRender render; extern TFT_eSprite background; - - bool checkValid(unsigned char* hash, unsigned char* target) { bool valid = true; for(uint8_t i=31; i>=0; i--) { @@ -42,7 +42,7 @@ bool checkValid(unsigned char* hash, unsigned char* target) { break; } } - #ifdef DEBUG_MINING + #ifdef DEBUG_MINING_SHARE if (valid) { Serial.print("\tvalid : "); for (size_t i = 0; i < 32; i++) @@ -87,21 +87,55 @@ bool verifyPayload (String line){ return true; } +unsigned long getNextId(unsigned long id) { + if (id == ULONG_MAX) { + id = 1; + return id; + } + return ++id; +} + +void getNextExtranonce2(int extranonce2_size, char *extranonce2) { + + unsigned long extranonce2_number = strtoul(extranonce2, NULL, 10); + extranonce2_number++; + + memset(extranonce2, '0', 2 * extranonce2_size); + if (extranonce2_number > long(pow(10, 2 * extranonce2_size))) { + return; + } + + char next_extranounce2[2 * extranonce2_size + 1]; + memset(extranonce2, '0', 2 * extranonce2_size); + ultoa(extranonce2_number, next_extranounce2, 10); + memcpy(extranonce2 + (2 * extranonce2_size) - long(log10(extranonce2_number)) - 1 , next_extranounce2, strlen(next_extranounce2)); + extranonce2[2 * extranonce2_size] = 0; +} + +bool checkError(const StaticJsonDocument doc) { + if (doc["error"].size() == 0) { + return false; + } + Serial.printf("ERROR: %d | reason: %s \n", (const int) doc["error"][0], (const char*) doc["error"][1]); + return true; +} + void runWorker(void *name) { // TEST: https://bitcoin.stackexchange.com/questions/22929/full-example-data-for-scrypt-stratum-client Serial.println(""); Serial.printf("\n[WORKER] Started. Running %s on core %d\n", (char *)name, xPortGetCoreID()); + + #ifdef DEBUG_MEMORY Serial.printf("### [Total Heap / Free heap]: %d / %d \n", ESP.getHeapSize(), ESP.getFreeHeap()); - - String ADDRESS = String(btcString); + #endif // connect to pool WiFiClient client; bool continueSecuence = false; - String line, extranonce1; - unsigned long id = 1; + String line, extranonce1, extranonce2 = String("0"); + unsigned long id = 0, extranonce_number = 0; unsigned int extranonce2_size; while(true) { @@ -109,54 +143,47 @@ void runWorker(void *name) { if(WiFi.status() != WL_CONNECTED) continue; // get template - DynamicJsonDocument doc(4 * 1024); - String payload; + StaticJsonDocument doc; + + char payload[BUFFER_JSON_DOC] = {0}; if (!client.connect(poolString, portNumber)) { continue; } - // STEP 1: Pool server connection - payload = "{\"id\": "+ String(id++) +", \"method\": \"mining.subscribe\", \"params\": [\"" + ADDRESS + "\", \"password\"]}\n"; + // STEP 1: Pool server connection (SUBSCRIBE) + // Docs: + // - https://cs.braiins.com/stratum-v1/docs + // - https://github.com/aeternity/protocol/blob/master/STRATUM.md#mining-subscribe + id = getNextId(id); + sprintf(payload, "{\"id\": %u, \"method\": \"mining.subscribe\", \"params\": [\"NerdMinerV2\"]}\n", id); Serial.printf("[WORKER] %s ==> Mining subscribe\n", (char *)name); Serial.print(" Sending : "); Serial.println(payload); - client.print(payload.c_str()); + client.print(payload); line = client.readStringUntil('\n'); if(!verifyPayload(line)) return; Serial.print(" Receiving: "); Serial.println(line); deserializeJson(doc, line); - int error = doc["error"]; + if (checkError(doc)) { + Serial.printf("[WORKER] %s >>>>>>>>> Work aborted\n", (char *)name); + continue; + } String sub_details = String((const char*) doc["result"][0][0][1]); extranonce1 = String((const char*) doc["result"][1]); int extranonce2_size = doc["result"][2]; // DIFFICULTY line = client.readStringUntil('\n'); + Serial.print(" Receiving: "); Serial.println(line); Serial.print(" sub_details: "); Serial.println(sub_details); Serial.print(" extranonce1: "); Serial.println(extranonce1); Serial.print(" extranonce2_size: "); Serial.println(extranonce2_size); - Serial.print(" error: "); Serial.println(error); - if((extranonce1.length() == 0) || line.length() == 0 || (error != 0)) { - Serial.printf("[WORKER] %s >>>>>>>>> Work aborted\n", (char *)name); - Serial.printf("extranonce1 length: %u | line2 length: %u | error code: %u \n", extranonce1.length(), line.length(), error); - client.stop(); - doc.clear(); - doc.garbageCollect(); - continue; - } - - // STEP 2: Pool authorize work - payload = "{\"params\": [\"" + ADDRESS + "\", \"password\"], \"id\": "+ String(id++) +", \"method\": \"mining.authorize\"}\n"; - Serial.printf("[WORKER] %s ==> Autorize work\n", (char *)name); - Serial.print(" Sending : "); Serial.println(payload); - client.print(payload.c_str()); - line = client.readStringUntil('\n'); - if(!verifyPayload(line)) return; - Serial.print(" Receiving: "); Serial.println(line); - Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); - Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); - client.stop(); + // Recibe Work + //line = "{\"params\": [\"b3ba\", \"7dcf1304b04e79024066cd9481aa464e2fe17966e19edf6f33970e1fe0b60277\", \"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270362f401062f503253482f049b8f175308\", \"0d2f7374726174756d506f6f6c2f000000000100868591052100001976a91431482118f1d7504daf1c001cbfaf91ad580d176d88ac00000000\", [\"57351e8569cb9d036187a79fd1844fd930c1309efcd16c46af9bb9713b6ee734\", \"936ab9c33420f187acae660fcdb07ffdffa081273674f0f41e6ecc1347451d23\"], \"00000002\", \"1b44dfdb\", \"53178f9b\", true], \"id\": null, \"method\": \"mining.notify\"}"; + line = client.readStringUntil('\n'); + doc.clear(); deserializeJson(doc, line); + Serial.print("doc size: "); Serial.println(doc.size()); String job_id = String((const char*) doc["params"][0]); String prevhash = String((const char*) doc["params"][1]); String coinb1 = String((const char*) doc["params"][2]); @@ -167,6 +194,28 @@ void runWorker(void *name) { String ntime = String((const char*) doc["params"][7]); bool clean_jobs = doc["params"][8]; //bool + if((extranonce1.length() == 0) || line.length() == 0) { + Serial.printf("[WORKER] %s >>>>>>>>> Work aborted\n", (char *)name); + Serial.printf("extranonce1 length: %u | line2 length: %u \n", extranonce1.length(), line.length()); + client.stop(); + doc.clear(); + doc.garbageCollect(); + continue; + } + + // STEP 2: Pool authorize work (Block Info) + id = getNextId(id); + sprintf(payload, "{\"params\": [\"%s\", \"x\"], \"id\": %u, \"method\": \"mining.authorize\"}\n", + btcString, + id); + Serial.printf("[WORKER] %s ==> Autorize work\n", (char *)name); + Serial.print(" Sending : "); Serial.println(payload); + client.print(payload); + line = client.readStringUntil('\n'); + if(!verifyPayload(line)) return; + Serial.print(" Receiving: "); Serial.println(line); + client.stop(); + #ifdef DEBUG_MINING Serial.print(" job_id: "); Serial.println(job_id); Serial.print(" prevhash: "); Serial.println(prevhash); @@ -183,7 +232,6 @@ void runWorker(void *name) { Serial.println(">>>>>>>>> Worker aborted"); client.stop(); doc.clear(); - doc.garbageCollect(); continue; } @@ -196,7 +244,7 @@ void runWorker(void *name) { memset(target, '0', TARGET_BUFFER_SIZE); int zeros = (int) strtol(nbits.substring(0, 2).c_str(), 0, 16) - 3; memcpy(target + zeros - 2, nbits.substring(2).c_str(), nbits.length() - 2); - target[TARGET_BUFFER_SIZE+1] = 0; + target[TARGET_BUFFER_SIZE] = 0; Serial.print(" target: "); Serial.println(target); // bytearray target uint8_t bytearray_target[32]; @@ -214,30 +262,15 @@ void runWorker(void *name) { } // get extranonce2 - extranonce2 = hex(random.randint(0,2**32-1))[2:].zfill(2*extranonce2_size) - uint32_t extranonce2_a_bin = esp_random(); - uint32_t extranonce2_b_bin = esp_random(); - String extranonce2_a = String(extranonce2_a_bin, HEX); - String extranonce2_b = String(extranonce2_b_bin, HEX); - uint8_t pad = 8 - extranonce2_a.length(); - char extranonce2_a_char[pad+1]; - for (int k = 0; k < pad; k++) { - extranonce2_a_char[k] = '0'; - } - extranonce2_a_char[pad+1] = 0; - extranonce2_a = String(extranonce2_a_char) + extranonce2_a; - - pad = 8 - extranonce2_b.length(); - char extranonce2_b_char[pad+1]; - for (int k = 0; k < pad; k++) { - extranonce2_b_char[k] = '0'; - } - - extranonce2_b_char[pad+1] = 0; - extranonce2_b = String(extranonce2_b_char) + extranonce2_b; - - String extranonce2 = String(extranonce2_a + extranonce2_b).substring(0, 17 - (2 * extranonce2_size)); + char extranonce2_char[2 * extranonce2_size+1]; + extranonce2.toCharArray(extranonce2_char, 2 * extranonce2_size + 1); + getNextExtranonce2(extranonce2_size, extranonce2_char); + //extranonce2 = String(extranonce2_char); + extranonce2 = "00000002"; + //get coinbase - coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest() String coinbase = coinb1 + extranonce1 + extranonce2 + coinb2; + Serial.print(" coinbase: "); Serial.println(coinbase); size_t str_len = coinbase.length()/2; uint8_t bytearray[str_len]; @@ -248,7 +281,7 @@ void runWorker(void *name) { Serial.print(" coinbase: "); Serial.println(coinbase); Serial.print(" coinbase bytes - size: "); Serial.println(res); for (size_t i = 0; i < res; i++) - Serial.printf("%02x ", bytearray[i]); + Serial.printf("%02x", bytearray[i]); Serial.println("---"); #endif @@ -279,7 +312,7 @@ void runWorker(void *name) { memcpy(merkle_result, shaResult, sizeof(shaResult)); byte merkle_concatenated[32 * 2]; - for (size_t k=0; k>> STARTING TO HASH NONCES"); @@ -444,7 +484,7 @@ void runWorker(void *name) { Serial.println(""); */ hashes++; - if (nonce++> MAX_NONCE) break; //exit + if (nonce++> TARGET_NONCE) break; //exit // check if 16bit share if(hash[31]!=0) continue; @@ -457,24 +497,30 @@ void runWorker(void *name) { // check if valid header if(checkValid(hash, bytearray_target)){ - //Serial.printf("%s on core %d: ", (char *)name, xPortGetCoreID()); - Serial.printf("[WORKER] %s CONGRATULATIONS! Valid completed with nonce: %d | 0x%x\n", (char *)name, nonce, nonce); - valids++; - Serial.printf("[WORKER] %s Submiting work valid!\n", (char *)name); - while (!client.connected()) { - client.connect(poolString, portNumber); - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - // STEP 3: Submit mining job - payload = "{\"params\": [\"" + ADDRESS + "\", \"" + job_id + "\", \"" + extranonce2 + "\", \"" + ntime + "\", \"" + String(nonce, HEX) + "\"], \"id\": "+ String(id++) +", \"method\": \"mining.submit\"}"; - Serial.print(" Sending : "); Serial.println(payload); - client.print(payload.c_str()); - line = client.readStringUntil('\n'); - Serial.print(" Receiving: "); Serial.println(line); - client.stop(); - // exit - nonce = MAX_NONCE; - break; + Serial.printf("[WORKER] %s CONGRATULATIONS! Valid completed with nonce: %d | 0x%x\n", (char *)name, nonce, nonce); + valids++; + Serial.printf("[WORKER] %s Submiting work valid!\n", (char *)name); + while (!client.connected()) { + client.connect(poolString, portNumber); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + // STEP 3: Submit mining job + id = getNextId(id); + sprintf(payload, "{\"params\": [\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"], \"id\": %u, \"method\": \"mining.submit\"}", + btcString, + job_id, + extranonce2, + ntime, + String(nonce, HEX), + id + ); + Serial.print(" Sending : "); Serial.println(payload); + client.print(payload); + Serial.print(" Receiving: "); Serial.println(client.readString()); + client.stop(); + // exit + nonce = MAX_NONCE; + break; } } // exit if found a valid result or nonce > MAX_NONCE @@ -482,22 +528,31 @@ void runWorker(void *name) { mbedtls_sha256_free(midstate); enableGlobalHash = false; + // TODO Pending doub if(hashes>=MAX_NONCE) { Mhashes=Mhashes+MAX_NONCE/1000000; hashes=hashes-MAX_NONCE;} - if (nonce == MAX_NONCE) { + if (nonce == TARGET_NONCE) { Serial.printf("[WORKER] %s SUBMITING WORK... MAX Nonce reached > MAX_NONCE\n", (char *)name); // STEP 3: Submit mining job + id = getNextId(id); if (client.connect(poolString, portNumber)) { - payload = "{\"params\": [\"" + ADDRESS + "\", \"" + job_id + "\", \"" + extranonce2 + "\", \"" + ntime + "\", \"" + String(nonce, HEX) + "\"], \"id\": "+ String(id++) +", \"method\": \"mining.submit\"}"; + sprintf(payload, "{\"params\": [\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"], \"id\": %u, \"method\": \"mining.submit\"}", + btcString, + job_id, + extranonce2, + ntime, + String(nonce, HEX), + id + ); Serial.print(" Sending : "); Serial.println(payload); - client.print(payload.c_str()); - Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); - while (client.available()) { - Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); - } + client.print(payload); + unsigned long timeout = millis(); + Serial.print(" Receiving: "); Serial.println(client.readString()); + Serial.printf("[WORKER] %s SUBMITED WORK\n", (char *)name); client.stop(); } } + uint32_t duration = micros() - startT; } @@ -505,11 +560,10 @@ void runWorker(void *name) { //////////////////THREAD CALLS/////////////////// - //Testeamos hashrate final usando hilo principal //this is currently on test -void runMiner(void){ +void runMiner(void) { uint32_t nonce=0; unsigned char bytearray_blockheader[80]; @@ -526,7 +580,7 @@ void runMiner(void){ //Iteraciones unsigned char *header64 = bytearray_blockheader + 64; - for(nonce = 0; nonce < 10000; nonce++){ + for(nonce = 0; nonce < 10000; nonce++) { memcpy(bytearray_blockheader + 77, &nonce, 3); mbedtls_sha256_clone(&ctx, midstate); //Clonamos el contexto anterior para continuar el SHA desde allĂ­ mbedtls_sha256_update_ret(&ctx, header64, 16); @@ -545,7 +599,7 @@ void runMiner(void){ } -void runMonitor(void *name){ +void runMonitor() { Serial.println("[MONITOR] started"); @@ -556,8 +610,9 @@ void runMonitor(void *name){ unsigned long mElapsed = millis()-mStart; unsigned long totalKHashes = (Mhashes*1000) + hashes/1000; //Serial.println("[runMonitor Task] -> Printing results on screen "); - Serial.printf(">>> Completed %d share(s), %d Khashes, avg. hashrate %.3f KH/s\n", - shares, totalKHashes, (1.0*(totalKHashes*1000))/mElapsed); + + //Serial.printf(">>> Completed %d share(s), %d Khashes, avg. hashrate %.3f KH/s\n", + // shares, totalKHashes, (1.0*(totalKHashes*1000))/mElapsed); //Hashrate render.setFontSize(70); @@ -599,6 +654,6 @@ void runMonitor(void *name){ background.pushSprite(0,0); // Pause the task for 5000ms - vTaskDelay(5000 / portTICK_PERIOD_MS); + // vTaskDelay(5000 / portTICK_PERIOD_MS); } } diff --git a/src/mining.h b/src/mining.h index eadc0b6..ffc5093 100644 --- a/src/mining.h +++ b/src/mining.h @@ -1,14 +1,9 @@ // Mining #define THREADS 1 -#define MAX_NONCE 3000000 -// #define MAX_NONCE 1.215.752.192 +#define MAX_NONCE 5000000U +#define TARGET_NONCE 471136297U -// Pool -//#define POOL_URL "solo.ckpool.org" //"btc.zsolo.bid" "eu.stratum.slushpool.com" -//#define POOL_PORT 3333 //6057 //3333 - - -void runMonitor(void *name); +void runMonitor(); void runWorker(void *name); void runMiner(void); \ No newline at end of file