From ffd1664b214a9d9e2da362e1a9a7327f6a2264cd Mon Sep 17 00:00:00 2001 From: BitMaker Date: Fri, 26 May 2023 13:02:14 +0200 Subject: [PATCH] Refactoring + Improvements - Added Stratum thread - Added only miner thread - Added utils lib - Checking all new stratum messages - Added difficulty check - Added a mining notify based on pool diff target - Improved stability and json errors --- src/NerdMinerV2.ino.cpp | 47 ++-- src/mining.cpp | 539 ++++++++-------------------------------- src/mining.h | 26 +- src/stratum.cpp | 255 +++++++++++++++++++ src/stratum.h | 67 +++++ src/utils.cpp | 366 +++++++++++++++++++++++++++ src/utils.h | 31 +++ src/wManager.cpp | 17 ++ 8 files changed, 878 insertions(+), 470 deletions(-) create mode 100644 src/stratum.cpp create mode 100644 src/stratum.h create mode 100644 src/utils.cpp create mode 100644 src/utils.h diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index df1c084..d13721c 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -26,13 +26,6 @@ OpenFontRender render; TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h TFT_eSprite background = TFT_eSprite(&tft); // Invoke library sprite -//static long templates = 0; -//static long hashes = 0; -//static int halfshares = 0; // increase if blockhash has 16 bits of zeroes -//static int shares = 0; // increase if blockhash has 32 bits of zeroes -//static int valids = 0; // increased if blockhash <= target - -int oldStatus = 0; unsigned long start = millis(); const char* ntpServer = "pool.ntp.org"; @@ -61,7 +54,7 @@ void setup() // Idle task that would reset WDT never runs, because core 0 gets fully utilized disableCore0WDT(); - disableCore1WDT(); + //disableCore1WDT(); // Setup the buttons // Button 1 (Boot) @@ -115,17 +108,28 @@ void setup() // Higher prio monitor task Serial.println(""); Serial.println("Initiating tasks..."); - xTaskCreate(runMonitor, "Monitor", 5000, NULL, 4, NULL); + char *name = (char*) malloc(32); + sprintf(name, "(%s)", "Monitor"); + BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 5000, (void*)name, 4, NULL,1); + + /******** CREATE STRATUM TASK *****/ + sprintf(name, "(%s)", "Stratum"); + BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 20000, (void*)name, 3, NULL,1); + /******** CREATE MINER TASKS *****/ - for (size_t i = 0; i < THREADS; i++) { - char *name = (char*) malloc(32); - sprintf(name, "(%d)", i); + //for (size_t i = 0; i < THREADS; i++) { + // char *name = (char*) malloc(32); + // sprintf(name, "(%d)", i); - // 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"); - } + // Start stratum tasks + sprintf(name, "(%s)", "Miner0"); + //BaseType_t res = xTaskCreatePinnedToCore(runMiner, "0", 10000, (void*)name, 1, NULL, 0); + BaseType_t res3 = xTaskCreatePinnedToCore(runMiner, "0", 10000, (void*)name, 1,NULL, 0); + //sprintf(name, "(%s)", "Miner1"); + //BaseType_t res4 = xTaskCreatePinnedToCore(runMiner, "1", 10000, (void*)name, 1,NULL, 1); + //Serial.printf("Starting %s %s!\n", "1", res3 == pdPASS? "successful":"failed"); + /******** TIME ZONE SETTING *****/ configTime(0, 0, ntpServer); @@ -149,17 +153,6 @@ void loop() { button2.tick(); wifiManagerProcess(); // avoid delays() in loop when non-blocking and other long running code - - int newStatus = WiFi.status(); - if (newStatus != oldStatus) { - if (newStatus == WL_CONNECTED) { - Serial.println("CONNECTED - Current ip: " + WiFi.localIP().toString()); - } else { - Serial.print("[Error] - current status: "); - Serial.println(newStatus); - } - oldStatus = newStatus; - } //Run miner on main core when there is time --Currently on test // runMiner(); diff --git a/src/mining.cpp b/src/mining.cpp index 233ada1..65e1dcb 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -8,10 +8,9 @@ #include "mbedtls/md.h" #include "mbedtls/sha256.h" #include "OpenFontRender.h" +#include "stratum.h" #include "mining.h" - -#define TARGET_BUFFER_SIZE 64 -#define BUFFER_JSON_DOC 4096 +#include "utils.h" static unsigned long templates = 0; static unsigned long hashes= 0; @@ -22,7 +21,6 @@ static String temp; static int halfshares; // increase if blockhash has 16 bits of zeroes static int shares; // increase if blockhash has 32 bits of zeroes static int valids; // increased if blockhash <= target -bool enableGlobalHash = false; // Variables to hold data from custom textboxes extern char poolString[80]; @@ -32,97 +30,13 @@ extern char btcString[80]; extern OpenFontRender render; extern TFT_eSprite background; +//Global work data +static WiFiClient client; +static miner_data mMiner; //Global miner data (Create a miner class TODO) +mining_subscribe mWorker; +mining_job mJob; - -bool checkValid(unsigned char* hash, unsigned char* target) { - bool valid = true; - for(uint8_t i=31; i>=0; i--) { - if(hash[i] > target[i]) { - valid = false; - break; - } else if (hash[i] < target[i]) { - valid = true; - break; - } - } - #ifdef DEBUG_MINING - if (valid) { - Serial.print("\tvalid : "); - for (size_t i = 0; i < 32; i++) - Serial.printf("%02x ", hash[i]); - Serial.println(); - } - #endif - return valid; -} - -uint8_t hex(char ch) { - uint8_t r = (ch > 57) ? (ch - 55) : (ch - 48); - return r & 0x0F; -} - -int to_byte_array(const char *in, size_t in_size, uint8_t *out) { - int count = 0; - if (in_size % 2) { - while (*in && out) { - *out = hex(*in++); - if (!*in) - return count; - *out = (*out << 4) | hex(*in++); - *out++; - count++; - } - return count; - } else { - while (*in && out) { - *out++ = (hex(*in++) << 4) | hex(*in++); - count++; - } - return count; - } -} - -bool verifyPayload (String* line){ - if(line->length() == 0) return false; - line->trim(); - if(line->isEmpty()) return false; - 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) { +void runStratumWorker(void *name) { // TEST: https://bitcoin.stackexchange.com/questions/22929/full-example-data-for-scrypt-stratum-client @@ -134,13 +48,10 @@ void runWorker(void *name) { #endif // connect to pool - WiFiClient client; + IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres + bool isMinerSuscribed = false; - bool continueSecuence = false; - String line, extranonce1, extranonce2 = String("0"); - unsigned long id = 0, extranonce_number = 0; - unsigned int extranonce2_size; while(true) { @@ -149,326 +60,117 @@ void runWorker(void *name) { continue; } - // get template - StaticJsonDocument doc; - - char payload[BUFFER_JSON_DOC] = {0}; - + //Test vars: + strcpy(poolString, "testServerIP"); + strcpy(btcString,"mybtcString"); + + portNumber = 3002; 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); + Serial.print("Resolved DNS got : "); Serial.println(serverIP); vTaskDelay(1000 / portTICK_PERIOD_MS); 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); - vTaskDelay(200 / portTICK_PERIOD_MS); - 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()); + mWorker = init_mining_subscribe(); + + // STEP 1: Pool server connection (SUBSCRIBE) + if(!tx_mining_subscribe(client, mWorker)) { client.stop(); - doc.clear(); - doc.garbageCollect(); continue; } + + //strcpy(mWorker.name, btcString); + //strcpy(mWorker.pass, "x"); + // STEP 2: Pool authorize work (Block Info) + tx_mining_auth(client, btcString, "x"); //Don't verifies authoritzation, TODO + isMinerSuscribed=true; } - // 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); - vTaskDelay(200 / portTICK_PERIOD_MS); - line = client.readStringUntil('\n'); - 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(); - deserializeJson(doc, line); - 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]); - String coinb2 = String((const char*) doc["params"][3]); - JsonArray merkle_branch = doc["params"][4]; - String version = String((const char*) doc["params"][5]); - String nbits = String((const char*) doc["params"][6]); - String ntime = String((const char*) doc["params"][7]); - bool clean_jobs = doc["params"][8]; //bool - #ifdef DEBUG_MINING - Serial.print(" job_id: "); Serial.println(job_id); - Serial.print(" prevhash: "); Serial.println(prevhash); - Serial.print(" coinb1: "); Serial.println(coinb1); - Serial.print(" coinb2: "); Serial.println(coinb2); - Serial.print(" merkle_branch size: "); Serial.println(merkle_branch.size()); - Serial.print(" version: "); Serial.println(version); - Serial.print(" nbits: "); Serial.println(nbits); - Serial.print(" ntime: "); Serial.println(ntime); - Serial.print(" clean_jobs: "); Serial.println(clean_jobs); - #endif - //Check if parameters where correctly received - if (checkError(doc)) { - Serial.printf("[WORKER] %s >>>>>>>>> Work aborted\n", (char *)name); - continue; + //Read pending messages from pool + while(client.available()){ + + Serial.println(" Received message from pool"); + String line = client.readStringUntil('\n'); + stratum_method result = parse_mining_method(line); + switch (result) + { + case STRATUM_PARSE_ERROR: Serial.println(" Parsed JSON: error on JSON"); break; + case MINING_NOTIFY: if(parse_mining_notify(line, mJob)){ + //Increse templates readed + templates++; + //Stop miner current job + mMiner.inRun = false; + //Prepare data for new job + mMiner=calculateMiningData(mWorker,mJob); + mMiner.newJob = true; + //Give new job to miner + + } + break; + case MINING_SET_DIFFICULTY: if(parse_mining_set_difficulty(line, mMiner.difficulty)){ + //Calculate new target + //TODO + //Give new target to workers + //TODO + } + break; + default: Serial.println(" Parsed JSON: unknown"); break; + + } } - templates++; - - // calculate target - target = (nbits[2:]+'00'*(int(nbits[:2],16) - 3)).zfill(64) + vTaskDelay(200 / portTICK_PERIOD_MS); //Small delay - char target[TARGET_BUFFER_SIZE+1]; - 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; - Serial.print(" target: "); Serial.println(target); - // bytearray target - uint8_t bytearray_target[32]; - size_t size_target = to_byte_array(target, 32, bytearray_target); - // uint8_t buf; - // for (size_t j = 0; j < 16; j++) { - // buf = bytearray_target[j]; - // bytearray_target[j] = bytearray_target[size_target - 1 - j]; - // bytearray_target[size_target - 1 - j] = buf; - // } - for (size_t j = 0; j < 8; j++) { - bytearray_target[j] ^= bytearray_target[size_target - 1 - j]; - bytearray_target[size_target - 1 - j] ^= bytearray_target[j]; - bytearray_target[j] ^= bytearray_target[size_target - 1 - j]; - } - - // 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"; - - //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]; - - size_t res = to_byte_array(coinbase.c_str(), str_len*2, bytearray); - - #ifdef DEBUG_MINING - Serial.print(" extranonce2: "); Serial.println(extranonce2); - 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.println("---"); - #endif - - mbedtls_sha256_context ctx; - mbedtls_sha256_init(&ctx); + } - byte interResult[32]; // 256 bit - byte shaResult[32]; // 256 bit +} + + +//////////////////THREAD CALLS/////////////////// + +//This works only with one thread, TODO -> Class or miner_data for each thread + +void runMiner(void * name){ - mbedtls_sha256_starts_ret(&ctx,0); - mbedtls_sha256_update_ret(&ctx, bytearray, str_len); - mbedtls_sha256_finish_ret(&ctx, interResult); + while(1){ - mbedtls_sha256_starts_ret(&ctx,0); - mbedtls_sha256_update_ret(&ctx, interResult, 32); - mbedtls_sha256_finish_ret(&ctx, shaResult); - mbedtls_sha256_free(&ctx); - - #ifdef DEBUG_MINING - Serial.print(" coinbase double sha: "); - for (size_t i = 0; i < 32; i++) - Serial.printf("%02x", shaResult[i]); - Serial.println(""); - #endif - - byte merkle_result[32]; - // copy coinbase hash - memcpy(merkle_result, shaResult, sizeof(shaResult)); - - byte merkle_concatenated[32 * 2]; - for (size_t k=0; k < merkle_branch.size(); k++) { - const char* merkle_element = (const char*) merkle_branch[k]; - uint8_t bytearray[32]; - size_t res = to_byte_array(merkle_element, 64, bytearray); - - #ifdef DEBUG_MINING - Serial.print(" merkle element "); Serial.print(k); Serial.print(": "); Serial.println(merkle_element); - #endif - for (size_t i = 0; i < 32; i++) { - merkle_concatenated[i] = merkle_result[i]; - merkle_concatenated[32 + i] = bytearray[i]; - } - - #ifdef DEBUG_MINING - Serial.print(" merkle element "); Serial.print(k); Serial.print(": "); Serial.println(merkle_element); - Serial.print(" merkle concatenated: "); - for (size_t i = 0; i < 64; i++) - Serial.printf("%02x", merkle_concatenated[i]); - Serial.println(""); - #endif - - mbedtls_sha256_context ctx; - mbedtls_sha256_init(&ctx); - mbedtls_sha256_starts_ret(&ctx,0); - mbedtls_sha256_update_ret(&ctx, merkle_concatenated, 64); - mbedtls_sha256_finish_ret(&ctx, interResult); - - mbedtls_sha256_starts_ret(&ctx,0); - mbedtls_sha256_update_ret(&ctx, interResult, 32); - mbedtls_sha256_finish_ret(&ctx, merkle_result); - mbedtls_sha256_free(&ctx); - - #ifdef DEBUG_MINING - Serial.print(" merkle sha : "); - for (size_t i = 0; i < 32; i++) - Serial.printf("%02x", merkle_result[i]); - Serial.println(""); - #endif - } - // merkle root from merkle_result - - Serial.print(" merkle sha : "); - char merkle_root[65]; - for (int i = 0; i < 32; i++) { - Serial.printf("%02x", merkle_result[i]); - snprintf(&merkle_root[i*2], 3, "%02x", merkle_result[i]); - } - merkle_root[65] = 0; - Serial.println(""); - - // calculate blockheader - // j.block_header = ''.join([j.version, j.prevhash, merkle_root, j.ntime, j.nbits]) - String blockheader = version + prevhash + String(merkle_root) + ntime + nbits + "00000000"; - str_len = blockheader.length()/2; - uint8_t bytearray_blockheader[str_len]; - res = to_byte_array(blockheader.c_str(), str_len*2, bytearray_blockheader); - - #ifdef DEBUG_MINING - Serial.println(" blockheader bytes "); Serial.print(str_len); Serial.print(" -> "); - #endif - - // reverse version - uint8_t buff; - size_t bsize, boffset; - boffset = 0; - bsize = 4; - for (size_t j = boffset; j < boffset + (bsize/2); j++) { - buff = bytearray_blockheader[j]; - bytearray_blockheader[j] = bytearray_blockheader[2 * boffset + bsize - 1 - j]; - bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; + //Wait new job + while(1){ + if(mMiner.newJob==true) break; + vTaskDelay(100 / portTICK_PERIOD_MS); //Small delay } - // reverse merkle - boffset = 36; - bsize = 32; - for (size_t j = boffset; j < boffset + (bsize/2); j++) { - buff = bytearray_blockheader[j]; - bytearray_blockheader[j] = bytearray_blockheader[2 * boffset + bsize - 1 - j]; - bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; - } - // reverse difficulty - boffset = 72; - bsize = 4; - for (size_t j = boffset; j < boffset + (bsize/2); j++) { - buff = bytearray_blockheader[j]; - bytearray_blockheader[j] = bytearray_blockheader[2 * boffset + bsize - 1 - j]; - bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; - } - - - #ifdef DEBUG_MINING - Serial.print(" >>> bytearray_blockheader : "); - for (size_t i = 0; i < 4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); - Serial.println(""); - Serial.print("version "); - for (size_t i = 0; i < 4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); - Serial.println(""); - Serial.print("prev hash "); - for (size_t i = 4; i < 4+32; i++) - Serial.printf("%02x", bytearray_blockheader[i]); - Serial.println(""); - Serial.print("merkle root "); - for (size_t i = 36; i < 36+32; i++) - Serial.printf("%02x", bytearray_blockheader[i]); - Serial.println(""); - Serial.print("nbits "); - for (size_t i = 68; i < 68+4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); - Serial.println(""); - Serial.print("difficulty "); - for (size_t i = 72; i < 72+4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); - Serial.println(""); - Serial.print("nonce "); - for (size_t i = 76; i < 76+4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); - Serial.println(""); - Serial.println("bytearray_blockheader: "); - for (size_t i = 0; i < str_len; i++) { - Serial.printf("%02x", bytearray_blockheader[i]); - } - Serial.println(""); - #endif + mMiner.newJob = false; //Clear newJob flag + mMiner.inRun = true; //Set inRun flag + //Prepare Premining data mbedtls_sha256_context midstate[32]; unsigned char hash[32]; + mbedtls_sha256_context ctx; //Calcular midstate mbedtls_sha256_init(midstate); mbedtls_sha256_starts_ret(midstate, 0); - mbedtls_sha256_update_ret(midstate, bytearray_blockheader, 64); + mbedtls_sha256_update_ret(midstate, mMiner.bytearray_blockheader, 64); // search a valid nonce - enableGlobalHash = true; - unsigned long nonce = TARGET_NONCE - MAX_NONCE; uint32_t startT = micros(); - unsigned char *header64 = bytearray_blockheader + 64; + unsigned char *header64 = mMiner.bytearray_blockheader + 64; Serial.println(">>> STARTING TO HASH NONCES"); while(true) { - memcpy(bytearray_blockheader + 76, &nonce, 4); + memcpy(mMiner.bytearray_blockheader + 76, &nonce, 4); //Con midstate // Primer SHA-256 @@ -486,36 +188,39 @@ void runWorker(void *name) { hashes++; if (nonce++> TARGET_NONCE) break; //exit + if(!mMiner.inRun) { Serial.println ("MINER WORK ABORTED >> waiting new job"); break;} // check if 16bit share if(hash[31] !=0 || hash[30] !=0) continue; halfshares++; + + //Check target to submit + //Difficulty of 1 > 0x00000000FFFF0000000000000000000000000000000000000000000000000000 + //NM2 pool diff 1e-9 > Target = diff_1 / diff_pool > 0x00003B9ACA00....00 + //Swapping diff bytes little endian >>>>>>>>>>>>>>>> 0x0000DC59D300....00 + //if((hash[29] <= 0xDC) && (hash[28] <= 0x59)) //0x00003B9ACA00 > diff value for 1e-9 + double diff_hash = diff_from_target(hash); + if(hash[29] <= 0x3B)//(diff_hash > 1e-9) + { + tx_mining_submit(client, mWorker, mJob, nonce); + Serial.print(" - Current diff share: "); Serial.println(diff_hash); + Serial.print(" - TX SHARE: "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", hash[i]); + Serial.println(""); + } + // check if 32bit share if(hash[29] !=0 || hash[28] !=0) continue; shares++; - // check if valid header - if(checkValid(hash, bytearray_target)){ + // check if valid header + if(checkValid(hash, mMiner.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()); + tx_mining_submit(client, mWorker, mJob, nonce); client.stop(); // exit nonce = MAX_NONCE; @@ -525,56 +230,12 @@ void runWorker(void *name) { mbedtls_sha256_free(&ctx); mbedtls_sha256_free(midstate); - enableGlobalHash = false; // TODO Pending doub if(hashes>=MAX_NONCE) { Mhashes=Mhashes+MAX_NONCE/1000000; hashes=hashes-MAX_NONCE;} uint32_t duration = micros() - startT; } - -} - - -//////////////////THREAD CALLS/////////////////// - -//Testeamos hashrate final usando hilo principal -//this is currently on test - -void runMiner(void){ - uint32_t nonce=0; - unsigned char bytearray_blockheader[80]; - - if(!enableGlobalHash) return; - - mbedtls_sha256_context midstate[32], ctx; - unsigned char hash[32]; - - //Calcular midstate - mbedtls_sha256_init(midstate); - mbedtls_sha256_starts_ret(midstate, 0); - mbedtls_sha256_update_ret(midstate, bytearray_blockheader, 64); - - //Iteraciones - unsigned char *header64 = bytearray_blockheader + 64; - - 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); - mbedtls_sha256_finish_ret(&ctx, hash); - - // Segundo SHA-256 - mbedtls_sha256_starts_ret(&ctx, 0); - mbedtls_sha256_update_ret(&ctx, hash, 32); - mbedtls_sha256_finish_ret(&ctx, hash); - - hashes++; - } - - mbedtls_sha256_free(&ctx); - mbedtls_sha256_free(midstate); - } void runMonitor(void *name){ diff --git a/src/mining.h b/src/mining.h index 57ca6da..1e1b201 100644 --- a/src/mining.h +++ b/src/mining.h @@ -1,10 +1,28 @@ +#ifndef MINING_API_H +#define MINING_API_H + // Mining -#define THREADS 1 #define MAX_NONCE 5000000U #define TARGET_NONCE 471136297U +#define DEFAULT_DIFFICULTY 1e-9; + +#define TARGET_BUFFER_SIZE 64 void runMonitor(void *name); -void runWorker(void *name); -void runMiner(void); -String printLocalTime(void); \ No newline at end of file +void runStratumWorker(void *name); +void runMiner(void *name); +String printLocalTime(void); + +typedef struct{ + uint8_t bytearray_target[32]; + uint8_t bytearray_pooltarget[32]; + uint8_t merkle_result[32]; + uint8_t bytearray_blockheader[80]; + float difficulty; + bool inRun; + bool newJob; +}miner_data; + + +#endif // UTILS_API_H \ No newline at end of file diff --git a/src/stratum.cpp b/src/stratum.cpp new file mode 100644 index 0000000..98d9b68 --- /dev/null +++ b/src/stratum.cpp @@ -0,0 +1,255 @@ +#include +#include +#include +#include "stratum.h" +#include "cJSON.h" +#include +#include +#include "esp_log.h" +#include "lwip/sockets.h" +#include "utils.h" + + + +StaticJsonDocument doc; +unsigned long id = 1; + +//Get next JSON RPC Id +unsigned long getNextId(unsigned long id) { + if (id == ULONG_MAX) { + id = 1; + return id; + } + return ++id; +} + +//Verify Payload doesn't has zero lenght +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.containsKey("error")) return false; + + 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; +} + + +// 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 +bool tx_mining_subscribe(WiFiClient& client, mining_subscribe& mSubscribe) +{ + char payload[BUFFER] = {0}; + + // Subscribe + id = 1; //Initialize id messages + sprintf(payload, "{\"id\": %u, \"method\": \"mining.subscribe\", \"params\": [\"NerdMinerV2\"]}\n", id); + + Serial.printf("[WORKER] ==> Mining subscribe\n"); + Serial.print(" Sending : "); Serial.println(payload); + client.print(payload); + + vTaskDelay(200 / portTICK_PERIOD_MS); //Small delay + + String line = client.readStringUntil('\n'); + if(!parse_mining_subscribe(line, mSubscribe)) return false; + + + Serial.print(" sub_details: "); Serial.println(mSubscribe.sub_details); + Serial.print(" extranonce1: "); Serial.println(mSubscribe.extranonce1); + Serial.print(" extranonce2_size: "); Serial.println(mSubscribe.extranonce2_size); + + if((mSubscribe.extranonce1.length() == 0) ) { + Serial.printf("[WORKER] >>>>>>>>> Work aborted\n"); + Serial.printf("extranonce1 length: %u \n", mSubscribe.extranonce1.length()); + doc.clear(); + doc.garbageCollect(); + return false; + } + return true; +} + +bool parse_mining_subscribe(String line, mining_subscribe& mSubscribe) +{ + if(!verifyPayload(&line)) return false; + Serial.print(" Receiving: "); Serial.println(line); + + DeserializationError error = deserializeJson(doc, line); + + if (error || checkError(doc)) return false; + if (!doc.containsKey("result")) return false; + + mSubscribe.sub_details = String((const char*) doc["result"][0][0][1]); + mSubscribe.extranonce1 = String((const char*) doc["result"][1]); + mSubscribe.extranonce2_size = doc["result"][2]; + + return true; +} + +mining_subscribe init_mining_subscribe(void) +{ + mining_subscribe new_mSub; + + new_mSub.extranonce1 = ""; + new_mSub.extranonce2 = ""; + new_mSub.extranonce2_size = 0; + new_mSub.sub_details = ""; + + + return new_mSub; +} + +// STEP 2: Pool server auth (authorize) +bool tx_mining_auth(WiFiClient& client, const char * user, const char * pass) +{ + char payload[BUFFER] = {0}; + + // Authorize + id = getNextId(id); + sprintf(payload, "{\"params\": [\"%s\", \"%s\"], \"id\": %u, \"method\": \"mining.authorize\"}\n", + user, pass, id); + + Serial.printf("[WORKER] ==> Autorize work\n"); + Serial.print(" Sending : "); Serial.println(payload); + client.print(payload); + + vTaskDelay(200 / portTICK_PERIOD_MS); //Small delay + + //Don't parse here any answer + //Miner started to receive mining notifications so better parse all at main thread + + return true; +} + +stratum_method parse_mining_method(String line) +{ + if(!verifyPayload(&line)) return STRATUM_PARSE_ERROR; + Serial.print(" Receiving: "); Serial.println(line); + + DeserializationError error = deserializeJson(doc, line); + + if (error || checkError(doc)) return STRATUM_PARSE_ERROR; + + if (!doc.containsKey("method")) return STRATUM_UNKNOWN; + + stratum_method result = STRATUM_UNKNOWN; + + if (strcmp("mining.notify", (const char*) doc["method"]) == 0) { + result = MINING_NOTIFY; + } else if (strcmp("mining.set_difficulty", (const char*) doc["method"]) == 0) { + result = MINING_SET_DIFFICULTY; + } + + return result; +} + +bool parse_mining_notify(String line, mining_job& mJob) +{ + Serial.println(" Parsing Method [MINING NOTIFY]"); + if(!verifyPayload(&line)) return false; + + DeserializationError error = deserializeJson(doc, line); + + if (error) return false; + if (!doc.containsKey("params")) return false; + + mJob.job_id = String((const char*) doc["params"][0]); + mJob.prev_block_hash = String((const char*) doc["params"][1]); + mJob.coinb1 = String((const char*) doc["params"][2]); + mJob.coinb2 = String((const char*) doc["params"][3]); + mJob.merkle_branch = doc["params"][4]; + mJob.version = String((const char*) doc["params"][5]); + mJob.nbits = String((const char*) doc["params"][6]); + mJob.ntime = String((const char*) doc["params"][7]); + mJob.clean_jobs = doc["params"][8]; //bool + + #ifdef DEBUG_MINING + Serial.print(" job_id: "); Serial.println(job_id); + Serial.print(" prevhash: "); Serial.println(prev_block_hash); + Serial.print(" coinb1: "); Serial.println(coinb1); + Serial.print(" coinb2: "); Serial.println(coinb2); + Serial.print(" merkle_branch size: "); Serial.println(merkle_branches.size()); + Serial.print(" version: "); Serial.println(version); + Serial.print(" nbits: "); Serial.println(nbits); + Serial.print(" ntime: "); Serial.println(ntime); + Serial.print(" clean_jobs: "); Serial.println(clean_jobs); + #endif + //Check if parameters where correctly received + if (checkError(doc)) { + Serial.printf("[WORKER] >>>>>>>>> Work aborted\n"); + return false; + } + return true; +} + + +bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job mJob, unsigned long nonce) +{ + char payload[BUFFER] = {0}; + + // Submit + id = getNextId(id); + sprintf(payload, "{\"id\": %u, \"method\": \"mining.submit\", \"params\": [\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"]}\n", + id, + "bc1qvv469gmw4zz6qa4u4dsezvrlmqcqszwyfzhgwj", //mWorker.name, + mJob.job_id, + mWorker.extranonce2, + mJob.ntime, + String(nonce, HEX), + id + ); + Serial.print(" Sending : "); Serial.print(payload); + client.print(payload); + //Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); + + return true; +} + +bool parse_mining_set_difficulty(String line, float& difficulty) +{ + Serial.println(" Parsing Method [SET DIFFICULTY]"); + if(!verifyPayload(&line)) return false; + + DeserializationError error = deserializeJson(doc, line); + + if (error) return false; + if (!doc.containsKey("params")) return false; + + Serial.print(" difficulty: "); Serial.println((const char *)doc["params"][0]); + //difficulty = (float) doc["params"][0]; + + #ifdef DEBUG_MINING + Serial.print(" job_id: "); Serial.println(job_id); + #endif + + return true; +} + +/* +int suggest_difficulty(int socket, uint32_t difficulty) +{ + char difficulty_msg[BUFFER_SIZE]; + sprintf(difficulty_msg, "{\"id\": %d, \"method\": \"mining.suggest_difficulty\", \"params\": [%d]}\n", send_uid++, difficulty); + ESP_LOGI(TAG, "-> %s", difficulty_msg); + write(socket, difficulty_msg, strlen(difficulty_msg)); + char * line; + line = receive_jsonrpc_line(socket); + + ESP_LOGI(TAG, "Received result %s", line); + + free(line); + + return 1; +} +*/ \ No newline at end of file diff --git a/src/stratum.h b/src/stratum.h new file mode 100644 index 0000000..78573a2 --- /dev/null +++ b/src/stratum.h @@ -0,0 +1,67 @@ +#ifndef STRATUM_API_H +#define STRATUM_API_H + +#include "cJSON.h" +#include +#include +#include +#include + +#define MAX_MERKLE_BRANCHES 32 +#define HASH_SIZE 32 +#define COINBASE_SIZE 100 +#define COINBASE2_SIZE 128 + +#define BUFFER_JSON_DOC 4096 +#define BUFFER 1024 + +typedef struct { + String sub_details; + String extranonce1; + String extranonce2; + int extranonce2_size; + String wName; + String wPass; +} mining_subscribe; + +typedef struct { + String job_id; + String prev_block_hash; + String coinb1; + String coinb2; + String nbits; + JsonArray merkle_branch; + String version; + uint32_t target; + String ntime; + bool clean_jobs; +} mining_job; + +typedef enum { + STRATUM_UNKNOWN, + STRATUM_PARSE_ERROR, + MINING_NOTIFY, + MINING_SET_DIFFICULTY +} stratum_method; + +unsigned long getNextId(unsigned long id); +bool verifyPayload (String* line); +bool checkError(const StaticJsonDocument doc); + +//Method Mining.subscribe +mining_subscribe init_mining_subscribe(void); +bool tx_mining_subscribe(WiFiClient& client, mining_subscribe& mSubscribe); +bool parse_mining_subscribe(String line, mining_subscribe& mSubscribe); + +//Method Mining.authorise +bool tx_mining_auth(WiFiClient& client, const char * user, const char * pass); +stratum_method parse_mining_method(String line); +bool parse_mining_notify(String line, mining_job& mJob); + +//Method Mining.submit +bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job mJob, unsigned long nonce); + +//Method Mining.set_difficulty +bool parse_mining_set_difficulty(String line, float& difficulty); + +#endif // STRATUM_API_H \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..e9acdd8 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,366 @@ + +#include +#include "utils.h" +#include "mining.h" +#include "stratum.h" +#include "mbedtls/sha256.h" + +#include +#include + +#ifndef bswap_16 +#define bswap_16(a) ((((uint16_t) (a) << 8) & 0xff00) | (((uint16_t) (a) >> 8) & 0xff)) +#endif + +#ifndef bswap_32 +#define bswap_32(a) ((((uint32_t) (a) << 24) & 0xff000000) | \ + (((uint32_t) (a) << 8) & 0xff0000) | \ + (((uint32_t) (a) >> 8) & 0xff00) | \ + (((uint32_t) (a) >> 24) & 0xff)) +#endif + +uint32_t swab32(uint32_t v) { + return bswap_32(v); +} + +uint8_t hex(char ch) { + uint8_t r = (ch > 57) ? (ch - 55) : (ch - 48); + return r & 0x0F; +} + +int to_byte_array(const char *in, size_t in_size, uint8_t *out) { + int count = 0; + if (in_size % 2) { + while (*in && out) { + *out = hex(*in++); + if (!*in) + return count; + *out = (*out << 4) | hex(*in++); + *out++; + count++; + } + return count; + } else { + while (*in && out) { + *out++ = (hex(*in++) << 4) | hex(*in++); + count++; + } + return count; + } +} + +void swap_endian_words(const char * hex_words, uint8_t * output) { + size_t hex_length = strlen(hex_words); + if (hex_length % 8 != 0) { + fprintf(stderr, "Must be 4-byte word aligned\n"); + exit(EXIT_FAILURE); + } + + size_t binary_length = hex_length / 2; + + for (size_t i = 0; i < binary_length; i += 4) { + for (int j = 0; j < 4; j++) { + unsigned int byte_val; + sscanf(hex_words + (i + j) * 2, "%2x", &byte_val); + output[i + (3 - j)] = byte_val; + } + } +} + +void reverse_bytes(uint8_t * data, size_t len) { + for (int i = 0; i < len / 2; ++i) { + uint8_t temp = data[i]; + data[i] = data[len - 1 - i]; + data[len - 1 - i] = temp; + } +} + +static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0; +/* Converts a little endian 256 bit value to a double */ +double le256todouble(const void *target) +{ + uint64_t *data64; + double dcut64; + + data64 = (uint64_t *)(target + 24); + dcut64 = bswap64(*data64) * 6277101735386680763835789423207666416102355444464034512896.0; + + data64 = (uint64_t *)(target + 16); + dcut64 += bswap64(*data64) * 340282366920938463463374607431768211456.0; + + data64 = (uint64_t *)(target + 8); + dcut64 += bswap64(*data64) * 18446744073709551616.0; + + data64 = (uint64_t *)(target); + dcut64 += bswap64(*data64); + + return dcut64; +} + +double diff_from_target(void *target) +{ + double d64, dcut64; + //reverse_bytes((uint8_t *) target, 32); + + d64 = truediffone; + dcut64 = le256todouble(target); + if (unlikely(!dcut64)) + dcut64 = 1; + return d64 / dcut64; +} + +/****************** PREMINING CALCULATIONS ********************/ + + +bool checkValid(unsigned char* hash, unsigned char* target) { + bool valid = true; + for(uint8_t i=31; i>=0; i--) { + if(hash[i] > target[i]) { + valid = false; + break; + } else if (hash[i] < target[i]) { + valid = true; + break; + } + } + #ifdef DEBUG_MINING + if (valid) { + Serial.print("\tvalid : "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x ", hash[i]); + Serial.println(); + } + #endif + return valid; +} + +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; +} + +miner_data init_miner_data(void){ + + miner_data newMinerData; + + newMinerData.difficulty = DEFAULT_DIFFICULTY; + newMinerData.inRun = false; + newMinerData.newJob = false; + + return newMinerData; +} + +miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ + + miner_data mMiner = init_miner_data(); + + // calculate target - target = (nbits[2:]+'00'*(int(nbits[:2],16) - 3)).zfill(64) + + char target[TARGET_BUFFER_SIZE+1]; + memset(target, '0', TARGET_BUFFER_SIZE); + int zeros = (int) strtol(mJob.nbits.substring(0, 2).c_str(), 0, 16) - 3; + memcpy(target + zeros - 2, mJob.nbits.substring(2).c_str(), mJob.nbits.length() - 2); + target[TARGET_BUFFER_SIZE] = 0; + Serial.print(" target: "); Serial.println(target); + + // bytearray target + size_t size_target = to_byte_array(target, 32, mMiner.bytearray_target); + + for (size_t j = 0; j < 8; j++) { + mMiner.bytearray_target[j] ^= mMiner.bytearray_target[size_target - 1 - j]; + mMiner.bytearray_target[size_target - 1 - j] ^= mMiner.bytearray_target[j]; + mMiner.bytearray_target[j] ^= mMiner.bytearray_target[size_target - 1 - j]; + } + + // get extranonce2 - extranonce2 = hex(random.randint(0,2**32-1))[2:].zfill(2*extranonce2_size) + //To review + char extranonce2_char[2 * mWorker.extranonce2_size+1]; + mWorker.extranonce2.toCharArray(extranonce2_char, 2 * mWorker.extranonce2_size + 1); + getNextExtranonce2(mWorker.extranonce2_size, extranonce2_char); + mWorker.extranonce2 = String(extranonce2_char); + //mWorker.extranonce2 = "00000002"; + + //get coinbase - coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest() + String coinbase = mJob.coinb1 + mWorker.extranonce1 + mWorker.extranonce2 + mJob.coinb2; + Serial.print(" coinbase: "); Serial.println(coinbase); + size_t str_len = coinbase.length()/2; + uint8_t bytearray[str_len]; + + size_t res = to_byte_array(coinbase.c_str(), str_len*2, bytearray); + + #ifdef DEBUG_MINING + Serial.print(" extranonce2: "); Serial.println(extranonce2); + 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.println("---"); + #endif + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + + byte interResult[32]; // 256 bit + byte shaResult[32]; // 256 bit + + mbedtls_sha256_starts_ret(&ctx,0); + mbedtls_sha256_update_ret(&ctx, bytearray, str_len); + 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); + mbedtls_sha256_free(&ctx); + + #ifdef DEBUG_MINING + Serial.print(" coinbase double sha: "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", shaResult[i]); + Serial.println(""); + #endif + + + // copy coinbase hash + memcpy(mMiner.merkle_result, shaResult, sizeof(shaResult)); + + byte merkle_concatenated[32 * 2]; + for (size_t k=0; k < mJob.merkle_branch.size(); k++) { + const char* merkle_element = (const char*) mJob.merkle_branch[k]; + uint8_t bytearray[32]; + size_t res = to_byte_array(merkle_element, 64, bytearray); + + #ifdef DEBUG_MINING + Serial.print(" merkle element "); Serial.print(k); Serial.print(": "); Serial.println(merkle_element); + #endif + for (size_t i = 0; i < 32; i++) { + merkle_concatenated[i] = mMiner.merkle_result[i]; + merkle_concatenated[32 + i] = bytearray[i]; + } + + #ifdef DEBUG_MINING + Serial.print(" merkle element "); Serial.print(k); Serial.print(": "); Serial.println(merkle_element); + Serial.print(" merkle concatenated: "); + for (size_t i = 0; i < 64; i++) + Serial.printf("%02x", merkle_concatenated[i]); + Serial.println(""); + #endif + + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + mbedtls_sha256_starts_ret(&ctx,0); + mbedtls_sha256_update_ret(&ctx, merkle_concatenated, 64); + mbedtls_sha256_finish_ret(&ctx, interResult); + + mbedtls_sha256_starts_ret(&ctx,0); + mbedtls_sha256_update_ret(&ctx, interResult, 32); + mbedtls_sha256_finish_ret(&ctx, mMiner.merkle_result); + mbedtls_sha256_free(&ctx); + + #ifdef DEBUG_MINING + Serial.print(" merkle sha : "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", merkle_result[i]); + Serial.println(""); + #endif + } + // merkle root from merkle_result + + Serial.print(" merkle sha : "); + char merkle_root[65]; + for (int i = 0; i < 32; i++) { + Serial.printf("%02x", mMiner.merkle_result[i]); + snprintf(&merkle_root[i*2], 3, "%02x", mMiner.merkle_result[i]); + } + merkle_root[65] = 0; + Serial.println(""); + + // calculate blockheader + // j.block_header = ''.join([j.version, j.prevhash, merkle_root, j.ntime, j.nbits]) + String blockheader = mJob.version + mJob.prev_block_hash + String(merkle_root) + mJob.ntime + mJob.nbits + "00000000"; + str_len = blockheader.length()/2; + + //uint8_t bytearray_blockheader[str_len]; + res = to_byte_array(blockheader.c_str(), str_len*2, mMiner.bytearray_blockheader); + + #ifdef DEBUG_MINING + Serial.println(" blockheader bytes "); Serial.print(str_len); Serial.print(" -> "); + #endif + + // reverse version + uint8_t buff; + size_t bsize, boffset; + boffset = 0; + bsize = 4; + for (size_t j = boffset; j < boffset + (bsize/2); j++) { + buff = mMiner.bytearray_blockheader[j]; + mMiner.bytearray_blockheader[j] = mMiner.bytearray_blockheader[2 * boffset + bsize - 1 - j]; + mMiner.bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; + } + + // reverse merkle + boffset = 36; + bsize = 32; + for (size_t j = boffset; j < boffset + (bsize/2); j++) { + buff = mMiner.bytearray_blockheader[j]; + mMiner.bytearray_blockheader[j] = mMiner.bytearray_blockheader[2 * boffset + bsize - 1 - j]; + mMiner.bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; + } + // reverse difficulty + boffset = 72; + bsize = 4; + for (size_t j = boffset; j < boffset + (bsize/2); j++) { + buff = mMiner.bytearray_blockheader[j]; + mMiner.bytearray_blockheader[j] = mMiner.bytearray_blockheader[2 * boffset + bsize - 1 - j]; + mMiner.bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; + } + + + #ifdef DEBUG_MINING + Serial.print(" >>> bytearray_blockheader : "); + for (size_t i = 0; i < 4; i++) + Serial.printf("%02x", bytearray_blockheader[i]); + Serial.println(""); + Serial.print("version "); + for (size_t i = 0; i < 4; i++) + Serial.printf("%02x", bytearray_blockheader[i]); + Serial.println(""); + Serial.print("prev hash "); + for (size_t i = 4; i < 4+32; i++) + Serial.printf("%02x", bytearray_blockheader[i]); + Serial.println(""); + Serial.print("merkle root "); + for (size_t i = 36; i < 36+32; i++) + Serial.printf("%02x", bytearray_blockheader[i]); + Serial.println(""); + Serial.print("nbits "); + for (size_t i = 68; i < 68+4; i++) + Serial.printf("%02x", bytearray_blockheader[i]); + Serial.println(""); + Serial.print("difficulty "); + for (size_t i = 72; i < 72+4; i++) + Serial.printf("%02x", bytearray_blockheader[i]); + Serial.println(""); + Serial.print("nonce "); + for (size_t i = 76; i < 76+4; i++) + Serial.printf("%02x", bytearray_blockheader[i]); + Serial.println(""); + Serial.println("bytearray_blockheader: "); + for (size_t i = 0; i < str_len; i++) { + Serial.printf("%02x", bytearray_blockheader[i]); + } + Serial.println(""); + #endif + return mMiner; +} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..4dcac22 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,31 @@ +#ifndef UTILS_API_H +#define UTILS_API_H + +#include +#include +#include "mining.h" +#include "stratum.h" + +/* + * General byte order swapping functions. + */ +#define bswap16(x) __bswap16(x) +#define bswap32(x) __bswap32(x) +#define bswap64(x) __bswap64(x) + + + +uint8_t hex(char ch); + +int to_byte_array(const char *in, size_t in_size, uint8_t *out); + +double le256todouble(const void *target); + +double diff_from_target(void *target); + +miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob); + +bool checkValid(unsigned char* hash, unsigned char* target); + + +#endif // UTILS_API_H \ No newline at end of file diff --git a/src/wManager.cpp b/src/wManager.cpp index 0273514..d93dff7 100644 --- a/src/wManager.cpp +++ b/src/wManager.cpp @@ -143,6 +143,8 @@ void init_WifiManager() // Change to true when testing to force configuration every time we run bool forceConfig = false; + // Check if button2 is pressed to enter configMode with actual configuration + if(!digitalRead(PIN_BUTTON_2)) forceConfig = true; bool spiffsSetup = loadConfigFile(); if (!spiffsSetup) @@ -273,8 +275,23 @@ void reset_configurations() { } +//----------------- MAIN PROCESS WIFI MANAGER -------------- +int oldStatus = 0; + void wifiManagerProcess() { + wm.process(); // avoid delays() in loop when non-blocking and other long running code + + int newStatus = WiFi.status(); + if (newStatus != oldStatus) { + if (newStatus == WL_CONNECTED) { + Serial.println("CONNECTED - Current ip: " + WiFi.localIP().toString()); + } else { + Serial.print("[Error] - current status: "); + Serial.println(newStatus); + } + oldStatus = newStatus; + } }