From 0aabedcbd1ab5f3aa8d81a09ec16c423d31520dc Mon Sep 17 00:00:00 2001 From: "Alfonso C. Alvarez" Date: Wed, 3 May 2023 21:11:59 +0200 Subject: [PATCH 1/2] Revert "Merge branch 'master' of https://github.com/alcar21/NerdMiner_v2 into feature/add_platformio_support_and_improvements" This reverts commit 8884bb58d70f6120b8843e5dccf8704c1d047e6f. --- .gitignore | 1 - platformio.ini | 8 +- src/NerdMinerV2.ino.cpp | 6 +- src/mining.cpp | 257 ++++++++++++++++------------------------ src/mining.h | 11 +- 5 files changed, 116 insertions(+), 167 deletions(-) diff --git a/.gitignore b/.gitignore index 6396bf6..5ca6960 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .pio .vscode NerdMinerLog.txt -platformio-device-monitor*.log diff --git a/platformio.ini b/platformio.ini index 910b64c..0b0cec5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,20 +18,22 @@ 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_SHARE + ;-D DEBUG_MINING=1 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 b921a25..f6c2750 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -79,6 +79,7 @@ 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++) { @@ -87,10 +88,8 @@ void setup() // Start mining tasks BaseType_t res = xTaskCreate(runWorker, name, 30000, (void*)name, 1, NULL); - Serial.printf("Starting miner %s %s!\n", name, res == pdPASS? "successful":"failed"); + Serial.printf("Starting %s %s!\n", name, res == pdPASS? "successful":"failed"); } - - Serial.println("NerdMiner v2 started......"); } void app_error_fault_handler(void *arg) { @@ -124,5 +123,4 @@ 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 581ec22..1fa30ac 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -3,7 +3,6 @@ #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" @@ -12,7 +11,6 @@ #include "mining.h" #define TARGET_BUFFER_SIZE 64 -#define BUFFER_JSON_DOC 1024 unsigned long templates = 0; unsigned long hashes= 0; @@ -31,6 +29,8 @@ 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_SHARE + #ifdef DEBUG_MINING if (valid) { Serial.print("\tvalid : "); for (size_t i = 0; i < 32; i++) @@ -87,55 +87,21 @@ 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()); - #endif + + String ADDRESS = String(btcString); // connect to pool WiFiClient client; bool continueSecuence = false; - String line, extranonce1, extranonce2 = String("0"); - unsigned long id = 0, extranonce_number = 0; + String line, extranonce1; + unsigned long id = 1; unsigned int extranonce2_size; while(true) { @@ -143,47 +109,54 @@ void runWorker(void *name) { if(WiFi.status() != WL_CONNECTED) continue; // get template - StaticJsonDocument doc; - - char payload[BUFFER_JSON_DOC] = {0}; + DynamicJsonDocument doc(4 * 1024); + String payload; if (!client.connect(poolString, portNumber)) { continue; } - // 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); + // STEP 1: Pool server connection + payload = "{\"id\": "+ String(id++) +", \"method\": \"mining.subscribe\", \"params\": [\"" + ADDRESS + "\", \"password\"]}\n"; Serial.printf("[WORKER] %s ==> Mining subscribe\n", (char *)name); Serial.print(" Sending : "); Serial.println(payload); - client.print(payload); + client.print(payload.c_str()); line = client.readStringUntil('\n'); if(!verifyPayload(line)) return; Serial.print(" Receiving: "); Serial.println(line); deserializeJson(doc, line); - if (checkError(doc)) { - Serial.printf("[WORKER] %s >>>>>>>>> Work aborted\n", (char *)name); - continue; - } + int error = doc["error"]; 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); - - // Recibe Work - //line = "{\"params\": [\"b3ba\", \"7dcf1304b04e79024066cd9481aa464e2fe17966e19edf6f33970e1fe0b60277\", \"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270362f401062f503253482f049b8f175308\", \"0d2f7374726174756d506f6f6c2f000000000100868591052100001976a91431482118f1d7504daf1c001cbfaf91ad580d176d88ac00000000\", [\"57351e8569cb9d036187a79fd1844fd930c1309efcd16c46af9bb9713b6ee734\", \"936ab9c33420f187acae660fcdb07ffdffa081273674f0f41e6ecc1347451d23\"], \"00000002\", \"1b44dfdb\", \"53178f9b\", true], \"id\": null, \"method\": \"mining.notify\"}"; + 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'); - doc.clear(); + 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(); + 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]); @@ -194,28 +167,6 @@ 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); @@ -232,6 +183,7 @@ void runWorker(void *name) { Serial.println(">>>>>>>>> Worker aborted"); client.stop(); doc.clear(); + doc.garbageCollect(); continue; } @@ -244,7 +196,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] = 0; + target[TARGET_BUFFER_SIZE+1] = 0; Serial.print(" target: "); Serial.println(target); // bytearray target uint8_t bytearray_target[32]; @@ -262,15 +214,30 @@ void runWorker(void *name) { } // get extranonce2 - extranonce2 = hex(random.randint(0,2**32-1))[2:].zfill(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"; - + 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)); //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]; @@ -281,7 +248,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 @@ -312,7 +279,7 @@ void runWorker(void *name) { memcpy(merkle_result, shaResult, sizeof(shaResult)); byte merkle_concatenated[32 * 2]; - for (size_t k=0; k < merkle_branch.size(); k++) { + for (size_t k=0; k>> STARTING TO HASH NONCES"); @@ -484,7 +444,7 @@ void runWorker(void *name) { Serial.println(""); */ hashes++; - if (nonce++> TARGET_NONCE) break; //exit + if (nonce++> MAX_NONCE) break; //exit // check if 16bit share if(hash[31]!=0) continue; @@ -497,30 +457,24 @@ void runWorker(void *name) { // check if valid header if(checkValid(hash, bytearray_target)){ - 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; + //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; } } // exit if found a valid result or nonce > MAX_NONCE @@ -528,31 +482,22 @@ 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 == TARGET_NONCE) { + if (nonce == MAX_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)) { - sprintf(payload, "{\"params\": [\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"], \"id\": %u, \"method\": \"mining.submit\"}", - btcString, - job_id, - extranonce2, - ntime, - String(nonce, HEX), - id - ); + 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); - unsigned long timeout = millis(); - Serial.print(" Receiving: "); Serial.println(client.readString()); - Serial.printf("[WORKER] %s SUBMITED WORK\n", (char *)name); + 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.stop(); } } - uint32_t duration = micros() - startT; } @@ -560,10 +505,11 @@ 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]; @@ -580,7 +526,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); @@ -599,7 +545,7 @@ void runMiner(void) { } -void runMonitor() { +void runMonitor(void *name){ Serial.println("[MONITOR] started"); @@ -610,9 +556,8 @@ void runMonitor() { 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); @@ -654,6 +599,6 @@ void runMonitor() { 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 ffc5093..eadc0b6 100644 --- a/src/mining.h +++ b/src/mining.h @@ -1,9 +1,14 @@ // Mining #define THREADS 1 -#define MAX_NONCE 5000000U -#define TARGET_NONCE 471136297U +#define MAX_NONCE 3000000 +// #define MAX_NONCE 1.215.752.192 -void runMonitor(); +// 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 runWorker(void *name); void runMiner(void); \ No newline at end of file From 444f9f26e774568d007a71e108e43e75a10bbad3 Mon Sep 17 00:00:00 2001 From: "Alfonso C. Alvarez" Date: Sun, 7 May 2023 13:21:00 +0200 Subject: [PATCH 2/2] minor fixes --- README.md | 6 + src/NerdMinerV2.ino.cpp | 2 +- src/mining.cpp | 325 ++++++++++++++++++++++------------------ src/mining.h | 9 +- 4 files changed, 187 insertions(+), 155 deletions(-) diff --git a/README.md b/README.md index 52a6ae4..ab10550 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ If you want you can compile the entire project using Arduino, PlatformIO or Expr After programming, you will only need to setup your Wifi and BTC address. 1. Connect to NerdMinerAP + - AP: NerdMinerAP + - PASS: MineYourCoins 1. Setup your Wifi Network 1. Add your BTCaddress @@ -76,10 +78,14 @@ Optional you can select other pool: - [x] Bug memory leaks - [x] Bug Reboots when received JSON contains some null values - [x] Implement midstate sha256 +- [x] Bug Wificlient DNS unresolved on Wifi.h - [ ] Code refactoring - [ ] Add blockHeight to screen - [ ] Add clock to show current time - [ ] Add new screen with global mining stats - [ ] Add support to control BM1397 +### Donations/Project contributions +If you would like to contribute and help dev team with this project you can send a donation to the following LN address ⚡teamnerdminer@getalby.com⚡ + Enjoy \ No newline at end of file diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index f6c2750..1b6281b 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -121,6 +121,6 @@ void loop() { checkResetConfigButton(); //Run miner on main core when there is time --Currently on test - runMiner(); + // runMiner(); } \ No newline at end of file diff --git a/src/mining.cpp b/src/mining.cpp index 1fa30ac..a726bc2 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -11,10 +11,13 @@ #include "mining.h" #define TARGET_BUFFER_SIZE 64 +#define BUFFER_JSON_DOC 4096 unsigned long templates = 0; unsigned long hashes= 0; unsigned long Mhashes = 0; +unsigned long totalKHashes = 0; +unsigned long mStart = millis(); int halfshares; // increase if blockhash has 16 bits of zeroes int shares; // increase if blockhash has 32 bits of zeroes @@ -79,83 +82,144 @@ int to_byte_array(const char *in, size_t in_size, uint8_t *out) { } } -bool verifyPayload (String line){ - String inData=line; - if(inData.length() == 0) return false; - inData.trim(); - if(inData.isEmpty()) return false; +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 verifyPayload (String* line){ + if(line->length() == 0) return false; + line->trim(); + if(line->isEmpty()) return false; return true; } +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 +// 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; + IPAddress serverIP; //Temporally save poolIPaddres + bool isMinerSuscribed = false; 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) { - if(WiFi.status() != WL_CONNECTED) continue; + if(WiFi.status() != WL_CONNECTED){ + vTaskDelay(1000 / portTICK_PERIOD_MS); + continue; + } // get template - DynamicJsonDocument doc(4 * 1024); - String payload; + StaticJsonDocument doc; - if (!client.connect(poolString, portNumber)) { - continue; + char payload[BUFFER_JSON_DOC] = {0}; + + if (!client.connected()) { + isMinerSuscribed = false; + Serial.println("Client not connected, trying to connect..."); + if (!client.connect(serverIP, portNumber)) { + Serial.println("Imposible to connect to : " + String(poolString)); + WiFi.hostByName(poolString, serverIP); + vTaskDelay(1000 / portTICK_PERIOD_MS); + continue; + } } - // STEP 1: Pool server connection - payload = "{\"id\": "+ String(id++) +", \"method\": \"mining.subscribe\", \"params\": [\"" + ADDRESS + "\", \"password\"]}\n"; - Serial.printf("[WORKER] %s ==> Mining subscribe\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); - deserializeJson(doc, line); - int error = doc["error"]; - 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(" 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 1: Pool server connection (SUBSCRIBE) + // Docs: + // - https://cs.braiins.com/stratum-v1/docs + // - https://github.com/aeternity/protocol/blob/master/STRATUM.md#mining-subscribe + if(!isMinerSuscribed){ + 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); + line = client.readStringUntil('\n'); + if(!verifyPayload(&line)) continue; + Serial.print(" Receiving: "); Serial.println(line); + deserializeJson(doc, line); + 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); + + 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; + } + isMinerSuscribed=true; } - // STEP 2: Pool authorize work - payload = "{\"params\": [\"" + ADDRESS + "\", \"password\"], \"id\": "+ String(id++) +", \"method\": \"mining.authorize\"}\n"; + // 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.c_str()); + client.print(payload); line = client.readStringUntil('\n'); - if(!verifyPayload(line)) return; + if(!verifyPayload(&line)) continue; Serial.print(" Receiving: "); Serial.println(line); Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); - client.stop(); - + // client.stop(); + deserializeJson(doc, line); String job_id = String((const char*) doc["params"][0]); String prevhash = String((const char*) doc["params"][1]); @@ -179,15 +243,11 @@ void runWorker(void *name) { Serial.print(" clean_jobs: "); Serial.println(clean_jobs); #endif //Check if parameters where correctly received - if((job_id.length() == 0)||(prevhash.length() == 0)||(coinb2.length() == 0)||(ntime.length() == 0)) { - Serial.println(">>>>>>>>> Worker aborted"); - client.stop(); - doc.clear(); - doc.garbageCollect(); - continue; - } + if (checkError(doc)) { + Serial.printf("[WORKER] %s >>>>>>>>> Work aborted\n", (char *)name); + continue; + } - doc.clear(); templates++; // calculate target - target = (nbits[2:]+'00'*(int(nbits[:2],16) - 3)).zfill(64) @@ -196,7 +256,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 +274,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 +293,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 +324,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"); while(true) { memcpy(bytearray_blockheader + 77, &nonce, 3); - // double sha - // Sin midstate - /*mbedtls_sha256_starts_ret(&ctx,0); - mbedtls_sha256_update_ret(&ctx, bytearray_blockheader, 80); - mbedtls_sha256_finish_ret(&ctx, interResult); - - mbedtls_sha256_starts_ret(&ctx,0); - mbedtls_sha256_update_ret(&ctx, interResult, 32); - mbedtls_sha256_finish_ret(&ctx, shaResult); - for (size_t i = 0; i < 32; i++) - Serial.printf("%02x", shaResult[i]); - Serial.println("");*/ - //Con midstate // Primer SHA-256 mbedtls_sha256_clone(&ctx, midstate); //Clonamos el contexto anterior para continuar el SHA desde allí @@ -444,37 +483,41 @@ 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; - if(hash[30]!=0) continue; + if(hash[31] !=0 || hash[30] !=0) continue; halfshares++; // check if 32bit share - if(hash[29]!=0) continue; - if(hash[28]!=0) continue; + if(hash[29] !=0 || hash[28] !=0) continue; shares++; // 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 +525,9 @@ 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) { - Serial.printf("[WORKER] %s SUBMITING WORK... MAX Nonce reached > MAX_NONCE\n", (char *)name); - // STEP 3: Submit mining job - if (client.connect(poolString, portNumber)) { - 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()); - Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); - while (client.available()) { - Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); - } - client.stop(); - } - } uint32_t duration = micros() - startT; } @@ -547,17 +577,18 @@ void runMiner(void){ void runMonitor(void *name){ - Serial.println("[MONITOR] started"); + // Serial.println("[MONITOR] started"); - unsigned long mStart = millis(); - while(1){ + + //while(1){ background.pushImage(0, 0, MinerWidth, MinerHeight, MinerScreen); unsigned long mElapsed = millis()-mStart; - unsigned long totalKHashes = (Mhashes*1000) + hashes/1000; + unsigned long totalKHashes = (Mhashes*1000) + hashes/1000 - totalKHashes; //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 +630,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..bfbb74b 100644 --- a/src/mining.h +++ b/src/mining.h @@ -1,13 +1,8 @@ // Mining #define THREADS 1 -#define MAX_NONCE 3000000 -// #define MAX_NONCE 1.215.752.192 - -// Pool -//#define POOL_URL "solo.ckpool.org" //"btc.zsolo.bid" "eu.stratum.slushpool.com" -//#define POOL_PORT 3333 //6057 //3333 - +#define MAX_NONCE 5000000U +#define TARGET_NONCE 471136297U void runMonitor(void *name); void runWorker(void *name);