From 146d0497d64d3b0e7d8d2b49c16d6a560f6a1a6e Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 11 Jul 2023 17:12:02 +0200 Subject: [PATCH 01/14] Add watchdog timer to miner task Set a 120s timeout for the watchdog to catch a starving miner task. If the miner task fails to reset the watchdog, the ESP32 will reboot. --- src/NerdMinerV2.ino.cpp | 12 +++++++++--- src/mining.cpp | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index 82d3425..3040fcc 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -20,6 +20,8 @@ //3 seconds WDT #define WDT_TIMEOUT 3 +//120 seconds WDT for miner task +#define WDT_MINER_TIMEOUT 120 OneButton button1(PIN_BUTTON_1); OneButton button2(PIN_BUTTON_2); @@ -56,6 +58,7 @@ void setup() Serial.setTimeout(0); delay(100); + esp_task_wdt_init(WDT_MINER_TIMEOUT, true); // Idle task that would reset WDT never runs, because core 0 gets fully utilized disableCore0WDT(); //disableCore1WDT(); @@ -137,8 +140,11 @@ void setup() // Start mining tasks //BaseType_t res = xTaskCreate(runWorker, name, 35000, (void*)name, 1, NULL); - xTaskCreate(runMiner, "Miner0", 15000, NULL, 1, NULL); - xTaskCreate(runMiner, "Miner1", 15000, NULL, 1, NULL); + TaskHandle_t minerTask1, minerTask2 = NULL; + xTaskCreate(runMiner, "Miner0", 15000, NULL, 1, &minerTask1); + xTaskCreate(runMiner, "Miner1", 15000, NULL, 1, &minerTask2); + esp_task_wdt_add(minerTask1); + esp_task_wdt_add(minerTask2); /******** MONITOR SETUP *****/ setup_monitor(); @@ -164,4 +170,4 @@ void loop() { wifiManagerProcess(); // avoid delays() in loop when non-blocking and other long running code vTaskDelay(50 / portTICK_PERIOD_MS); -} \ No newline at end of file +} diff --git a/src/mining.cpp b/src/mining.cpp index f272b95..fce6e90 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include // Graphics and font library for ILI9341 driver chip #include #include "media/Free_Fonts.h" @@ -338,6 +339,8 @@ void runMiner(void * name){ } uint32_t duration = micros() - startT; + if (esp_task_wdt_reset() == ESP_OK) + Serial.print(">>> Resetting watchdog timer"); } } From 57c60ed0f1eee0d27aba16ae4842e62e88259ed0 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 11 Jul 2023 21:58:53 +0200 Subject: [PATCH 02/14] Rework two-threaded miner task code Distribute odd and even nonces between miner tasks based on task_id --- src/NerdMinerV2.ino.cpp | 4 ++-- src/mining.cpp | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index 3040fcc..9ccd5a7 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -141,8 +141,8 @@ void setup() // Start mining tasks //BaseType_t res = xTaskCreate(runWorker, name, 35000, (void*)name, 1, NULL); TaskHandle_t minerTask1, minerTask2 = NULL; - xTaskCreate(runMiner, "Miner0", 15000, NULL, 1, &minerTask1); - xTaskCreate(runMiner, "Miner1", 15000, NULL, 1, &minerTask2); + xTaskCreate(runMiner, "Miner0", 15000, (void*)0, 1, &minerTask1); + xTaskCreate(runMiner, "Miner1", 15000, (void*)1, 1, &minerTask2); esp_task_wdt_add(minerTask1); esp_task_wdt_add(minerTask2); diff --git a/src/mining.cpp b/src/mining.cpp index fce6e90..5fe645f 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -214,9 +214,9 @@ void runStratumWorker(void *name) { #include "shaTests/jadeSHA256.h" #include "shaTests/customSHA256.h" #include "mbedtls/sha256.h" -void runMiner(void * name){ - unsigned long nonce; - unsigned long max_nonce; +void runMiner(void * task_id) { + + unsigned int miner_id = (uint32_t)task_id; while(1){ @@ -227,16 +227,10 @@ void runMiner(void * name){ } vTaskDelay(10 / portTICK_PERIOD_MS); //Small delay to join both mining threads - if(mMiner.newJob) { + if(mMiner.newJob) mMiner.newJob = false; //Clear newJob flag - nonce = 0; - max_nonce = MAX_NONCE; - } - else if(mMiner.newJob2){ + else if(mMiner.newJob2) mMiner.newJob2 = false; //Clear newJob flag - nonce = TARGET_NONCE - MAX_NONCE; - max_nonce = TARGET_NONCE; - } mMiner.inRun = true; //Set inRun flag //Prepare Premining data @@ -259,6 +253,9 @@ void runMiner(void * name){ Serial.println(""); */ // search a valid nonce + unsigned long nonce = TARGET_NONCE - MAX_NONCE; + // split up odd/even nonces between miner tasks + nonce += miner_id; uint32_t startT = micros(); unsigned char *header64 = mMiner.bytearray_blockheader + 64; Serial.println(">>> STARTING TO HASH NONCES"); @@ -284,7 +281,8 @@ void runMiner(void * name){ Serial.println(""); */ hashes++; - if (nonce++> max_nonce) break; //exit + nonce += 2; + if (nonce > TARGET_NONCE) break; //exit if(!mMiner.inRun) { Serial.println ("MINER WORK ABORTED >> waiting new job"); break;} // check if 16bit share @@ -315,9 +313,9 @@ void runMiner(void * name){ // 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); + Serial.printf("[WORKER] %d CONGRATULATIONS! Valid block found with nonce: %d | 0x%x\n", miner_id, nonce, nonce); valids++; - Serial.printf("[WORKER] %s Submiting work valid!\n", (char *)name); + Serial.printf("[WORKER] %d Submitted work valid!\n", miner_id); // STEP 3: Submit mining job tx_mining_submit(client, mWorker, mJob, nonce); client.stop(); From 3872bd7a086d49e0666e41028522eaad453aeb6e Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 13 Jul 2023 15:41:58 +0200 Subject: [PATCH 03/14] Force re-connect when losing WiFi Apparently the ESP32 does not auto re-connect to WiFi, so enforce it. --- src/mining.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mining.cpp b/src/mining.cpp index 5fe645f..656eea1 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -116,7 +116,9 @@ void runStratumWorker(void *name) { while(true) { if(WiFi.status() != WL_CONNECTED){ - vTaskDelay(1000 / portTICK_PERIOD_MS); + // WiFi is disconnected, so reconnect now + WiFi.reconnect(); + vTaskDelay(5000 / portTICK_PERIOD_MS); continue; } From f15715f3f34d26774ecae4c7705f21f6e309c053 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 20 Jul 2023 12:50:35 +0200 Subject: [PATCH 04/14] Use double precision for pool difficulty --- src/mining.cpp | 2 +- src/mining.h | 4 ++-- src/stratum.cpp | 10 +++++----- src/stratum.h | 4 ++-- src/utils.cpp | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mining.cpp b/src/mining.cpp index 656eea1..5e208c3 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -111,7 +111,7 @@ void runStratumWorker(void *name) { // connect to pool - float currentPoolDifficulty = atof(DEFAULT_DIFFICULTY); + double currentPoolDifficulty = DEFAULT_DIFFICULTY; while(true) { diff --git a/src/mining.h b/src/mining.h index 742b3e6..6a4c8f8 100644 --- a/src/mining.h +++ b/src/mining.h @@ -5,7 +5,7 @@ // Mining #define MAX_NONCE 5000000U #define TARGET_NONCE 471136297U -#define DEFAULT_DIFFICULTY "1e-9" +#define DEFAULT_DIFFICULTY 1e-9 #define KEEPALIVE_TIME_ms 30000 #define POOLINACTIVITY_TIME_ms 60000 @@ -21,7 +21,7 @@ typedef struct{ uint8_t bytearray_pooltarget[32]; uint8_t merkle_result[32]; uint8_t bytearray_blockheader[80]; - float poolDifficulty; + double poolDifficulty; bool inRun; bool newJob; bool newJob2; diff --git a/src/stratum.cpp b/src/stratum.cpp index dacaaaf..a3c9a0e 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -216,7 +216,7 @@ bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job m return true; } -bool parse_mining_set_difficulty(String line, float& difficulty) +bool parse_mining_set_difficulty(String line, double& difficulty) { Serial.println(" Parsing Method [SET DIFFICULTY]"); if(!verifyPayload(&line)) return false; @@ -226,8 +226,8 @@ bool parse_mining_set_difficulty(String line, float& difficulty) if (error) return false; if (!doc.containsKey("params")) return false; - Serial.print(" difficulty: "); Serial.println((float)doc["params"][0],12); - difficulty = (float)doc["params"][0]; + Serial.print(" difficulty: "); Serial.println((double)doc["params"][0],12); + difficulty = (double)doc["params"][0]; #ifdef DEBUG_MINING Serial.print(" job_id: "); Serial.println(job_id); @@ -236,12 +236,12 @@ bool parse_mining_set_difficulty(String line, float& difficulty) return true; } -bool tx_suggest_difficulty(WiFiClient& client, const char * difficulty) +bool tx_suggest_difficulty(WiFiClient& client, double difficulty) { char payload[BUFFER] = {0}; id = getNextId(id); - sprintf(payload, "{\"id\": %d, \"method\": \"mining.suggest_difficulty\", \"params\": [%s]}\n", id, difficulty); + sprintf(payload, "{\"id\": %d, \"method\": \"mining.suggest_difficulty\", \"params\": [%.10g]}\n", id, difficulty); Serial.print(" Sending : "); Serial.print(payload); return client.print(payload); diff --git a/src/stratum.h b/src/stratum.h index cdd47b3..44f65de 100644 --- a/src/stratum.h +++ b/src/stratum.h @@ -62,8 +62,8 @@ bool parse_mining_notify(String line, mining_job& mJob); bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job mJob, unsigned long nonce); //Difficulty Methods -bool tx_suggest_difficulty(WiFiClient& client, const char * difficulty); -bool parse_mining_set_difficulty(String line, float& difficulty); +bool tx_suggest_difficulty(WiFiClient& client, double difficulty); +bool parse_mining_set_difficulty(String line, double& difficulty); #endif // STRATUM_API_H \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp index 3050dc8..f7e95ab 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -155,7 +155,7 @@ miner_data init_miner_data(void){ miner_data newMinerData; - newMinerData.poolDifficulty = atof(DEFAULT_DIFFICULTY); + newMinerData.poolDifficulty = DEFAULT_DIFFICULTY; newMinerData.inRun = false; newMinerData.newJob = false; From 65b08c47332a36f4bdc7c7e9af0065cc7d0c1aba Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 20 Jul 2023 18:45:29 +0200 Subject: [PATCH 05/14] Fix debug logging --- src/stratum.cpp | 22 +++++++++------------- src/utils.cpp | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/stratum.cpp b/src/stratum.cpp index a3c9a0e..4970656 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -176,15 +176,15 @@ bool parse_mining_notify(String line, mining_job& mJob) 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); + Serial.print(" job_id: "); Serial.println(mJob.job_id); + Serial.print(" prevhash: "); Serial.println(mJob.prev_block_hash); + Serial.print(" coinb1: "); Serial.println(mJob.coinb1); + Serial.print(" coinb2: "); Serial.println(mJob.coinb2); + Serial.print(" merkle_branch size: "); Serial.println(mJob.merkle_branch.size()); + Serial.print(" version: "); Serial.println(mJob.version); + Serial.print(" nbits: "); Serial.println(mJob.nbits); + Serial.print(" ntime: "); Serial.println(mJob.ntime); + Serial.print(" clean_jobs: "); Serial.println(mJob.clean_jobs); #endif //Check if parameters where correctly received if (checkError(doc)) { @@ -229,10 +229,6 @@ bool parse_mining_set_difficulty(String line, double& difficulty) Serial.print(" difficulty: "); Serial.println((double)doc["params"][0],12); difficulty = (double)doc["params"][0]; - #ifdef DEBUG_MINING - Serial.print(" job_id: "); Serial.println(job_id); - #endif - return true; } diff --git a/src/utils.cpp b/src/utils.cpp index f7e95ab..1291bdd 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -201,7 +201,7 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ size_t res = to_byte_array(coinbase.c_str(), str_len*2, bytearray); #ifdef DEBUG_MINING - Serial.print(" extranonce2: "); Serial.println(extranonce2); + Serial.print(" extranonce2: "); Serial.println(mWorker.extranonce2); Serial.print(" coinbase: "); Serial.println(coinbase); Serial.print(" coinbase bytes - size: "); Serial.println(res); for (size_t i = 0; i < res; i++) @@ -271,7 +271,7 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ #ifdef DEBUG_MINING Serial.print(" merkle sha : "); for (size_t i = 0; i < 32; i++) - Serial.printf("%02x", merkle_result[i]); + Serial.printf("%02x", mMiner.merkle_result[i]); Serial.println(""); #endif } @@ -330,35 +330,35 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ #ifdef DEBUG_MINING Serial.print(" >>> bytearray_blockheader : "); for (size_t i = 0; i < 4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); + Serial.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); Serial.print("version "); for (size_t i = 0; i < 4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); + Serial.printf("%02x", mMiner.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.printf("%02x", mMiner.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.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); Serial.print("nbits "); for (size_t i = 68; i < 68+4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); + Serial.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); Serial.print("difficulty "); for (size_t i = 72; i < 72+4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); + Serial.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); Serial.print("nonce "); for (size_t i = 76; i < 76+4; i++) - Serial.printf("%02x", bytearray_blockheader[i]); + Serial.printf("%02x", mMiner.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.printf("%02x", mMiner.bytearray_blockheader[i]); } Serial.println(""); #endif From 3a0cb9a83c1bc7b5d1b98b137e3d194fcc03dd27 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 23 Jul 2023 00:26:19 +0200 Subject: [PATCH 06/14] Major Nerdminer code rework * Rework block header generation * Fix dual-threaded mining code * Fix share diff calculation * Set default difficulty to 1e-4 --- src/mining.cpp | 40 +++++++++++++++++++++++++++++++------- src/mining.h | 3 ++- src/utils.cpp | 52 ++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/mining.cpp b/src/mining.cpp index 5e208c3..39fb323 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -259,10 +259,19 @@ void runMiner(void * task_id) { // split up odd/even nonces between miner tasks nonce += miner_id; uint32_t startT = micros(); - unsigned char *header64 = mMiner.bytearray_blockheader + 64; + unsigned char *header64; + // each miner thread needs to track its own blockheader template + memcpy(mMiner.bytearray_blockheader2, &mMiner.bytearray_blockheader, 80); + if (miner_id == 0) + header64 = mMiner.bytearray_blockheader + 64; + else + header64 = mMiner.bytearray_blockheader2 + 64; Serial.println(">>> STARTING TO HASH NONCES"); while(true) { - memcpy(mMiner.bytearray_blockheader + 76, &nonce, 4); + if (miner_id == 0) + memcpy(mMiner.bytearray_blockheader + 76, &nonce, 4); + else + memcpy(mMiner.bytearray_blockheader2 + 76, &nonce, 4); //Con midstate // Primer SHA-256 @@ -283,12 +292,15 @@ void runMiner(void * task_id) { Serial.println(""); */ hashes++; - nonce += 2; 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; + if(hash[31] !=0 || hash[30] !=0) { + // increment nonce + nonce += 2; + continue; + } halfshares++; //Check target to submit @@ -305,12 +317,24 @@ void runMiner(void * task_id) { Serial.print(" - TX SHARE: "); for (size_t i = 0; i < 32; i++) Serial.printf("%02x", hash[i]); - Serial.println(""); + #ifdef DEBUG_MINING + Serial.println(""); + Serial.print(" - Current nonce: "); Serial.println(nonce); + Serial.print(" - Current block header: "); + for (size_t i = 0; i < 80; i++) { + Serial.printf("%02x", mMiner.bytearray_blockheader[i]); + } + #endif + Serial.println(""); mLastTXtoPool = millis(); } // check if 32bit share - if(hash[29] !=0 || hash[28] !=0) continue; + if(hash[29] !=0 || hash[28] !=0) { + // increment nonce + nonce += 2; + continue; + } shares++; // check if valid header @@ -324,7 +348,9 @@ void runMiner(void * task_id) { // exit nonce = MAX_NONCE; break; - } + } + // increment nonce + nonce += 2; } // exit if found a valid result or nonce > MAX_NONCE wc_Sha256Free(&sha256); diff --git a/src/mining.h b/src/mining.h index 6a4c8f8..4cb28f5 100644 --- a/src/mining.h +++ b/src/mining.h @@ -5,7 +5,7 @@ // Mining #define MAX_NONCE 5000000U #define TARGET_NONCE 471136297U -#define DEFAULT_DIFFICULTY 1e-9 +#define DEFAULT_DIFFICULTY 1e-4 #define KEEPALIVE_TIME_ms 30000 #define POOLINACTIVITY_TIME_ms 60000 @@ -21,6 +21,7 @@ typedef struct{ uint8_t bytearray_pooltarget[32]; uint8_t merkle_result[32]; uint8_t bytearray_blockheader[80]; + uint8_t bytearray_blockheader2[80]; double poolDifficulty; bool inRun; bool newJob; diff --git a/src/utils.cpp b/src/utils.cpp index 1291bdd..8216ef7 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -83,16 +83,16 @@ double le256todouble(const void *target) double dcut64; data64 = (uint64_t *)(target + 24); - dcut64 = bswap64(*data64) * 6277101735386680763835789423207666416102355444464034512896.0; + dcut64 = *data64 * 6277101735386680763835789423207666416102355444464034512896.0; data64 = (uint64_t *)(target + 16); - dcut64 += bswap64(*data64) * 340282366920938463463374607431768211456.0; + dcut64 += *data64 * 340282366920938463463374607431768211456.0; data64 = (uint64_t *)(target + 8); - dcut64 += bswap64(*data64) * 18446744073709551616.0; + dcut64 += *data64 * 18446744073709551616.0; data64 = (uint64_t *)(target); - dcut64 += bswap64(*data64); + dcut64 += *data64; return dcut64; } @@ -100,7 +100,6 @@ double le256todouble(const void *target) double diff_from_target(void *target) { double d64, dcut64; - //reverse_bytes((uint8_t *) target, 32); d64 = truediffone; dcut64 = le256todouble(target); @@ -285,22 +284,23 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ } 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"; + 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: "); Serial.print(blockheader); Serial.println(" blockheader bytes "); Serial.print(str_len); Serial.print(" -> "); #endif // reverse version uint8_t buff; - size_t bsize, boffset; + size_t bword, bsize, boffset; boffset = 0; bsize = 4; for (size_t j = boffset; j < boffset + (bsize/2); j++) { @@ -309,14 +309,42 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ mMiner.bytearray_blockheader[2 * boffset + bsize - 1 - j] = buff; } - // reverse merkle - boffset = 36; + // reverse prev hash (4-byte word swap) + boffset = 4; + bword = 4; bsize = 32; + for (size_t i = 1; i <= bsize / bword; i++) { + for (size_t j = boffset; j < boffset + bword / 2; j++) { + buff = mMiner.bytearray_blockheader[j]; + mMiner.bytearray_blockheader[j] = mMiner.bytearray_blockheader[2 * boffset + bword - 1 - j]; + mMiner.bytearray_blockheader[2 * boffset + bword - 1 - j] = buff; + } + boffset += bword; + } + +/* + // reverse merkle (4-byte word swap) + boffset = 36; + bword = 4; + bsize = 32; + for (size_t i = 1; i <= bsize / bword; i++) { + for (size_t j = boffset; j < boffset + bword / 2; j++) { + buff = mMiner.bytearray_blockheader[j]; + mMiner.bytearray_blockheader[j] = mMiner.bytearray_blockheader[2 * boffset + bword - 1 - j]; + mMiner.bytearray_blockheader[2 * boffset + bword - 1 - j] = buff; + } + boffset += bword; + } +*/ + // reverse ntime + boffset = 68; + 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 difficulty boffset = 72; bsize = 4; @@ -344,11 +372,11 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ for (size_t i = 36; i < 36+32; i++) Serial.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); - Serial.print("nbits "); + Serial.print("ntime "); for (size_t i = 68; i < 68+4; i++) Serial.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); - Serial.print("difficulty "); + Serial.print("nbits "); for (size_t i = 72; i < 72+4; i++) Serial.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); From c0af3f03b141c660f9e1fb543481e8120c952711 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 23 Jul 2023 15:59:19 +0200 Subject: [PATCH 07/14] Disable miner thread includes for now --- src/mining.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mining.cpp b/src/mining.cpp index 39fb323..dc4e5db 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -213,9 +213,9 @@ void runStratumWorker(void *name) { //This works only with one thread, TODO -> Class or miner_data for each thread -#include "shaTests/jadeSHA256.h" -#include "shaTests/customSHA256.h" -#include "mbedtls/sha256.h" +//#include "shaTests/jadeSHA256.h" +//#include "shaTests/customSHA256.h" +//#include "mbedtls/sha256.h" void runMiner(void * task_id) { unsigned int miner_id = (uint32_t)task_id; From d5ac9addfe51b0623c1ef5196f9df821c3ab95be Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 27 Jul 2023 16:30:57 +0200 Subject: [PATCH 08/14] Log successful submit as such --- src/mining.cpp | 1 + src/stratum.cpp | 9 +++++++-- src/stratum.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mining.cpp b/src/mining.cpp index dc4e5db..825952d 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -197,6 +197,7 @@ void runStratumWorker(void *name) { case MINING_SET_DIFFICULTY: parse_mining_set_difficulty(line, currentPoolDifficulty); mMiner.poolDifficulty = currentPoolDifficulty; break; + case STRATUM_SUCCESS: Serial.println(" Parsed JSON: Success"); break; default: Serial.println(" Parsed JSON: unknown"); break; } diff --git a/src/stratum.cpp b/src/stratum.cpp index 4970656..00d72a4 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -142,8 +142,13 @@ stratum_method parse_mining_method(String line) if (error || checkError(doc)) return STRATUM_PARSE_ERROR; - if (!doc.containsKey("method")) return STRATUM_UNKNOWN; - + if (!doc.containsKey("method")) { + // "error":null means success + if (doc["error"].isNull()) + return STRATUM_SUCCESS; + else + return STRATUM_UNKNOWN; + } stratum_method result = STRATUM_UNKNOWN; if (strcmp("mining.notify", (const char*) doc["method"]) == 0) { diff --git a/src/stratum.h b/src/stratum.h index 44f65de..d8110e7 100644 --- a/src/stratum.h +++ b/src/stratum.h @@ -38,6 +38,7 @@ typedef struct { } mining_job; typedef enum { + STRATUM_SUCCESS, STRATUM_UNKNOWN, STRATUM_PARSE_ERROR, MINING_NOTIFY, From df99eea69db5f1f89b8f032b8dba88555fe28193 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 27 Jul 2023 23:24:33 +0200 Subject: [PATCH 09/14] Stop miners instantly when losing pool connection --- src/mining.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mining.cpp b/src/mining.cpp index 825952d..98a4f99 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -141,6 +141,8 @@ void runStratumWorker(void *name) { if(!isMinerSuscribed){ + //Stop miner current jobs + mMiner.inRun = false; mWorker = init_mining_subscribe(); // STEP 1: Pool server connection (SUBSCRIBE) From d61440de25b63c1f863703752bf54a50e9a13db2 Mon Sep 17 00:00:00 2001 From: BitMaker Date: Tue, 25 Jul 2023 20:05:46 +0200 Subject: [PATCH 10/14] Improving custom SHA tests --- test/TestHashPerformance/src/jadeSHA256.cpp | 159 ++++++++++++++++-- .../src/testShaPerformance.cpp | 27 ++- 2 files changed, 168 insertions(+), 18 deletions(-) diff --git a/test/TestHashPerformance/src/jadeSHA256.cpp b/test/TestHashPerformance/src/jadeSHA256.cpp index 4e3073e..cd31a6e 100644 --- a/test/TestHashPerformance/src/jadeSHA256.cpp +++ b/test/TestHashPerformance/src/jadeSHA256.cpp @@ -16,8 +16,6 @@ #define HASH_SIZE 32 -#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) - #ifndef PUT_UINT32_BE #define PUT_UINT32_BE(n, data, offset) \ { \ @@ -105,6 +103,8 @@ static const uint32_t K[] = { }; #define SHR(x, n) ((x & 0xFFFFFFFF) >> n) +//#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTR(x, n) (SHR(x, n) | ((x) << (32 - (n)))) #define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) #define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) @@ -188,7 +188,12 @@ IRAM_ATTR void calc_midstate(uint8_t* buf_ptr, _sha256_context* midstate) uint32_t A[8] = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; uint32_t temp1, temp2, W[64]; + uint8_t i; + /*for (i = 0; i < 16; i++) { + W[i] = GET_UINT32_BE(buf_ptr, 4 * i); + }*/ + W[0] = GET_UINT32_BE(buf_ptr, 0); W[1] = GET_UINT32_BE(buf_ptr, 4); W[2] = GET_UINT32_BE(buf_ptr, 8); @@ -205,7 +210,51 @@ IRAM_ATTR void calc_midstate(uint8_t* buf_ptr, _sha256_context* midstate) W[13] = GET_UINT32_BE(buf_ptr, 52); W[14] = GET_UINT32_BE(buf_ptr, 56); W[15] = GET_UINT32_BE(buf_ptr, 60); + + + for (i = 0; i < 16; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], W[i+0], K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], W[i+1], K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], W[i+2], K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], W[i+3], K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], W[i+4], K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], W[i+5], K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], W[i+6], K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], W[i+7], K[i+7]); + } + + for (i = 16; i < 64; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], R(i+0), K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], R(i+1), K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], R(i+2), K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], R(i+3), K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], R(i+4), K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], R(i+5), K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], R(i+6), K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], R(i+7), K[i+7]); + } + + for (i = 0; i < 8; i++) { + midstate->state[i] += A[i]; + } + /* P(A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[0], K[0]); P(A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[1], K[1]); P(A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[2], K[2]); @@ -271,15 +320,16 @@ IRAM_ATTR void calc_midstate(uint8_t* buf_ptr, _sha256_context* midstate) P(A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(62), K[62]); P(A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(63), K[63]); - midstate->state[0] = A[0];// 0x6A09E667 + A[0]; - midstate->state[1] = A[1];// 0xBB67AE85 + A[1]; - midstate->state[2] = A[2];// 0x3C6EF372 + A[2]; - midstate->state[3] = A[3];// 0xA54FF53A + A[3]; - midstate->state[4] = A[4];// 0x510E527F + A[4]; - midstate->state[5] = A[5];// 0x9B05688C + A[5]; - midstate->state[6] = A[6];// 0x1F83D9AB + A[6]; - midstate->state[7] = A[7];// 0x5BE0CD19 + A[7]; - midstate->buffer[16] = 0x80; + midstate->state[0] = 0x6A09E667 + A[0]; + midstate->state[1] = 0xBB67AE85 + A[1]; + midstate->state[2] = 0x3C6EF372 + A[2]; + midstate->state[3] = 0xA54FF53A + A[3]; + midstate->state[4] = 0x510E527F + A[4]; + midstate->state[5] = 0x9B05688C + A[5]; + midstate->state[6] = 0x1F83D9AB + A[6]; + midstate->state[7] = 0x5BE0CD19 + A[7]; + */ + //midstate->buffer[16] = 0x80; memcpy(midstate->buffer, buf_ptr + 64, 12); } @@ -289,18 +339,59 @@ IRAM_ATTR bool make_double_sha(_sha256_context* midstate) uint8_t temp3, temp4; uint32_t W[64] = { GET_UINT32_BE(midstate->buffer, 0), GET_UINT32_BE(midstate->buffer, 4), - GET_UINT32_BE(midstate->buffer, 8), GET_UINT32_BE(midstate->buffer, 12), 2147483648, 0, 0, 0, 0, 0, 0, 0, 0, 0, + GET_UINT32_BE(midstate->buffer, 8), GET_UINT32_BE(midstate->buffer, 12), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; uint32_t A[8] = { midstate->state[0], midstate->state[1], midstate->state[2], midstate->state[3], midstate->state[4], midstate->state[5], midstate->state[6], midstate->state[7] }; -//2147483648 +//0x80000000 union { uint32_t num; uint8_t b[4]; } u; uint8_t* p = NULL; + uint8_t i; + + for (i = 0; i < 16; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], W[i+0], K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], W[i+1], K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], W[i+2], K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], W[i+3], K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], W[i+4], K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], W[i+5], K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], W[i+6], K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], W[i+7], K[i+7]); + } + + for (i = 16; i < 64; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], R(i+0), K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], R(i+1), K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], R(i+2), K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], R(i+3), K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], R(i+4), K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], R(i+5), K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], R(i+6), K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], R(i+7), K[i+7]); + } + + /* P(A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[0], K[0]); P(A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[1], K[1]); P(A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[2], K[2]); @@ -365,6 +456,7 @@ IRAM_ATTR bool make_double_sha(_sha256_context* midstate) P(A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], R(61), K[61]); P(A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(62), K[62]); P(A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(63), K[63]); + */ PUT_UINT32_BE(midstate->state[0] + A[0], midstate->buffer, 0); PUT_UINT32_BE(midstate->state[1] + A[1], midstate->buffer, 4); @@ -403,6 +495,45 @@ IRAM_ATTR bool make_double_sha(_sha256_context* midstate) W[14] = 0; W[15] = 256; + for (i = 0; i < 16; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], W[i+0], K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], W[i+1], K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], W[i+2], K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], W[i+3], K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], W[i+4], K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], W[i+5], K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], W[i+6], K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], W[i+7], K[i+7]); + } + + for (i = 16; i < 64; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], R(i+0), K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], R(i+1), K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], R(i+2), K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], R(i+3), K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], R(i+4), K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], R(i+5), K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], R(i+6), K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], R(i+7), K[i+7]); + } + + /* P(A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[0], K[0]); P(A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[1], K[1]); P(A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[2], K[2]); @@ -471,7 +602,7 @@ IRAM_ATTR bool make_double_sha(_sha256_context* midstate) P(A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(62), K[62]); //CHECK_BYTES(0x9B05688C, A[5], 8); P(A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(63), K[63]); - + */ /*CHECK_BYTES(0x510E527F, A[4], 12); CHECK_BYTES(0xA54FF53A, A[3], 16); CHECK_BYTES(0x3C6EF372, A[2], 20); diff --git a/test/TestHashPerformance/src/testShaPerformance.cpp b/test/TestHashPerformance/src/testShaPerformance.cpp index b8e4fdc..bae45bd 100644 --- a/test/TestHashPerformance/src/testShaPerformance.cpp +++ b/test/TestHashPerformance/src/testShaPerformance.cpp @@ -65,15 +65,19 @@ void loop() { Serial.println(""); //Test WOLF - Sha256 midstate[32]; + Sha256 midstate; Sha256 sha256; uint8_t hash2[32]; - wc_InitSha256(midstate); - wc_Sha256Update(midstate, blockheader, 64); + wc_InitSha256(&midstate); + wc_Sha256Update(&midstate, blockheader, 64); + Serial.println("Wolf midstate:"); + for (size_t i = 0; i < 8; i++) + Serial.printf("%02x", midstate.digest[i]); + Serial.println(""); // Mining starts here //Primer sha startT = micros(); - wc_Sha256Copy(midstate, &sha256); + wc_Sha256Copy(&midstate, &sha256); wc_Sha256Update(&sha256, blockheader+64, 16); wc_Sha256Final(&sha256, hash2); // Segundo SHA-256 @@ -94,6 +98,13 @@ void loop() { mbedtls_sha256_init(&midstate3); mbedtls_sha256_starts_ret(&midstate3, 0); mbedtls_sha256_update_ret(&midstate3, blockheader, 64); + Serial.println("Mbed midstate:"); + for (size_t i = 0; i < 8; i++) + Serial.printf("%02x", midstate3.state[i]); + Serial.println(""); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", midstate3.buffer[i]); + Serial.println(""); // Mining starts here // Primer SHA-256 @@ -115,7 +126,15 @@ void loop() { //Test Jade SHA _sha256_context midstate_cached = { 0 }; + memcpy(midstate_cached.buffer, blockheader, 64); calc_midstate(blockheader, &midstate_cached); + Serial.println("Jade midstate:"); + for (size_t i = 0; i < 8; i++) + Serial.printf("%02x", midstate_cached.state[i]); + Serial.println(""); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", midstate_cached.buffer[i]); + Serial.println(""); *((uint32_t*)&midstate_cached.buffer[12]) = 0xFFFFFFFF;//nonce; // Mining starts here startT = micros(); From ffe1a790404da4c653479885ad296801dc6b90e5 Mon Sep 17 00:00:00 2001 From: BitMaker Date: Sun, 30 Jul 2023 11:01:06 +0200 Subject: [PATCH 11/14] Release 1.6.0 - Solved bugs found on merkle root calculations. - Solved issues about difficulty calculation - Tested on low difficulty pools. - Changed the default pool to http://public-pool.airdns.org/ - Added new notes to readme about supported pools - Added new custom sha tests to use on next versions --- README.md | 10 +- src/NerdMinerV2.ino.cpp | 2 +- src/ShaTests/customSHA256.cpp | 222 ------- src/ShaTests/customSHA256.h | 103 ---- src/ShaTests/nerdSHA256.cpp | 467 +++++++++++++++ src/ShaTests/nerdSHA256.h | 26 + src/mining.cpp | 6 +- src/wManager.cpp | 4 +- test/TestHashPerformance/src/nerdSHA256.cpp | 567 ++++++++++++++++++ test/TestHashPerformance/src/nerdSHA256.h | 26 + .../src/testShaPerformance.cpp | 26 +- 11 files changed, 1126 insertions(+), 333 deletions(-) delete mode 100644 src/ShaTests/customSHA256.cpp delete mode 100644 src/ShaTests/customSHA256.h create mode 100644 src/ShaTests/nerdSHA256.cpp create mode 100644 src/ShaTests/nerdSHA256.h create mode 100644 test/TestHashPerformance/src/nerdSHA256.cpp create mode 100644 test/TestHashPerformance/src/nerdSHA256.h diff --git a/README.md b/README.md index 0984b8e..993291a 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,15 @@ After programming, you will only need to setup your Wifi and BTC address. 1. Setup your Wifi Network 1. Add your BTCaddress -Optional you can select other pool: +Recommended low difficulty share pools: + +| Pool URL | Port | URL | Status | +|--- |--- |--- |--- | +| public-pool.airdns.org | 21496 | https://public-pool.airdns.org:37273/ | Check your stats. Supporting open source miners discord group | +| nerdminers.org | | | Currently pointing to th Open Source Solo Bitcoin Mining Pool | +| nerdminer.io | 3333 | https://nerdminer.io | Mantained by CHMEX | + +Other standard pools not compatible with low difficulty share: | Pool URL | Port | URL | |--- |--- |--- | diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index 9ccd5a7..8c14847 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -16,7 +16,7 @@ #include "mining.h" #include "monitor.h" -#define CURRENT_VERSION "V1.5.2" +#define CURRENT_VERSION "V1.6.0" //3 seconds WDT #define WDT_TIMEOUT 3 diff --git a/src/ShaTests/customSHA256.cpp b/src/ShaTests/customSHA256.cpp deleted file mode 100644 index b123945..0000000 --- a/src/ShaTests/customSHA256.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "customSHA256.h" - -#define TOTAL_LEN_LEN 8 - -/* - * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here. - * When useful for clarification, portions of the pseudo-code are reproduced here too. - */ - -/* - * @brief Rotate a 32-bit value by a number of bits to the right. - * @param value The value to be rotated. - * @param count The number of bits to rotate by. - * @return The rotated value. - */ -static inline uint32_t right_rot(uint32_t value, unsigned int count) -{ - /* - * Defined behaviour in standard C for all count where 0 < count < 32, which is what we need here. - */ - return value >> count | value << (32 - count); -} - -/* - * @brief Update a hash value under calculation with a new chunk of data. - * @param h Pointer to the first hash item, of a total of eight. - * @param p Pointer to the chunk data, which has a standard length. - * - * @note This is the SHA-256 work horse. - */ -static inline void consume_chunk(uint32_t *h, const uint8_t *p) -{ - unsigned i, j; - uint32_t ah[8]; - - /* Initialize working variables to current hash value: */ - for (i = 0; i < 8; i++) - ah[i] = h[i]; - - /* - * The w-array is really w[64], but since we only need 16 of them at a time, we save stack by - * calculating 16 at a time. - * - * This optimization was not there initially and the rest of the comments about w[64] are kept in their - * initial state. - */ - - /* - * create a 64-entry message schedule array w[0..63] of 32-bit words (The initial values in w[0..63] - * don't matter, so many implementations zero them here) copy chunk into first 16 words w[0..15] of the - * message schedule array - */ - uint32_t w[16]; - - /* Compression function main loop: */ - for (i = 0; i < 4; i++) { - for (j = 0; j < 16; j++) { - if (i == 0) { - w[j] = - (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | (uint32_t)p[3]; - p += 4; - } else { - /* Extend the first 16 words into the remaining 48 words w[16..63] of the - * message schedule array: */ - const uint32_t s0 = right_rot(w[(j + 1) & 0xf], 7) ^ right_rot(w[(j + 1) & 0xf], 18) ^ - (w[(j + 1) & 0xf] >> 3); - const uint32_t s1 = right_rot(w[(j + 14) & 0xf], 17) ^ - right_rot(w[(j + 14) & 0xf], 19) ^ (w[(j + 14) & 0xf] >> 10); - w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1; - } - const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25); - const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]); - - /* - * Initialize array of round constants: - * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311): - */ - static const uint32_t k[] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, - 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, - 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, - 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, - 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, - 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, - 0xc67178f2}; - - const uint32_t temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j]; - const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22); - const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]); - const uint32_t temp2 = s0 + maj; - - ah[7] = ah[6]; - ah[6] = ah[5]; - ah[5] = ah[4]; - ah[4] = ah[3] + temp1; - ah[3] = ah[2]; - ah[2] = ah[1]; - ah[1] = ah[0]; - ah[0] = temp1 + temp2; - } - } - - /* Add the compressed chunk to the current hash value: */ - for (i = 0; i < 8; i++) - h[i] += ah[i]; -} - -/* - * Public functions. See header file for documentation. - */ - -void sha_256_init(struct Sha_256 *sha_256, uint8_t hash[SIZE_OF_SHA_256_HASH]) -{ - sha_256->hash = hash; - sha_256->chunk_pos = sha_256->chunk; - sha_256->space_left = SIZE_OF_SHA_256_CHUNK; - sha_256->total_len = 0; - /* - * Initialize hash values (first 32 bits of the fractional parts of the square roots of the first 8 primes - * 2..19): - */ - sha_256->h[0] = 0x6a09e667; - sha_256->h[1] = 0xbb67ae85; - sha_256->h[2] = 0x3c6ef372; - sha_256->h[3] = 0xa54ff53a; - sha_256->h[4] = 0x510e527f; - sha_256->h[5] = 0x9b05688c; - sha_256->h[6] = 0x1f83d9ab; - sha_256->h[7] = 0x5be0cd19; -} - -void sha_256_write(struct Sha_256 *sha_256, const uint8_t *data, size_t len) -{ - sha_256->total_len += len; - - const uint8_t *p = data; - - while (len > 0) { - /* - * If the input chunks have sizes that are multiples of the calculation chunk size, no copies are - * necessary. We operate directly on the input data instead. - */ - if (sha_256->space_left == SIZE_OF_SHA_256_CHUNK && len >= SIZE_OF_SHA_256_CHUNK) { - consume_chunk(sha_256->h, p); - len -= SIZE_OF_SHA_256_CHUNK; - p += SIZE_OF_SHA_256_CHUNK; - continue; - } - /* General case, no particular optimization. */ - const size_t consumed_len = len < sha_256->space_left ? len : sha_256->space_left; - memcpy(sha_256->chunk_pos, p, consumed_len); - sha_256->space_left -= consumed_len; - len -= consumed_len; - p += consumed_len; - if (sha_256->space_left == 0) { - consume_chunk(sha_256->h, sha_256->chunk); - sha_256->chunk_pos = sha_256->chunk; - sha_256->space_left = SIZE_OF_SHA_256_CHUNK; - } else { - sha_256->chunk_pos += consumed_len; - } - } -} - -uint8_t *sha_256_close(struct Sha_256 *sha_256) -{ - uint8_t *pos = sha_256->chunk_pos; - size_t space_left = sha_256->space_left; - uint32_t *const h = sha_256->h; - - /* - * The current chunk cannot be full. Otherwise, it would already have been consumed. I.e. there is space left for - * at least one byte. The next step in the calculation is to add a single one-bit to the data. - */ - *pos++ = 0x80; - --space_left; - - /* - * Now, the last step is to add the total data length at the end of the last chunk, and zero padding before - * that. But we do not necessarily have enough space left. If not, we pad the current chunk with zeroes, and add - * an extra chunk at the end. - */ - if (space_left < TOTAL_LEN_LEN) { - memset(pos, 0x00, space_left); - consume_chunk(h, sha_256->chunk); - pos = sha_256->chunk; - space_left = SIZE_OF_SHA_256_CHUNK; - } - const size_t left = space_left - TOTAL_LEN_LEN; - memset(pos, 0x00, left); - pos += left; - size_t len = sha_256->total_len; - pos[7] = (uint8_t)(len << 3); - len >>= 5; - int i; - for (i = 6; i >= 0; --i) { - pos[i] = (uint8_t)len; - len >>= 8; - } - consume_chunk(h, sha_256->chunk); - /* Produce the final hash value (big-endian): */ - int j; - uint8_t *const hash = sha_256->hash; - for (i = 0, j = 0; i < 8; i++) { - hash[j++] = (uint8_t)(h[i] >> 24); - hash[j++] = (uint8_t)(h[i] >> 16); - hash[j++] = (uint8_t)(h[i] >> 8); - hash[j++] = (uint8_t)h[i]; - } - return sha_256->hash; -} - -void calc_sha_256(uint8_t hash[SIZE_OF_SHA_256_HASH], const uint8_t *input, size_t len) -{ - struct Sha_256 sha_256; - sha_256_init(&sha_256, hash); - sha_256_write(&sha_256, input, len); - (void)sha_256_close(&sha_256); -} \ No newline at end of file diff --git a/src/ShaTests/customSHA256.h b/src/ShaTests/customSHA256.h deleted file mode 100644 index 961a41e..0000000 --- a/src/ShaTests/customSHA256.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef SHA_256_H -#define SHA_256_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * @brief Size of the SHA-256 sum. This times eight is 256 bits. - */ -#define SIZE_OF_SHA_256_HASH 32 - -/* - * @brief Size of the chunks used for the calculations. - * - * @note This should mostly be ignored by the user, although when using the streaming API, it has an impact for - * performance. Add chunks whose size is a multiple of this, and you will avoid a lot of superfluous copying in RAM! - */ -#define SIZE_OF_SHA_256_CHUNK 64 - -/* - * @brief The opaque SHA-256 type, that should be instantiated when using the streaming API. - * - * @note Although the details are exposed here, in order to make instantiation easy, you should refrain from directly - * accessing the fields, as they may change in the future. - */ -struct Sha_256 { - uint8_t *hash; - uint8_t chunk[SIZE_OF_SHA_256_CHUNK]; - uint8_t *chunk_pos; - size_t space_left; - size_t total_len; - uint32_t h[8]; -}; - -/* - * @brief The simple SHA-256 calculation function. - * @param hash Hash array, where the result is delivered. - * @param input Pointer to the data the hash shall be calculated on. - * @param len Length of the input data, in byte. - * - * @note If all of the data you are calculating the hash value on is available in a contiguous buffer in memory, this is - * the function you should use. - * - * @note If either of the passed pointers is NULL, the results are unpredictable. - */ -void calc_sha_256(uint8_t hash[SIZE_OF_SHA_256_HASH], const uint8_t *input, size_t len); - -/* - * @brief Initialize a SHA-256 streaming calculation. - * @param sha_256 A pointer to a SHA-256 structure. - * @param hash Hash array, where the result will be delivered. - * - * @note If all of the data you are calculating the hash value on is not available in a contiguous buffer in memory, this is - * where you should start. Instantiate a SHA-256 structure, for instance by simply declaring it locally, make your hash - * buffer available, and invoke this function. Once a SHA-256 hash has been calculated (see further below) a SHA-256 - * structure can be initialized again for the next calculation. - * - * @note If either of the passed pointers is NULL, the results are unpredictable. - */ -void sha_256_init(struct Sha_256 *sha_256, uint8_t hash[SIZE_OF_SHA_256_HASH]); - -/* - * @brief Stream more input data for an on-going SHA-256 calculation. - * @param sha_256 A pointer to a previously initialized SHA-256 structure. - * @param data Pointer to the data to be added to the calculation. - * @param len Length of the data to add, in byte. - * - * @note This function may be invoked an arbitrary number of times between initialization and closing, but the maximum - * data length is limited by the SHA-256 algorithm: the total number of bits (i.e. the total number of bytes times - * eight) must be representable by a 64-bit unsigned integer. While that is not a practical limitation, the results are - * unpredictable if that limit is exceeded. - * - * @note This function may be invoked on empty data (zero length), although that obviously will not add any data. - * - * @note If either of the passed pointers is NULL, the results are unpredictable. - */ -void sha_256_write(struct Sha_256 *sha_256, const uint8_t *data, size_t len); - -/* - * @brief Conclude a SHA-256 streaming calculation, making the hash value available. - * @param sha_256 A pointer to a previously initialized SHA-256 structure. - * @return Pointer to the hash array, where the result is delivered. - * - * @note After this function has been invoked, the result is available in the hash buffer that initially was provided. A - * pointer to the hash value is returned for convenience, but you should feel free to ignore it: it is simply a pointer - * to the first byte of your initially provided hash array. - * - * @note If the passed pointer is NULL, the results are unpredictable. - * - * @note Invoking this function for a calculation with no data (the writing function has never been invoked, or it only - * has been invoked with empty data) is legal. It will calculate the SHA-256 value of the empty string. - */ -uint8_t *sha_256_close(struct Sha_256 *sha_256); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/src/ShaTests/nerdSHA256.cpp b/src/ShaTests/nerdSHA256.cpp new file mode 100644 index 0000000..dfa7de7 --- /dev/null +++ b/src/ShaTests/nerdSHA256.cpp @@ -0,0 +1,467 @@ +#define NDEBUG +#include +#include +#include + +//#include +#include +#include + +#include "nerdSHA256.h" +#include +#include + +#define HASH_SIZE 32 + +IRAM_ATTR static inline uint32_t rotlFixed(uint32_t x, uint32_t y) + { + return (x << y) | (x >> (sizeof(y) * 8 - y)); + } +IRAM_ATTR static inline uint32_t rotrFixed(uint32_t x, uint32_t y) + { + return (x >> y) | (x << (sizeof(y) * 8 - y)); + } +/* SHA256 math based on specification */ +#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x,y,z) ((((x) | (y)) & (z)) | ((x) & (y))) + +#define R(x, n) (((x) & 0xFFFFFFFFU) >> (n)) + +#define S(x, n) rotrFixed(x, n) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +#define a(i) S[(0-(i)) & 7] +#define b(i) S[(1-(i)) & 7] +#define c(i) S[(2-(i)) & 7] +#define d(i) S[(3-(i)) & 7] +#define e(i) S[(4-(i)) & 7] +#define f(i) S[(5-(i)) & 7] +#define g(i) S[(6-(i)) & 7] +#define h(i) S[(7-(i)) & 7] + +#define XTRANSFORM(S, D) Transform_Sha256((S),(D)) +#define XMEMCPY(d,s,l) memcpy((d),(s),(l)) +#define XMEMSET(b,c,l) memset((b),(c),(l)) + +/* SHA256 version that keeps all data in registers */ +#define SCHED1(j) (W[j] = *((uint32_t*)&data[j*sizeof(uint32_t)])) +#define SCHED(j) ( \ + W[ j & 15] += \ + Gamma1(W[(j-2) & 15])+ \ + W[(j-7) & 15] + \ + Gamma0(W[(j-15) & 15]) \ + ) + +#define RND1(j) \ + t0 = h(j) + Sigma1(e(j)) + Ch(e(j), f(j), g(j)) + K[i+j] + SCHED1(j); \ + t1 = Sigma0(a(j)) + Maj(a(j), b(j), c(j)); \ + d(j) += t0; \ + h(j) = t0 + t1 +#define RNDN(j) \ + t0 = h(j) + Sigma1(e(j)) + Ch(e(j), f(j), g(j)) + K[i+j] + SCHED(j); \ + t1 = Sigma0(a(j)) + Maj(a(j), b(j), c(j)); \ + d(j) += t0; \ + h(j) = t0 + t1 + +//DRAM_ATTR static const uint32_t K[] = { +DRAM_ATTR static const uint32_t K[64] = { + 0x428A2F98L, 0x71374491L, 0xB5C0FBCFL, 0xE9B5DBA5L, 0x3956C25BL, + 0x59F111F1L, 0x923F82A4L, 0xAB1C5ED5L, 0xD807AA98L, 0x12835B01L, + 0x243185BEL, 0x550C7DC3L, 0x72BE5D74L, 0x80DEB1FEL, 0x9BDC06A7L, + 0xC19BF174L, 0xE49B69C1L, 0xEFBE4786L, 0x0FC19DC6L, 0x240CA1CCL, + 0x2DE92C6FL, 0x4A7484AAL, 0x5CB0A9DCL, 0x76F988DAL, 0x983E5152L, + 0xA831C66DL, 0xB00327C8L, 0xBF597FC7L, 0xC6E00BF3L, 0xD5A79147L, + 0x06CA6351L, 0x14292967L, 0x27B70A85L, 0x2E1B2138L, 0x4D2C6DFCL, + 0x53380D13L, 0x650A7354L, 0x766A0ABBL, 0x81C2C92EL, 0x92722C85L, + 0xA2BFE8A1L, 0xA81A664BL, 0xC24B8B70L, 0xC76C51A3L, 0xD192E819L, + 0xD6990624L, 0xF40E3585L, 0x106AA070L, 0x19A4C116L, 0x1E376C08L, + 0x2748774CL, 0x34B0BCB5L, 0x391C0CB3L, 0x4ED8AA4AL, 0x5B9CCA4FL, + 0x682E6FF3L, 0x748F82EEL, 0x78A5636FL, 0x84C87814L, 0x8CC70208L, + 0x90BEFFFAL, 0xA4506CEBL, 0xBEF9A3F7L, 0xC67178F2L + }; + +IRAM_ATTR static int Transform_Sha256(nerd_sha256* sha256, const uint8_t* data) +{ + uint32_t S[8], t0, t1; + int i; + uint32_t W[NERD_BLOCK_SIZE/sizeof(uint32_t)]; + + /* Copy digest to working vars */ + S[0] = sha256->digest[0]; + S[1] = sha256->digest[1]; + S[2] = sha256->digest[2]; + S[3] = sha256->digest[3]; + S[4] = sha256->digest[4]; + S[5] = sha256->digest[5]; + S[6] = sha256->digest[6]; + S[7] = sha256->digest[7]; + + i = 0; + RND1( 0); RND1( 1); RND1( 2); RND1( 3); + RND1( 4); RND1( 5); RND1( 6); RND1( 7); + RND1( 8); RND1( 9); RND1(10); RND1(11); + RND1(12); RND1(13); RND1(14); RND1(15); + /* 64 operations, partially loop unrolled */ + for (i = 16; i < 64; i += 16) { + RNDN( 0); RNDN( 1); RNDN( 2); RNDN( 3); + RNDN( 4); RNDN( 5); RNDN( 6); RNDN( 7); + RNDN( 8); RNDN( 9); RNDN(10); RNDN(11); + RNDN(12); RNDN(13); RNDN(14); RNDN(15); + } + + /* Add the working vars back into digest */ + sha256->digest[0] += S[0]; + sha256->digest[1] += S[1]; + sha256->digest[2] += S[2]; + sha256->digest[3] += S[3]; + sha256->digest[4] += S[4]; + sha256->digest[5] += S[5]; + sha256->digest[6] += S[6]; + sha256->digest[7] += S[7]; + + return 0; +} + +IRAM_ATTR static uint32_t ByteReverseWord32(uint32_t value){ + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return rotlFixed(value, 16U); +} + +IRAM_ATTR static void ByteReverseWords(uint32_t* out, const uint32_t* in, uint32_t byteCount) +{ + uint32_t count, i; + count = byteCount/(uint32_t)sizeof(uint32_t); + for (i = 0; i < count; i++) out[i] = ByteReverseWord32(in[i]); +} + + +IRAM_ATTR static int nerd_update(nerd_sha256* sha256, uint8_t* data, uint32_t len) +{ + int ret = 0; + uint32_t blocksLen; + uint8_t* local; + + //ShaUpdate + uint32_t tmp = sha256->loLen; + if ((sha256->loLen += len) < tmp) { + sha256->hiLen++; /* carry low to high */ + } + + local = (uint8_t*)sha256->buffer; + + /* process any remainder from previous operation */ + if (sha256->buffLen > 0) { + blocksLen = min(len, NERD_BLOCK_SIZE - sha256->buffLen); + XMEMCPY(&local[sha256->buffLen], data, blocksLen); + + sha256->buffLen += blocksLen; + data += blocksLen; + len -= blocksLen; + + if (sha256->buffLen == NERD_BLOCK_SIZE) { + + ByteReverseWords(sha256->buffer, sha256->buffer, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(sha256, (const uint8_t*)local); + + if (ret == 0) + sha256->buffLen = 0; + else + len = 0; /* error */ + } + } + + /* process blocks */ + while (len >= NERD_BLOCK_SIZE) { + uint32_t* local32 = sha256->buffer; + XMEMCPY(local32, data, NERD_BLOCK_SIZE); + + data += NERD_BLOCK_SIZE; + len -= NERD_BLOCK_SIZE; + + ByteReverseWords(local32, local32, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(sha256, (const uint8_t*)local32); + + if (ret != 0) + break; + } + /* save remainder */ + if (ret == 0 && len > 0) { + XMEMCPY(local, data, len); + sha256->buffLen = len; + } + + return ret; +} + +IRAM_ATTR static int nerd_finishSHA(nerd_sha256* sha256, uint8_t* hash){ + + int ret; + uint8_t* local; + + local = (uint8_t*)sha256->buffer; + local[sha256->buffLen++] = 0x80; // add 1 + //Padd with zeros + if (sha256->buffLen > NERD_PAD_SIZE) { + + XMEMSET(&local[sha256->buffLen], 0, NERD_BLOCK_SIZE - sha256->buffLen); + sha256->buffLen += NERD_BLOCK_SIZE - sha256->buffLen; + + ByteReverseWords(sha256->buffer, sha256->buffer, NERD_BLOCK_SIZE); + XTRANSFORM(sha256, (const uint8_t*)local); + + sha256->buffLen = 0; + } + + XMEMSET(&local[sha256->buffLen], 0, NERD_PAD_SIZE - sha256->buffLen); + + // put lengths in bits + sha256->hiLen = (sha256->loLen >> (8 * sizeof(sha256->loLen) - 3)) + (sha256->hiLen << 3); + sha256->loLen = sha256->loLen << 3; + + ByteReverseWords(sha256->buffer, sha256->buffer, NERD_BLOCK_SIZE); + + // ! length ordering dependent on digest endian type ! + XMEMCPY(&local[NERD_PAD_SIZE], &sha256->hiLen, sizeof(uint32_t)); + XMEMCPY(&local[NERD_PAD_SIZE + sizeof(uint32_t)], &sha256->loLen, sizeof(uint32_t)); + + XTRANSFORM(sha256, (const uint8_t*)local); + + ByteReverseWords(sha256->digest, sha256->digest, NERD_DIGEST_SIZE); + + //Copy temp hash + XMEMCPY(hash, sha256->digest, NERD_DIGEST_SIZE); + + return 0; +} + +IRAM_ATTR int nerd_midstate(nerd_sha256* sha256, uint8_t* data, uint32_t len) +{ + int ret = 0; + uint32_t blocksLen; + uint8_t* local; + + //Init SHA context + XMEMSET(sha256->digest, 0, sizeof(sha256->digest)); + sha256->digest[0] = 0x6A09E667L; + sha256->digest[1] = 0xBB67AE85L; + sha256->digest[2] = 0x3C6EF372L; + sha256->digest[3] = 0xA54FF53AL; + sha256->digest[4] = 0x510E527FL; + sha256->digest[5] = 0x9B05688CL; + sha256->digest[6] = 0x1F83D9ABL; + sha256->digest[7] = 0x5BE0CD19L; + + sha256->buffLen = 0; + sha256->loLen = 0; + sha256->hiLen = 0; + //endINIT Sha contexxt + + nerd_update(sha256,data,len); + + return 0; +} + +/* +IRAM_ATTR int nerd_double_sha(nerd_sha256* midstate, uint8_t* data, uint8_t* doubleHash) +{ + nerd_sha256 sha256; + int ret = 0; + uint8_t hash[32]; + + //Copy current context + XMEMCPY(&sha256, midstate, sizeof(nerd_sha256)); + + // ------ First SHA ------ + nerd_update(&sha256,data,16); //Pending 16 bytes from 80 of blockheader + nerd_finishSHA(&sha256,hash); + + // ------ Second SHA ------ + //Init SHA context + XMEMSET(sha256.digest, 0, sizeof(sha256.digest)); + sha256.digest[0] = 0x6A09E667L; + sha256.digest[1] = 0xBB67AE85L; + sha256.digest[2] = 0x3C6EF372L; + sha256.digest[3] = 0xA54FF53AL; + sha256.digest[4] = 0x510E527FL; + sha256.digest[5] = 0x9B05688CL; + sha256.digest[6] = 0x1F83D9ABL; + sha256.digest[7] = 0x5BE0CD19L; + + sha256.buffLen = 0; + sha256.loLen = 0; + sha256.hiLen = 0; + //endINIT Sha context + nerd_update(&sha256,hash,32); + nerd_finishSHA(&sha256,doubleHash); + + return 0; +} +*/ + +IRAM_ATTR int nerd_double_sha(nerd_sha256* midstate, uint8_t* data, uint8_t* doubleHash) +{ + IRAM_DATA_ATTR nerd_sha256 sha256; + //nerd_sha256 sha256_2; + int ret = 0; + uint32_t blocksLen; + uint8_t* local; + uint8_t* local2; + uint8_t tmpHash[32]; + uint8_t* hash; + + //Copy current context + XMEMCPY(&sha256, midstate, sizeof(nerd_sha256)); + + // ----- 1rst SHA ------------ + //*********** ShaUpdate *********** + uint32_t len = 16; //Pending bytes to make the sha256 + uint32_t tmp = sha256.loLen; + if ((sha256.loLen += len) < tmp) { + sha256.hiLen++; + } + + local = (uint8_t*)sha256.buffer; + // save remainder + if (ret == 0 && len > 0) { + XMEMCPY(local, data, len); + sha256.buffLen = len; + } + //*********** end update *********** + + //*********** Init SHA_finish *********** + + local[sha256.buffLen++] = 0x80; // add 1 + + XMEMSET(&local[sha256.buffLen], 0, NERD_PAD_SIZE - sha256.buffLen); + + // put lengths in bits + sha256.hiLen = (sha256.loLen >> (8 * sizeof(sha256.loLen) - 3)) + (sha256.hiLen << 3); + sha256.loLen = sha256.loLen << 3; + + ByteReverseWords(sha256.buffer, sha256.buffer, NERD_BLOCK_SIZE); + + // ! length ordering dependent on digest endian type ! + XMEMCPY(&local[NERD_PAD_SIZE], &sha256.hiLen, sizeof(uint32_t)); + XMEMCPY(&local[NERD_PAD_SIZE + sizeof(uint32_t)], &sha256.loLen, sizeof(uint32_t)); + + XTRANSFORM(&sha256, (const uint8_t*)local); + + ByteReverseWords((uint32_t* )tmpHash, sha256.digest, NERD_DIGEST_SIZE); + + hash = tmpHash; + + //*********** end SHA_finish *********** + + // ----- 2nd SHA ------------ + //Init SHA context again + XMEMSET(sha256.digest, 0, sizeof(sha256.digest)); + sha256.digest[0] = 0x6A09E667L; + sha256.digest[1] = 0xBB67AE85L; + sha256.digest[2] = 0x3C6EF372L; + sha256.digest[3] = 0xA54FF53AL; + sha256.digest[4] = 0x510E527FL; + sha256.digest[5] = 0x9B05688CL; + sha256.digest[6] = 0x1F83D9ABL; + sha256.digest[7] = 0x5BE0CD19L; + + sha256.buffLen = 0; + sha256.loLen = 0; + sha256.hiLen = 0; + //endINIT Sha context + + //*********** ShaUpdate *********** + len = 32; //Current hash size to make the 2nd sha256 + tmp = sha256.loLen; + if ((sha256.loLen += len) < tmp) { + sha256.hiLen++; + } + + local2 = (uint8_t*)sha256.buffer; + + // process any remainder from previous operation + if (sha256.buffLen > 0) { + blocksLen = min(len, NERD_BLOCK_SIZE - sha256.buffLen); + XMEMCPY(&local2[sha256.buffLen], hash, blocksLen); + + sha256.buffLen += blocksLen; + hash += blocksLen; + len -= blocksLen; + + if (sha256.buffLen == NERD_BLOCK_SIZE) { + + ByteReverseWords(sha256.buffer, sha256.buffer, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(&sha256, (const uint8_t*)local2); + + if (ret == 0) + sha256.buffLen = 0; + else + len = 0; // error + } + } + + // process blocks + while (len >= NERD_BLOCK_SIZE) { + uint32_t* local32 = sha256.buffer; + XMEMCPY(local32, hash, NERD_BLOCK_SIZE); + + hash += NERD_BLOCK_SIZE; + len -= NERD_BLOCK_SIZE; + + ByteReverseWords(local32, local32, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(&sha256, (const uint8_t*)local32); + + if (ret != 0) + break; + } + // save remainder + if (ret == 0 && len > 0) { + XMEMCPY(local2, hash, len); + sha256.buffLen = len; + } + //*********** end update *********** + + //*********** Init SHA_finish *********** + + //local2 = (uint8_t*)sha256.buffer; + local2[sha256.buffLen++] = 0x80; // add 1 + //local2[33] = 0x80; // add 1 + + //Padd with zeros + + if (sha256.buffLen > NERD_PAD_SIZE) { + + XMEMSET(&local2[sha256.buffLen], 0, NERD_BLOCK_SIZE - sha256.buffLen); + sha256.buffLen += NERD_BLOCK_SIZE - sha256.buffLen; + + //ByteReverseWords(sha256_2.buffer, sha256_2.buffer, NERD_BLOCK_SIZE); + XTRANSFORM(&sha256, (const uint8_t*)local2); + + sha256.buffLen = 0; + } + + XMEMSET(&local2[sha256.buffLen], 0, NERD_PAD_SIZE - sha256.buffLen); + + // put lengths in bits + sha256.hiLen = (sha256.loLen >> (8 * sizeof(sha256.loLen) - 3)) + (sha256.hiLen << 3); + sha256.loLen = sha256.loLen << 3; + + ByteReverseWords(sha256.buffer, sha256.buffer, NERD_BLOCK_SIZE); + + // ! length ordering dependent on digest endian type ! + XMEMCPY(&local2[NERD_PAD_SIZE], &sha256.hiLen, sizeof(uint32_t)); + XMEMCPY(&local2[NERD_PAD_SIZE + sizeof(uint32_t)], &sha256.loLen, sizeof(uint32_t)); + + XTRANSFORM(&sha256, (const uint8_t*)local2); + + ByteReverseWords((uint32_t*)doubleHash, sha256.digest, NERD_DIGEST_SIZE); + + return 0; +} + diff --git a/src/ShaTests/nerdSHA256.h b/src/ShaTests/nerdSHA256.h new file mode 100644 index 0000000..c4b866a --- /dev/null +++ b/src/ShaTests/nerdSHA256.h @@ -0,0 +1,26 @@ +#ifndef nerdSHA256_H_ +#define nerdSHA256_H_ + +#include +#include +#include + +#define NERD_DIGEST_SIZE 32 +#define NERD_BLOCK_SIZE 64 +#define NERD_PAD_SIZE 56 + +struct nerd_sha256 { + uint32_t digest[NERD_DIGEST_SIZE / sizeof(uint32_t)]; + uint32_t buffer[NERD_BLOCK_SIZE / sizeof(uint32_t)]; + uint32_t buffLen; /* in bytes */ + uint32_t loLen; /* length in bytes */ + uint32_t hiLen; /* length in bytes */ + void* heap; +}; + +/* Calculate midstate */ +IRAM_ATTR int nerd_midstate(nerd_sha256* sha256, uint8_t* data, uint32_t len); + +IRAM_ATTR int nerd_double_sha(nerd_sha256* midstate, uint8_t* data, uint8_t* doubleHash); + +#endif /* nerdSHA256_H_ */ \ No newline at end of file diff --git a/src/mining.cpp b/src/mining.cpp index 98a4f99..456f0e8 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -4,6 +4,7 @@ #include #include // Graphics and font library for ILI9341 driver chip #include +//#include "ShaTests/nerdSHA256.h" #include "media/Free_Fonts.h" #include "media/images.h" #include "OpenFontRender.h" @@ -240,12 +241,14 @@ void runMiner(void * task_id) { //Prepare Premining data Sha256 midstate[32]; - unsigned char hash[32]; + //nerd_sha256 nerdMidstate; + uint8_t hash[32]; Sha256 sha256; //Calcular midstate WOLF wc_InitSha256(midstate); wc_Sha256Update(midstate, mMiner.bytearray_blockheader, 64); + //nerd_midstate(&nerdMidstate, mMiner.bytearray_blockheader, 64); /*Serial.println("Blockheader:"); @@ -285,6 +288,7 @@ void runMiner(void * task_id) { // Segundo SHA-256 wc_Sha256Update(&sha256, hash, 32); wc_Sha256Final(&sha256, hash); + //nerd_double_sha(&nerdMidstate, header64, hash); /*for (size_t i = 0; i < 32; i++) Serial.printf("%02x", hash[i]); diff --git a/src/wManager.cpp b/src/wManager.cpp index e083da5..992292b 100644 --- a/src/wManager.cpp +++ b/src/wManager.cpp @@ -19,8 +19,8 @@ bool shouldSaveConfig = false; // Variables to hold data from custom textboxes -char poolString[80] = "solo.ckpool.org"; -int portNumber = 3333; +char poolString[80] = "public-pool.airdns.org"; +int portNumber = 21496;//3333; char btcString[80] = "yourBtcAddress"; int GMTzone = 2; //Currently selected in spain diff --git a/test/TestHashPerformance/src/nerdSHA256.cpp b/test/TestHashPerformance/src/nerdSHA256.cpp new file mode 100644 index 0000000..39c8172 --- /dev/null +++ b/test/TestHashPerformance/src/nerdSHA256.cpp @@ -0,0 +1,567 @@ +#define NDEBUG +#include +#include +#include + +//#include +#include +#include + +#include "nerdSHA256.h" +#include +#include + +#define HASH_SIZE 32 + +IRAM_ATTR static inline uint32_t rotlFixed(uint32_t x, uint32_t y) + { + return (x << y) | (x >> (sizeof(y) * 8 - y)); + } +IRAM_ATTR static inline uint32_t rotrFixed(uint32_t x, uint32_t y) + { + return (x >> y) | (x << (sizeof(y) * 8 - y)); + } +/* SHA256 math based on specification */ +#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x,y,z) ((((x) | (y)) & (z)) | ((x) & (y))) + +//#define R(x, n) (((x) & 0xFFFFFFFFU) >> (n)) + +#define S(x, n) rotrFixed(x, n) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +#define a(i) S[(0-(i)) & 7] +#define b(i) S[(1-(i)) & 7] +#define c(i) S[(2-(i)) & 7] +#define d(i) S[(3-(i)) & 7] +#define e(i) S[(4-(i)) & 7] +#define f(i) S[(5-(i)) & 7] +#define g(i) S[(6-(i)) & 7] +#define h(i) S[(7-(i)) & 7] + +#define XTRANSFORM(S, D) Transform_Sha256((S),(D)) +#define XMEMCPY(d,s,l) memcpy((d),(s),(l)) +#define XMEMSET(b,c,l) memset((b),(c),(l)) + +/* SHA256 version that keeps all data in registers */ +#define SCHED1(j) (W[j] = *((uint32_t*)&data[j*sizeof(uint32_t)])) +#define SCHED(j) ( \ + W[ j & 15] += \ + Gamma1(W[(j-2) & 15])+ \ + W[(j-7) & 15] + \ + Gamma0(W[(j-15) & 15]) \ + ) + +#define RND1(j) \ + t0 = h(j) + Sigma1(e(j)) + Ch(e(j), f(j), g(j)) + K[i+j] + SCHED1(j); \ + t1 = Sigma0(a(j)) + Maj(a(j), b(j), c(j)); \ + d(j) += t0; \ + h(j) = t0 + t1 +#define RNDN(j) \ + t0 = h(j) + Sigma1(e(j)) + Ch(e(j), f(j), g(j)) + K[i+j] + SCHED(j); \ + t1 = Sigma0(a(j)) + Maj(a(j), b(j), c(j)); \ + d(j) += t0; \ + h(j) = t0 + t1 + +#define SHR(x, n) ((x & 0xFFFFFFFF) >> n) +//#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define ROTR(x, n) (SHR(x, n) | ((x) << (32 - (n)))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) + +#define F0(x, y, z) ((x & y) | (z & (x | y))) +#define F1(x, y, z) (z ^ (x & (y ^ z))) + +#define R(t) (W[t] = S1(W[t - 2]) + W[t - 7] + S0(W[t - 15]) + W[t - 16]) + +#define P(a, b, c, d, e, f, g, h, x, K) \ + { \ + temp1 = h + S3(e) + F1(e, f, g) + K + x; \ + temp2 = S2(a) + F0(a, b, c); \ + d += temp1; \ + h = temp1 + temp2; \ + } +#define GET_UINT32_BE(b, i) \ + (((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) \ + | ((uint32_t)(b)[(i) + 3])) + +//DRAM_ATTR static const uint32_t K[] = { +DRAM_ATTR static const uint32_t K[64] = { + 0x428A2F98L, 0x71374491L, 0xB5C0FBCFL, 0xE9B5DBA5L, 0x3956C25BL, + 0x59F111F1L, 0x923F82A4L, 0xAB1C5ED5L, 0xD807AA98L, 0x12835B01L, + 0x243185BEL, 0x550C7DC3L, 0x72BE5D74L, 0x80DEB1FEL, 0x9BDC06A7L, + 0xC19BF174L, 0xE49B69C1L, 0xEFBE4786L, 0x0FC19DC6L, 0x240CA1CCL, + 0x2DE92C6FL, 0x4A7484AAL, 0x5CB0A9DCL, 0x76F988DAL, 0x983E5152L, + 0xA831C66DL, 0xB00327C8L, 0xBF597FC7L, 0xC6E00BF3L, 0xD5A79147L, + 0x06CA6351L, 0x14292967L, 0x27B70A85L, 0x2E1B2138L, 0x4D2C6DFCL, + 0x53380D13L, 0x650A7354L, 0x766A0ABBL, 0x81C2C92EL, 0x92722C85L, + 0xA2BFE8A1L, 0xA81A664BL, 0xC24B8B70L, 0xC76C51A3L, 0xD192E819L, + 0xD6990624L, 0xF40E3585L, 0x106AA070L, 0x19A4C116L, 0x1E376C08L, + 0x2748774CL, 0x34B0BCB5L, 0x391C0CB3L, 0x4ED8AA4AL, 0x5B9CCA4FL, + 0x682E6FF3L, 0x748F82EEL, 0x78A5636FL, 0x84C87814L, 0x8CC70208L, + 0x90BEFFFAL, 0xA4506CEBL, 0xBEF9A3F7L, 0xC67178F2L + }; + +/* +IRAM_ATTR static int Transform_Sha256(nerd_sha256* sha256, const uint8_t* buf_ptr) +{ + + uint32_t A[8] = {0 }; + + uint32_t temp1, temp2, W[64]; + int i=0; + + for (i = 0; i < 8; i++) { + A[i] = sha256->digest[i]; + } + + W[0] = GET_UINT32_BE(buf_ptr, 0); + W[1] = GET_UINT32_BE(buf_ptr, 4); + W[2] = GET_UINT32_BE(buf_ptr, 8); + W[3] = GET_UINT32_BE(buf_ptr, 12); + W[4] = GET_UINT32_BE(buf_ptr, 16); + W[5] = GET_UINT32_BE(buf_ptr, 20); + W[6] = GET_UINT32_BE(buf_ptr, 24); + W[7] = GET_UINT32_BE(buf_ptr, 28); + W[8] = GET_UINT32_BE(buf_ptr, 32); + W[9] = GET_UINT32_BE(buf_ptr, 36); + W[10] = GET_UINT32_BE(buf_ptr, 40); + W[11] = GET_UINT32_BE(buf_ptr, 44); + W[12] = GET_UINT32_BE(buf_ptr, 48); + W[13] = GET_UINT32_BE(buf_ptr, 52); + W[14] = GET_UINT32_BE(buf_ptr, 56); + W[15] = GET_UINT32_BE(buf_ptr, 60); + + for (i = 0; i < 16; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], W[i+0], K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], W[i+1], K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], W[i+2], K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], W[i+3], K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], W[i+4], K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], W[i+5], K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], W[i+6], K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], W[i+7], K[i+7]); + } + + for (i = 16; i < 64; i += 8) { + P(A[0], A[1], A[2], A[3], A[4], + A[5], A[6], A[7], R(i+0), K[i+0]); + P(A[7], A[0], A[1], A[2], A[3], + A[4], A[5], A[6], R(i+1), K[i+1]); + P(A[6], A[7], A[0], A[1], A[2], + A[3], A[4], A[5], R(i+2), K[i+2]); + P(A[5], A[6], A[7], A[0], A[1], + A[2], A[3], A[4], R(i+3), K[i+3]); + P(A[4], A[5], A[6], A[7], A[0], + A[1], A[2], A[3], R(i+4), K[i+4]); + P(A[3], A[4], A[5], A[6], A[7], + A[0], A[1], A[2], R(i+5), K[i+5]); + P(A[2], A[3], A[4], A[5], A[6], + A[7], A[0], A[1], R(i+6), K[i+6]); + P(A[1], A[2], A[3], A[4], A[5], + A[6], A[7], A[0], R(i+7), K[i+7]); + } + + for (i = 0; i < 8; i++) { + sha256->digest[i] += A[i]; + } +} +*/ + +IRAM_ATTR static int Transform_Sha256(nerd_sha256* sha256, const uint8_t* data) +{ + uint32_t S[8], t0, t1; + int i; + uint32_t W[NERD_BLOCK_SIZE/sizeof(uint32_t)]; + + // Copy digest to working vars + S[0] = sha256->digest[0]; + S[1] = sha256->digest[1]; + S[2] = sha256->digest[2]; + S[3] = sha256->digest[3]; + S[4] = sha256->digest[4]; + S[5] = sha256->digest[5]; + S[6] = sha256->digest[6]; + S[7] = sha256->digest[7]; + + i = 0; + RND1( 0); RND1( 1); RND1( 2); RND1( 3); + RND1( 4); RND1( 5); RND1( 6); RND1( 7); + RND1( 8); RND1( 9); RND1(10); RND1(11); + RND1(12); RND1(13); RND1(14); RND1(15); + // 64 operations, partially loop unrolled + for (i = 16; i < 64; i += 16) { + RNDN( 0); RNDN( 1); RNDN( 2); RNDN( 3); + RNDN( 4); RNDN( 5); RNDN( 6); RNDN( 7); + RNDN( 8); RNDN( 9); RNDN(10); RNDN(11); + RNDN(12); RNDN(13); RNDN(14); RNDN(15); + } + + // Add the working vars back into digest + sha256->digest[0] += S[0]; + sha256->digest[1] += S[1]; + sha256->digest[2] += S[2]; + sha256->digest[3] += S[3]; + sha256->digest[4] += S[4]; + sha256->digest[5] += S[5]; + sha256->digest[6] += S[6]; + sha256->digest[7] += S[7]; + + return 0; +} + +IRAM_ATTR static uint32_t ByteReverseWord32(uint32_t value){ + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return rotlFixed(value, 16U); +} + +IRAM_ATTR static void ByteReverseWords(uint32_t* out, const uint32_t* in, uint32_t byteCount) +{ + uint32_t count, i; + count = byteCount/(uint32_t)sizeof(uint32_t); + for (i = 0; i < count; i++) out[i] = ByteReverseWord32(in[i]); +} + + +IRAM_ATTR static int nerd_update(nerd_sha256* sha256, uint8_t* data, uint32_t len) +{ + int ret = 0; + uint32_t blocksLen; + uint8_t* local; + + //ShaUpdate + uint32_t tmp = sha256->loLen; + if ((sha256->loLen += len) < tmp) { + sha256->hiLen++; /* carry low to high */ + } + + local = (uint8_t*)sha256->buffer; + + /* process any remainder from previous operation */ + if (sha256->buffLen > 0) { + blocksLen = min(len, NERD_BLOCK_SIZE - sha256->buffLen); + XMEMCPY(&local[sha256->buffLen], data, blocksLen); + + sha256->buffLen += blocksLen; + data += blocksLen; + len -= blocksLen; + + if (sha256->buffLen == NERD_BLOCK_SIZE) { + + ByteReverseWords(sha256->buffer, sha256->buffer, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(sha256, (const uint8_t*)local); + + if (ret == 0) + sha256->buffLen = 0; + else + len = 0; /* error */ + } + } + + /* process blocks */ + while (len >= NERD_BLOCK_SIZE) { + uint32_t* local32 = sha256->buffer; + XMEMCPY(local32, data, NERD_BLOCK_SIZE); + + data += NERD_BLOCK_SIZE; + len -= NERD_BLOCK_SIZE; + + ByteReverseWords(local32, local32, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(sha256, (const uint8_t*)local32); + + if (ret != 0) + break; + } + /* save remainder */ + if (ret == 0 && len > 0) { + XMEMCPY(local, data, len); + sha256->buffLen = len; + } + + return ret; +} + +IRAM_ATTR static int nerd_finishSHA(nerd_sha256* sha256, uint8_t* hash){ + + int ret; + uint8_t* local; + + local = (uint8_t*)sha256->buffer; + local[sha256->buffLen++] = 0x80; // add 1 + //Padd with zeros + if (sha256->buffLen > NERD_PAD_SIZE) { + + XMEMSET(&local[sha256->buffLen], 0, NERD_BLOCK_SIZE - sha256->buffLen); + sha256->buffLen += NERD_BLOCK_SIZE - sha256->buffLen; + + ByteReverseWords(sha256->buffer, sha256->buffer, NERD_BLOCK_SIZE); + XTRANSFORM(sha256, (const uint8_t*)local); + + sha256->buffLen = 0; + } + + XMEMSET(&local[sha256->buffLen], 0, NERD_PAD_SIZE - sha256->buffLen); + + // put lengths in bits + sha256->hiLen = (sha256->loLen >> (8 * sizeof(sha256->loLen) - 3)) + (sha256->hiLen << 3); + sha256->loLen = sha256->loLen << 3; + + ByteReverseWords(sha256->buffer, sha256->buffer, NERD_BLOCK_SIZE); + + // ! length ordering dependent on digest endian type ! + XMEMCPY(&local[NERD_PAD_SIZE], &sha256->hiLen, sizeof(uint32_t)); + XMEMCPY(&local[NERD_PAD_SIZE + sizeof(uint32_t)], &sha256->loLen, sizeof(uint32_t)); + + XTRANSFORM(sha256, (const uint8_t*)local); + + ByteReverseWords(sha256->digest, sha256->digest, NERD_DIGEST_SIZE); + + //Copy temp hash + XMEMCPY(hash, sha256->digest, NERD_DIGEST_SIZE); + + return 0; +} + +IRAM_ATTR int nerd_midstate(nerd_sha256* sha256, uint8_t* data, uint32_t len) +{ + int ret = 0; + uint32_t blocksLen; + uint8_t* local; + + //Init SHA context + XMEMSET(sha256->digest, 0, sizeof(sha256->digest)); + sha256->digest[0] = 0x6A09E667L; + sha256->digest[1] = 0xBB67AE85L; + sha256->digest[2] = 0x3C6EF372L; + sha256->digest[3] = 0xA54FF53AL; + sha256->digest[4] = 0x510E527FL; + sha256->digest[5] = 0x9B05688CL; + sha256->digest[6] = 0x1F83D9ABL; + sha256->digest[7] = 0x5BE0CD19L; + + sha256->buffLen = 0; + sha256->loLen = 0; + sha256->hiLen = 0; + //endINIT Sha contexxt + + nerd_update(sha256,data,len); + + return 0; +} + +/* +IRAM_ATTR int nerd_double_sha(nerd_sha256* midstate, uint8_t* data, uint8_t* doubleHash) +{ + nerd_sha256 sha256; + int ret = 0; + uint8_t hash[32]; + + //Copy current context + XMEMCPY(&sha256, midstate, sizeof(nerd_sha256)); + + // ------ First SHA ------ + nerd_update(&sha256,data,16); //Pending 16 bytes from 80 of blockheader + nerd_finishSHA(&sha256,hash); + + // ------ Second SHA ------ + //Init SHA context + XMEMSET(sha256.digest, 0, sizeof(sha256.digest)); + sha256.digest[0] = 0x6A09E667L; + sha256.digest[1] = 0xBB67AE85L; + sha256.digest[2] = 0x3C6EF372L; + sha256.digest[3] = 0xA54FF53AL; + sha256.digest[4] = 0x510E527FL; + sha256.digest[5] = 0x9B05688CL; + sha256.digest[6] = 0x1F83D9ABL; + sha256.digest[7] = 0x5BE0CD19L; + + sha256.buffLen = 0; + sha256.loLen = 0; + sha256.hiLen = 0; + //endINIT Sha context + nerd_update(&sha256,hash,32); + nerd_finishSHA(&sha256,doubleHash); + + return 0; +} +*/ + +IRAM_ATTR int nerd_double_sha(nerd_sha256* midstate, uint8_t* data, uint8_t* doubleHash) +{ + IRAM_DATA_ATTR nerd_sha256 sha256; + //nerd_sha256 sha256_2; + int ret = 0; + uint32_t blocksLen; + uint8_t* local; + uint8_t* local2; + uint8_t tmpHash[32]; + uint8_t* hash; + + //Copy current context + XMEMCPY(&sha256, midstate, sizeof(nerd_sha256)); + + // ----- 1rst SHA ------------ + //*********** ShaUpdate *********** + uint32_t len = 16; //Pending bytes to make the sha256 + uint32_t tmp = sha256.loLen; + if ((sha256.loLen += len) < tmp) { + sha256.hiLen++; + } + + local = (uint8_t*)sha256.buffer; + // save remainder + if (ret == 0 && len > 0) { + XMEMCPY(local, data, len); + sha256.buffLen = len; + } + //*********** end update *********** + + //*********** Init SHA_finish *********** + + local[sha256.buffLen++] = 0x80; // add 1 + + XMEMSET(&local[sha256.buffLen], 0, NERD_PAD_SIZE - sha256.buffLen); + + // put lengths in bits + sha256.hiLen = (sha256.loLen >> (8 * sizeof(sha256.loLen) - 3)) + (sha256.hiLen << 3); + sha256.loLen = sha256.loLen << 3; + + ByteReverseWords(sha256.buffer, sha256.buffer, NERD_BLOCK_SIZE); + + // ! length ordering dependent on digest endian type ! + XMEMCPY(&local[NERD_PAD_SIZE], &sha256.hiLen, sizeof(uint32_t)); + XMEMCPY(&local[NERD_PAD_SIZE + sizeof(uint32_t)], &sha256.loLen, sizeof(uint32_t)); + + XTRANSFORM(&sha256, (const uint8_t*)local); + + ByteReverseWords((uint32_t* )tmpHash, sha256.digest, NERD_DIGEST_SIZE); + + hash = tmpHash; + + //*********** end SHA_finish *********** + + // ----- 2nd SHA ------------ + //Init SHA context again + XMEMSET(sha256.digest, 0, sizeof(sha256.digest)); + sha256.digest[0] = 0x6A09E667L; + sha256.digest[1] = 0xBB67AE85L; + sha256.digest[2] = 0x3C6EF372L; + sha256.digest[3] = 0xA54FF53AL; + sha256.digest[4] = 0x510E527FL; + sha256.digest[5] = 0x9B05688CL; + sha256.digest[6] = 0x1F83D9ABL; + sha256.digest[7] = 0x5BE0CD19L; + + sha256.buffLen = 0; + sha256.loLen = 0; + sha256.hiLen = 0; + //endINIT Sha context + + //*********** ShaUpdate *********** + len = 32; //Current hash size to make the 2nd sha256 + tmp = sha256.loLen; + if ((sha256.loLen += len) < tmp) { + sha256.hiLen++; + } + + local2 = (uint8_t*)sha256.buffer; + + // process any remainder from previous operation + if (sha256.buffLen > 0) { + blocksLen = min(len, NERD_BLOCK_SIZE - sha256.buffLen); + XMEMCPY(&local2[sha256.buffLen], hash, blocksLen); + + sha256.buffLen += blocksLen; + hash += blocksLen; + len -= blocksLen; + + if (sha256.buffLen == NERD_BLOCK_SIZE) { + + ByteReverseWords(sha256.buffer, sha256.buffer, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(&sha256, (const uint8_t*)local2); + + if (ret == 0) + sha256.buffLen = 0; + else + len = 0; // error + } + } + + // process blocks + while (len >= NERD_BLOCK_SIZE) { + uint32_t* local32 = sha256.buffer; + XMEMCPY(local32, hash, NERD_BLOCK_SIZE); + + hash += NERD_BLOCK_SIZE; + len -= NERD_BLOCK_SIZE; + + ByteReverseWords(local32, local32, NERD_BLOCK_SIZE); + + ret = XTRANSFORM(&sha256, (const uint8_t*)local32); + + if (ret != 0) + break; + } + // save remainder + if (ret == 0 && len > 0) { + XMEMCPY(local2, hash, len); + sha256.buffLen = len; + } + //*********** end update *********** + + //*********** Init SHA_finish *********** + + //local2 = (uint8_t*)sha256.buffer; + local2[sha256.buffLen++] = 0x80; // add 1 + //local2[33] = 0x80; // add 1 + + //Padd with zeros + + if (sha256.buffLen > NERD_PAD_SIZE) { + + XMEMSET(&local2[sha256.buffLen], 0, NERD_BLOCK_SIZE - sha256.buffLen); + sha256.buffLen += NERD_BLOCK_SIZE - sha256.buffLen; + + //ByteReverseWords(sha256_2.buffer, sha256_2.buffer, NERD_BLOCK_SIZE); + XTRANSFORM(&sha256, (const uint8_t*)local2); + + sha256.buffLen = 0; + } + + XMEMSET(&local2[sha256.buffLen], 0, NERD_PAD_SIZE - sha256.buffLen); + + // put lengths in bits + sha256.hiLen = (sha256.loLen >> (8 * sizeof(sha256.loLen) - 3)) + (sha256.hiLen << 3); + sha256.loLen = sha256.loLen << 3; + + ByteReverseWords(sha256.buffer, sha256.buffer, NERD_BLOCK_SIZE); + + // ! length ordering dependent on digest endian type ! + XMEMCPY(&local2[NERD_PAD_SIZE], &sha256.hiLen, sizeof(uint32_t)); + XMEMCPY(&local2[NERD_PAD_SIZE + sizeof(uint32_t)], &sha256.loLen, sizeof(uint32_t)); + + XTRANSFORM(&sha256, (const uint8_t*)local2); + + ByteReverseWords((uint32_t*)doubleHash, sha256.digest, NERD_DIGEST_SIZE); + + return 0; +} + diff --git a/test/TestHashPerformance/src/nerdSHA256.h b/test/TestHashPerformance/src/nerdSHA256.h new file mode 100644 index 0000000..c4b866a --- /dev/null +++ b/test/TestHashPerformance/src/nerdSHA256.h @@ -0,0 +1,26 @@ +#ifndef nerdSHA256_H_ +#define nerdSHA256_H_ + +#include +#include +#include + +#define NERD_DIGEST_SIZE 32 +#define NERD_BLOCK_SIZE 64 +#define NERD_PAD_SIZE 56 + +struct nerd_sha256 { + uint32_t digest[NERD_DIGEST_SIZE / sizeof(uint32_t)]; + uint32_t buffer[NERD_BLOCK_SIZE / sizeof(uint32_t)]; + uint32_t buffLen; /* in bytes */ + uint32_t loLen; /* length in bytes */ + uint32_t hiLen; /* length in bytes */ + void* heap; +}; + +/* Calculate midstate */ +IRAM_ATTR int nerd_midstate(nerd_sha256* sha256, uint8_t* data, uint32_t len); + +IRAM_ATTR int nerd_double_sha(nerd_sha256* midstate, uint8_t* data, uint8_t* doubleHash); + +#endif /* nerdSHA256_H_ */ \ No newline at end of file diff --git a/test/TestHashPerformance/src/testShaPerformance.cpp b/test/TestHashPerformance/src/testShaPerformance.cpp index bae45bd..65e0fda 100644 --- a/test/TestHashPerformance/src/testShaPerformance.cpp +++ b/test/TestHashPerformance/src/testShaPerformance.cpp @@ -5,6 +5,7 @@ #include "jadeSHA256.h" #include "customSHA256.h" +#include "nerdSHA256.h" #include "mbedtls/md.h" #include "mbedtls/sha256.h" #include @@ -69,11 +70,12 @@ void loop() { Sha256 sha256; uint8_t hash2[32]; wc_InitSha256(&midstate); - wc_Sha256Update(&midstate, blockheader, 64); - Serial.println("Wolf midstate:"); + wc_Sha256Update(&midstate, blockheader, 64); + Serial.print("Wolf midstate: "); for (size_t i = 0; i < 8; i++) Serial.printf("%02x", midstate.digest[i]); Serial.println(""); + // Mining starts here //Primer sha startT = micros(); @@ -144,5 +146,23 @@ void loop() { for (size_t i = 0; i < 32; i++) Serial.printf("%02x", midstate_cached.buffer[i]); Serial.println(""); + + //Test nerdSHA + nerd_sha256 nerdMidstate; + uint8_t nerdHash[32]; + nerd_midstate(&nerdMidstate, blockheader, 64); + Serial.print("Nerd midstate: "); + for (size_t i = 0; i < 8; i++) + Serial.printf("%02x", nerdMidstate.digest[i]); + Serial.println(""); -} \ No newline at end of file + //Mining starts here + startT = micros(); + nerd_double_sha(&nerdMidstate, blockheader+64,nerdHash); + expired = micros() - startT; + Serial.println("Nerd double SHA[" + String(expired) + "us]:"); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", nerdHash[i]); + Serial.println(""); + +} \ No newline at end of file From 05fcb0bf7d87f61ec48844399c49f8748a8260ba Mon Sep 17 00:00:00 2001 From: BitMaker Date: Sun, 30 Jul 2023 11:09:05 +0200 Subject: [PATCH 12/14] add 1.6.0 bin file --- bin/0x10000_firmware_v1.6.0.bin | Bin 0 -> 1765008 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 bin/0x10000_firmware_v1.6.0.bin diff --git a/bin/0x10000_firmware_v1.6.0.bin b/bin/0x10000_firmware_v1.6.0.bin new file mode 100644 index 0000000000000000000000000000000000000000..9f1d23ce989bfa5f296716ec677471857fad7809 GIT binary patch literal 1765008 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

#p{Zo!g=1^#xI82Ar9V1fag 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