#include #include #include #include #include #include //#include "ShaTests/nerdSHA256.h" #include "ShaTests/nerdSHA256plus.h" #include "stratum.h" #include "mining.h" #include "utils.h" #include "monitor.h" #include "timeconst.h" #include "drivers/displays/display.h" #include "drivers/storage/storage.h" #include #include #include #include "mbedtls/sha256.h" #include "i2c_master.h" //10 Jobs per second #define NONCE_PER_JOB_SW 4096 #define NONCE_PER_JOB_HW 16*1024 //#define I2C_SLAVE //#define SHA256_VALIDATE //#define RANDOM_NONCE #define RANDOM_NONCE_MASK 0xFFFFC000 #ifdef HARDWARE_SHA265 #include #include #include #if defined(CONFIG_IDF_TARGET_ESP32) #include #endif #endif nvs_handle_t stat_handle; uint32_t templates = 0; uint32_t hashes = 0; uint32_t Mhashes = 0; uint32_t totalKHashes = 0; uint32_t elapsedKHs = 0; uint64_t upTime = 0; volatile uint32_t shares; // increase if blockhash has 32 bits of zeroes volatile uint32_t valids; // increased if blockhash <= target // Track best diff double best_diff = 0.0; // Variables to hold data from custom textboxes //Track mining stats in non volatile memory extern TSettings Settings; IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres //Global work data static WiFiClient client; static miner_data mMiner; //Global miner data (Create a miner class TODO) mining_subscribe mWorker; mining_job mJob; monitor_data mMonitor; static bool volatile isMinerSuscribed = false; unsigned long mLastTXtoPool = millis(); int saveIntervals[7] = {5 * 60, 15 * 60, 30 * 60, 1 * 3600, 3 * 3600, 6 * 3600, 12 * 3600}; int saveIntervalsSize = sizeof(saveIntervals)/sizeof(saveIntervals[0]); int currentIntervalIndex = 0; bool checkPoolConnection(void) { if (client.connected()) { return true; } isMinerSuscribed = false; Serial.println("Client not connected, trying to connect..."); //Resolve first time pool DNS and save IP if(serverIP == IPAddress(1,1,1,1)) { WiFi.hostByName(Settings.PoolAddress.c_str(), serverIP); Serial.printf("Resolved DNS and save ip (first time) got: %s\n", serverIP.toString()); } //Try connecting pool IP if (!client.connect(serverIP, Settings.PoolPort)) { Serial.println("Imposible to connect to : " + Settings.PoolAddress); WiFi.hostByName(Settings.PoolAddress.c_str(), serverIP); Serial.printf("Resolved DNS got: %s\n", serverIP.toString()); return false; } return true; } //Implements a socketKeepAlive function and //checks if pool is not sending any data to reconnect again. //Even connection could be alive, pool could stop sending new job NOTIFY unsigned long mStart0Hashrate = 0; bool checkPoolInactivity(unsigned int keepAliveTime, unsigned long inactivityTime){ unsigned long currentKHashes = (Mhashes*1000) + hashes/1000; unsigned long elapsedKHs = currentKHashes - totalKHashes; uint32_t time_now = millis(); // If no shares sent to pool // send something to pool to hold socket oppened if (time_now < mLastTXtoPool) //32bit wrap mLastTXtoPool = time_now; if ( time_now > mLastTXtoPool + keepAliveTime) { mLastTXtoPool = time_now; Serial.println(" Sending : KeepAlive suggest_difficulty"); //if (client.print("{}\n") == 0) { tx_suggest_difficulty(client, DEFAULT_DIFFICULTY); /*if(tx_suggest_difficulty(client, DEFAULT_DIFFICULTY)){ Serial.println(" Sending keepAlive to pool -> Detected client disconnected"); return true; }*/ } if(elapsedKHs == 0){ //Check if hashrate is 0 during inactivityTIme if(mStart0Hashrate == 0) mStart0Hashrate = time_now; if((time_now-mStart0Hashrate) > inactivityTime) { mStart0Hashrate=0; return true;} return false; } mStart0Hashrate = 0; return false; } struct JobRequest { uint32_t id; uint32_t nonce_start; uint32_t nonce_count; double difficulty; uint8_t sha_buffer[128]; uint32_t midstate[8]; uint32_t bake[16]; }; struct JobResult { uint32_t id; uint32_t nonce; uint32_t nonce_count; double difficulty; uint8_t hash[32]; }; static std::mutex s_job_mutex; std::list> s_job_request_list_sw; #ifdef HARDWARE_SHA265 std::list> s_job_request_list_hw; #endif std::list> s_job_result_list; static volatile uint8_t s_working_current_job_id = 0xFF; static void JobPush(std::list> &job_list, uint32_t id, uint32_t nonce_start, uint32_t nonce_count, double difficulty, const uint8_t* sha_buffer, const uint32_t* midstate, const uint32_t* bake) { std::shared_ptr job = std::make_shared(); job->id = id; job->nonce_start = nonce_start; job->nonce_count = nonce_count; job->difficulty = difficulty; memcpy(job->sha_buffer, sha_buffer, sizeof(job->sha_buffer)); memcpy(job->midstate, midstate, sizeof(job->midstate)); memcpy(job->bake, bake, sizeof(job->bake)); job_list.push_back(job); } struct Submition { double diff; bool is32bit; bool isValid; }; static void MiningJobStop(uint32_t &job_pool, std::map> & submition_map) { { std::lock_guard lock(s_job_mutex); s_job_result_list.clear(); s_job_request_list_sw.clear(); #ifdef HARDWARE_SHA265 s_job_request_list_hw.clear(); #endif } s_working_current_job_id = 0xFF; job_pool = 0xFFFFFFFF; submition_map.clear(); } #ifdef RANDOM_NONCE uint64_t s_random_state = 1; static uint32_t RandomGet() { s_random_state += 0x9E3779B97F4A7C15ull; uint64_t z = s_random_state; z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull; z = (z ^ (z >> 27)) * 0x94D049BB133111EBull; return z ^ (z >> 31); } #endif void runStratumWorker(void *name) { // TEST: https://bitcoin.stackexchange.com/questions/22929/full-example-data-for-scrypt-stratum-client Serial.println(""); Serial.printf("\n[WORKER] Started. Running %s on core %d\n", (char *)name, xPortGetCoreID()); #ifdef DEBUG_MEMORY Serial.printf("### [Total Heap / Free heap / Min free heap]: %d / %d / %d \n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap()); #endif std::map> s_submition_map; #ifdef I2C_SLAVE std::vector i2c_slave_vector; //scan for i2c slaves if (i2c_master_start() == 0) i2c_slave_vector = i2c_master_scan(0x0, 0x80); Serial.printf("Found %d slave workers\n", i2c_slave_vector.size()); if (!i2c_slave_vector.empty()) { Serial.print(" Workers: "); for (size_t n = 0; n < i2c_slave_vector.size(); ++n) Serial.printf("0x%02X,", (uint32_t)i2c_slave_vector[n]); Serial.println(""); } #endif // connect to pool double currentPoolDifficulty = DEFAULT_DIFFICULTY; uint32_t nonce_pool = 0; uint32_t job_pool = 0xFFFFFFFF; uint32_t last_job_time = millis(); while(true) { if(WiFi.status() != WL_CONNECTED){ // WiFi is disconnected, so reconnect now mMonitor.NerdStatus = NM_Connecting; MiningJobStop(job_pool, s_submition_map); WiFi.reconnect(); vTaskDelay(5000 / portTICK_PERIOD_MS); continue; } if(!checkPoolConnection()){ //If server is not reachable add random delay for connection retries //Generate value between 1 and 60 secs MiningJobStop(job_pool, s_submition_map); vTaskDelay(((1 + rand() % 60) * 1000) / portTICK_PERIOD_MS); continue; } if(!isMinerSuscribed) { //Stop miner current jobs mWorker = init_mining_subscribe(); // STEP 1: Pool server connection (SUBSCRIBE) if(!tx_mining_subscribe(client, mWorker)) { client.stop(); MiningJobStop(job_pool, s_submition_map); continue; } strcpy(mWorker.wName, Settings.BtcWallet); strcpy(mWorker.wPass, Settings.PoolPassword); // STEP 2: Pool authorize work (Block Info) tx_mining_auth(client, mWorker.wName, mWorker.wPass); //Don't verifies authoritzation, TODO //tx_mining_auth2(client, mWorker.wName, mWorker.wPass); //Don't verifies authoritzation, TODO // STEP 3: Suggest pool difficulty tx_suggest_difficulty(client, currentPoolDifficulty); isMinerSuscribed=true; uint32_t time_now = millis(); mLastTXtoPool = time_now; last_job_time = time_now; } //Check if pool is down for almost 5minutes and then restart connection with pool (1min=600000ms) if(checkPoolInactivity(KEEPALIVE_TIME_ms, POOLINACTIVITY_TIME_ms)){ //Restart connection Serial.println(" Detected more than 2 min without data form stratum server. Closing socket and reopening..."); client.stop(); isMinerSuscribed=false; MiningJobStop(job_pool, s_submition_map); continue; } { uint32_t time_now = millis(); if (time_now < last_job_time) //32bit wrap last_job_time = time_now; if (time_now >= last_job_time + 10*60*1000) //10minutes without job { client.stop(); isMinerSuscribed=false; MiningJobStop(job_pool, s_submition_map); continue; } } uint32_t hw_midstate[8]; uint32_t diget_mid[8]; uint32_t bake[16]; #if defined(CONFIG_IDF_TARGET_ESP32) uint8_t sha_buffer_swap[128]; #endif //Read pending messages from pool while(client.connected() && client.available()) { String line = client.readStringUntil('\n'); //Serial.println(" Received message from pool"); stratum_method result = parse_mining_method(line); switch (result) { case MINING_NOTIFY: if(parse_mining_notify(line, mJob)) { { std::lock_guard lock(s_job_mutex); s_job_request_list_sw.clear(); #ifdef HARDWARE_SHA265 s_job_request_list_hw.clear(); #endif } //Increse templates readed templates++; job_pool++; s_working_current_job_id = job_pool & 0xFF; //Terminate current job in thread last_job_time = millis(); mLastTXtoPool = last_job_time; uint32_t mh = hashes/1000000; Mhashes += mh; hashes -= mh*1000000; //Prepare data for new jobs mMiner=calculateMiningData(mWorker, mJob); memset(mMiner.bytearray_blockheader+80, 0, 128-80); mMiner.bytearray_blockheader[80] = 0x80; mMiner.bytearray_blockheader[126] = 0x02; mMiner.bytearray_blockheader[127] = 0x80; nerd_mids(diget_mid, mMiner.bytearray_blockheader); nerd_sha256_bake(diget_mid, mMiner.bytearray_blockheader+64, bake); #ifdef HARDWARE_SHA265 #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) esp_sha_acquire_hardware(); sha_hal_hash_block(SHA2_256, mMiner.bytearray_blockheader, 64/4, true); sha_hal_read_digest(SHA2_256, hw_midstate); esp_sha_release_hardware(); #endif #endif #if defined(CONFIG_IDF_TARGET_ESP32) for (int i = 0; i < 32; ++i) ((uint32_t*)sha_buffer_swap)[i] = __builtin_bswap32(((const uint32_t*)(mMiner.bytearray_blockheader))[i]); #endif #ifdef RANDOM_NONCE nonce_pool = RandomGet() & RANDOM_NONCE_MASK; #else #ifdef I2C_SLAVE if (!i2c_slave_vector.empty()) nonce_pool = 0x10000000; else #endif nonce_pool = 0xDA54E700; //nonce 0x00000000 is not possible, start from some random nonce #endif { std::lock_guard lock(s_job_mutex); for (int i = 0; i < 4; ++ i) { #if 1 JobPush( s_job_request_list_sw, job_pool, nonce_pool, NONCE_PER_JOB_SW, currentPoolDifficulty, mMiner.bytearray_blockheader, diget_mid, bake); #ifdef RANDOM_NONCE nonce_pool = RandomGet() & RANDOM_NONCE_MASK; #else nonce_pool += NONCE_PER_JOB_SW; #endif #endif #ifdef HARDWARE_SHA265 #if defined(CONFIG_IDF_TARGET_ESP32) JobPush( s_job_request_list_hw, job_pool, nonce_pool, NONCE_PER_JOB_HW, currentPoolDifficulty, sha_buffer_swap, hw_midstate, bake); #else JobPush( s_job_request_list_hw, job_pool, nonce_pool, NONCE_PER_JOB_HW, currentPoolDifficulty, mMiner.bytearray_blockheader, hw_midstate, bake); #endif #ifdef RANDOM_NONCE nonce_pool = RandomGet() & RANDOM_NONCE_MASK; #else nonce_pool += NONCE_PER_JOB_HW; #endif #endif } } #ifdef I2C_SLAVE //Nonce for nonce_pool starts from 0x10000000 //For i2c slave we give nonces from 0x20000000, that is 0x10000000 nonces per slave i2c_feed_slaves(i2c_slave_vector, job_pool & 0xFF, 0x20, currentPoolDifficulty, mMiner.bytearray_blockheader); #endif } else { Serial.println("Parsing error, need restart"); client.stop(); isMinerSuscribed=false; MiningJobStop(job_pool, s_submition_map); } break; case MINING_SET_DIFFICULTY: parse_mining_set_difficulty(line, currentPoolDifficulty); break; case STRATUM_SUCCESS: { unsigned long id = parse_extract_id(line); auto itt = s_submition_map.find(id); if (itt != s_submition_map.end()) { if (itt->second->diff > best_diff) best_diff = itt->second->diff; if (itt->second->is32bit) shares++; if (itt->second->isValid) { Serial.println("CONGRATULATIONS! Valid block found"); valids++; } s_submition_map.erase(itt); } } break; case STRATUM_PARSE_ERROR: { unsigned long id = parse_extract_id(line); auto itt = s_submition_map.find(id); if (itt != s_submition_map.end()) { Serial.printf("Refuse submition %d\n", id); s_submition_map.erase(itt); } } break; default: Serial.println(" Parsed JSON: unknown"); break; } } std::list> job_result_list; #ifdef I2C_SLAVE if (i2c_slave_vector.empty() || job_pool == 0xFFFFFFFF) { vTaskDelay(50 / portTICK_PERIOD_MS); //Small delay } else { uint32_t time_start = millis(); i2c_hit_slaves(i2c_slave_vector); vTaskDelay(5 / portTICK_PERIOD_MS); uint32_t nonces_done = 0; std::vector nonce_vector = i2c_harvest_slaves(i2c_slave_vector, job_pool & 0xFF, nonces_done); hashes += nonces_done; for (size_t n = 0; n < nonce_vector.size(); ++n) { std::shared_ptr result = std::make_shared(); ((uint32_t*)(mMiner.bytearray_blockheader+64+12))[0] = nonce_vector[n]; if (nerd_sha256d_baked(diget_mid, mMiner.bytearray_blockheader+64, bake, result->hash)) { result->id = job_pool; result->nonce = nonce_vector[n]; result->nonce_count = 0; result->difficulty = diff_from_target(result->hash); job_result_list.push_back(result); } } uint32_t time_end = millis(); //if (nonces_done > 16384) //Serial.printf("Harvest slaves in %dms hashes=%d\n", time_end - time_start, nonces_done); if (time_end > time_start) { uint32_t elapsed = time_end - time_start; if (elapsed < 50) vTaskDelay((50 - elapsed) / portTICK_PERIOD_MS); } else vTaskDelay(40 / portTICK_PERIOD_MS); } #else vTaskDelay(50 / portTICK_PERIOD_MS); //Small delay #endif if (job_pool != 0xFFFFFFFF) { std::lock_guard lock(s_job_mutex); job_result_list.insert(job_result_list.end(), s_job_result_list.begin(), s_job_result_list.end()); s_job_result_list.clear(); #if 1 while (s_job_request_list_sw.size() < 4) { JobPush( s_job_request_list_sw, job_pool, nonce_pool, NONCE_PER_JOB_SW, currentPoolDifficulty, mMiner.bytearray_blockheader, diget_mid, bake); #ifdef RANDOM_NONCE nonce_pool = RandomGet() & RANDOM_NONCE_MASK; #else nonce_pool += NONCE_PER_JOB_SW; #endif } #endif #ifdef HARDWARE_SHA265 while (s_job_request_list_hw.size() < 4) { #if defined(CONFIG_IDF_TARGET_ESP32) JobPush( s_job_request_list_hw, job_pool, nonce_pool, NONCE_PER_JOB_HW, currentPoolDifficulty, sha_buffer_swap, hw_midstate, bake); #else JobPush( s_job_request_list_hw, job_pool, nonce_pool, NONCE_PER_JOB_HW, currentPoolDifficulty, mMiner.bytearray_blockheader, hw_midstate, bake); #endif #ifdef RANDOM_NONCE nonce_pool = RandomGet() & RANDOM_NONCE_MASK; #else nonce_pool += NONCE_PER_JOB_HW; #endif } #endif } while (!job_result_list.empty()) { std::shared_ptr res = job_result_list.front(); job_result_list.pop_front(); hashes += res->nonce_count; if (res->difficulty > currentPoolDifficulty && job_pool == res->id && res->nonce != 0xFFFFFFFF) { if (!client.connected()) break; unsigned long sumbit_id = 0; tx_mining_submit(client, mWorker, mJob, res->nonce, sumbit_id); Serial.print(" - Current diff share: "); Serial.println(res->difficulty,12); Serial.print(" - Current pool diff : "); Serial.println(currentPoolDifficulty,12); Serial.print(" - TX SHARE: "); for (size_t i = 0; i < 32; i++) Serial.printf("%02x", res->hash[i]); Serial.println(""); mLastTXtoPool = millis(); std::shared_ptr submition = std::make_shared(); submition->diff = res->difficulty; submition->is32bit = (res->hash[29] == 0 && res->hash[28] == 0); if (submition->is32bit) { submition->isValid = checkValid(res->hash, mMiner.bytearray_target); } else submition->isValid = false; s_submition_map.insert(std::make_pair(sumbit_id, submition)); if (s_submition_map.size() > 32) s_submition_map.erase(s_submition_map.begin()); } } } } //////////////////THREAD CALLS/////////////////// void minerWorkerSw(void * task_id) { unsigned int miner_id = (uint32_t)task_id; Serial.printf("[MINER] %d Started minerWorkerSw Task!\n", miner_id); std::shared_ptr job; std::shared_ptr result; uint8_t hash[32]; uint32_t wdt_counter = 0; while (1) { { std::lock_guard lock(s_job_mutex); if (result) { if (s_job_result_list.size() < 16) s_job_result_list.push_back(result); result.reset(); } if (!s_job_request_list_sw.empty()) { job = s_job_request_list_sw.front(); s_job_request_list_sw.pop_front(); } else job.reset(); } if (job) { result = std::make_shared(); result->difficulty = job->difficulty; result->nonce = 0xFFFFFFFF; result->id = job->id; result->nonce_count = job->nonce_count; uint8_t job_in_work = job->id & 0xFF; for (uint32_t n = 0; n < job->nonce_count; ++n) { ((uint32_t*)(job->sha_buffer+64+12))[0] = job->nonce_start+n; if (nerd_sha256d_baked(job->midstate, job->sha_buffer+64, job->bake, hash)) { double diff_hash = diff_from_target(hash); if (diff_hash > result->difficulty) { result->difficulty = diff_hash; result->nonce = job->nonce_start+n; memcpy(result->hash, hash, 32); } } if ( (uint16_t)(n & 0xFF) == 0 &&s_working_current_job_id != job_in_work) { result->nonce_count = n+1; break; } } } else vTaskDelay(2 / portTICK_PERIOD_MS); wdt_counter++; if (wdt_counter >= 8) { wdt_counter = 0; esp_task_wdt_reset(); } } } #ifdef HARDWARE_SHA265 #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) static inline void nerd_sha_ll_fill_text_block_sha256(const void *input_text, uint32_t nonce) { uint32_t *data_words = (uint32_t *)input_text; uint32_t *reg_addr_buf = (uint32_t *)(SHA_TEXT_BASE); REG_WRITE(®_addr_buf[0], data_words[0]); REG_WRITE(®_addr_buf[1], data_words[1]); REG_WRITE(®_addr_buf[2], data_words[2]); #if 0 REG_WRITE(®_addr_buf[3], nonce); //REG_WRITE(®_addr_buf[3], data_words[3]); REG_WRITE(®_addr_buf[4], data_words[4]); REG_WRITE(®_addr_buf[5], data_words[5]); REG_WRITE(®_addr_buf[6], data_words[6]); REG_WRITE(®_addr_buf[7], data_words[7]); REG_WRITE(®_addr_buf[8], data_words[8]); REG_WRITE(®_addr_buf[9], data_words[9]); REG_WRITE(®_addr_buf[10], data_words[10]); REG_WRITE(®_addr_buf[11], data_words[11]); REG_WRITE(®_addr_buf[12], data_words[12]); REG_WRITE(®_addr_buf[13], data_words[13]); REG_WRITE(®_addr_buf[14], data_words[14]); REG_WRITE(®_addr_buf[15], data_words[15]); #else REG_WRITE(®_addr_buf[3], nonce); REG_WRITE(®_addr_buf[4], 0x00000080); REG_WRITE(®_addr_buf[5], 0x00000000); REG_WRITE(®_addr_buf[6], 0x00000000); REG_WRITE(®_addr_buf[7], 0x00000000); REG_WRITE(®_addr_buf[8], 0x00000000); REG_WRITE(®_addr_buf[9], 0x00000000); REG_WRITE(®_addr_buf[10], 0x00000000); REG_WRITE(®_addr_buf[11], 0x00000000); REG_WRITE(®_addr_buf[12], 0x00000000); REG_WRITE(®_addr_buf[13], 0x00000000); REG_WRITE(®_addr_buf[14], 0x00000000); REG_WRITE(®_addr_buf[15], 0x80020000); #endif } static inline void nerd_sha_ll_fill_text_block_sha256_inter() { uint32_t *reg_addr_buf = (uint32_t *)(SHA_TEXT_BASE); DPORT_INTERRUPT_DISABLE(); REG_WRITE(®_addr_buf[0], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 0 * 4)); REG_WRITE(®_addr_buf[1], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 1 * 4)); REG_WRITE(®_addr_buf[2], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 2 * 4)); REG_WRITE(®_addr_buf[3], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 3 * 4)); REG_WRITE(®_addr_buf[4], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 4 * 4)); REG_WRITE(®_addr_buf[5], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 5 * 4)); REG_WRITE(®_addr_buf[6], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 6 * 4)); REG_WRITE(®_addr_buf[7], DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 7 * 4)); DPORT_INTERRUPT_RESTORE(); REG_WRITE(®_addr_buf[8], 0x00000080); REG_WRITE(®_addr_buf[9], 0x00000000); REG_WRITE(®_addr_buf[10], 0x00000000); REG_WRITE(®_addr_buf[11], 0x00000000); REG_WRITE(®_addr_buf[12], 0x00000000); REG_WRITE(®_addr_buf[13], 0x00000000); REG_WRITE(®_addr_buf[14], 0x00000000); REG_WRITE(®_addr_buf[15], 0x00010000); } static inline void nerd_sha_ll_read_digest(void* ptr) { DPORT_INTERRUPT_DISABLE(); ((uint32_t*)ptr)[0] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 0 * 4); ((uint32_t*)ptr)[1] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 1 * 4); ((uint32_t*)ptr)[2] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 2 * 4); ((uint32_t*)ptr)[3] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 3 * 4); ((uint32_t*)ptr)[4] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 4 * 4); ((uint32_t*)ptr)[5] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 5 * 4); ((uint32_t*)ptr)[6] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 6 * 4); ((uint32_t*)ptr)[7] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 7 * 4); DPORT_INTERRUPT_RESTORE(); } static inline bool nerd_sha_ll_read_digest_if(void* ptr) { DPORT_INTERRUPT_DISABLE(); uint32_t last = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 7 * 4); #if 1 if ( (uint16_t)(last >> 16) != 0) { DPORT_INTERRUPT_RESTORE(); return false; } #endif ((uint32_t*)ptr)[7] = last; ((uint32_t*)ptr)[0] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 0 * 4); ((uint32_t*)ptr)[1] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 1 * 4); ((uint32_t*)ptr)[2] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 2 * 4); ((uint32_t*)ptr)[3] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 3 * 4); ((uint32_t*)ptr)[4] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 4 * 4); ((uint32_t*)ptr)[5] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 5 * 4); ((uint32_t*)ptr)[6] = DPORT_SEQUENCE_REG_READ(SHA_H_BASE + 6 * 4); DPORT_INTERRUPT_RESTORE(); return true; } static inline void nerd_sha_ll_write_digest(void *digest_state) { uint32_t *digest_state_words = (uint32_t *)digest_state; uint32_t *reg_addr_buf = (uint32_t *)(SHA_H_BASE); REG_WRITE(®_addr_buf[0], digest_state_words[0]); REG_WRITE(®_addr_buf[1], digest_state_words[1]); REG_WRITE(®_addr_buf[2], digest_state_words[2]); REG_WRITE(®_addr_buf[3], digest_state_words[3]); REG_WRITE(®_addr_buf[4], digest_state_words[4]); REG_WRITE(®_addr_buf[5], digest_state_words[5]); REG_WRITE(®_addr_buf[6], digest_state_words[6]); REG_WRITE(®_addr_buf[7], digest_state_words[7]); } static inline void nerd_sha_hal_wait_idle() { while (REG_READ(SHA_BUSY_REG)) {} } //#define VALIDATION void minerWorkerHw(void * task_id) { unsigned int miner_id = (uint32_t)task_id; Serial.printf("[MINER] %d Started minerWorkerHw Task!\n", miner_id); std::shared_ptr job; std::shared_ptr result; uint8_t interResult[64]; uint8_t hash[32]; uint8_t digest_mid[32]; uint8_t sha_buffer[64]; uint32_t wdt_counter = 0; #ifdef VALIDATION uint8_t doubleHash[32]; uint32_t diget_mid[8]; uint32_t bake[16]; #endif while (1) { { std::lock_guard lock(s_job_mutex); if (result) { if (s_job_result_list.size() < 16) s_job_result_list.push_back(result); result.reset(); } if (!s_job_request_list_hw.empty()) { job = s_job_request_list_hw.front(); s_job_request_list_hw.pop_front(); } else job.reset(); } if (job) { result = std::make_shared(); result->id = job->id; result->nonce = 0xFFFFFFFF; result->nonce_count = job->nonce_count; result->difficulty = job->difficulty; uint8_t job_in_work = job->id & 0xFF; memcpy(digest_mid, job->midstate, sizeof(digest_mid)); memcpy(sha_buffer, job->sha_buffer+64, sizeof(sha_buffer)); #ifdef VALIDATION nerd_mids(diget_mid, job->sha_buffer); nerd_sha256_bake(diget_mid, job->sha_buffer+64, bake); #endif esp_sha_acquire_hardware(); REG_WRITE(SHA_MODE_REG, SHA2_256); uint32_t nend = job->nonce_start + job->nonce_count; for (uint32_t n = job->nonce_start; n < nend; ++n) { //nerd_sha_hal_wait_idle(); nerd_sha_ll_write_digest(digest_mid); //nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256(sha_buffer, n); //sha_ll_continue_block(SHA2_256); REG_WRITE(SHA_CONTINUE_REG, 1); sha_ll_load(SHA2_256); nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256_inter(); //sha_ll_start_block(SHA2_256); REG_WRITE(SHA_START_REG, 1); sha_ll_load(SHA2_256); nerd_sha_hal_wait_idle(); if (nerd_sha_ll_read_digest_if(hash)) { //Serial.printf("Hw 16bit Share, nonce=0x%X\n", n); #ifdef VALIDATION //Validation ((uint32_t*)(job->sha_buffer+64+12))[0] = n; nerd_sha256d_baked(diget_mid, job->sha_buffer+64, bake, doubleHash); for (int i = 0; i < 32; ++i) { if (hash[i] != doubleHash[i]) { Serial.println("***HW sha256 esp32s3 bug detected***"); break; } } #endif //~5 per second double diff_hash = diff_from_target(hash); if (diff_hash > result->difficulty) { if (isSha256Valid(hash)) { result->difficulty = diff_hash; result->nonce = n; memcpy(result->hash, hash, sizeof(hash)); } } } if ( (uint8_t)(n & 0xFF) == 0 && s_working_current_job_id != job_in_work) { result->nonce_count = n-job->nonce_start+1; break; } } esp_sha_release_hardware(); } else vTaskDelay(2 / portTICK_PERIOD_MS); wdt_counter++; if (wdt_counter >= 8) { wdt_counter = 0; esp_task_wdt_reset(); } } } #endif //#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) #if defined(CONFIG_IDF_TARGET_ESP32) static inline bool nerd_sha_ll_read_digest_swap_if(void* ptr) { DPORT_INTERRUPT_DISABLE(); uint32_t fin = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 7 * 4); if ( (uint32_t)(fin & 0xFFFF) != 0) { DPORT_INTERRUPT_RESTORE(); return false; } ((uint32_t*)ptr)[7] = __builtin_bswap32(fin); ((uint32_t*)ptr)[0] = __builtin_bswap32(DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 0 * 4)); ((uint32_t*)ptr)[1] = __builtin_bswap32(DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 1 * 4)); ((uint32_t*)ptr)[2] = __builtin_bswap32(DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 2 * 4)); ((uint32_t*)ptr)[3] = __builtin_bswap32(DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 3 * 4)); ((uint32_t*)ptr)[4] = __builtin_bswap32(DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 4 * 4)); ((uint32_t*)ptr)[5] = __builtin_bswap32(DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 5 * 4)); ((uint32_t*)ptr)[6] = __builtin_bswap32(DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 6 * 4)); DPORT_INTERRUPT_RESTORE(); return true; } static inline void nerd_sha_ll_read_digest(void* ptr) { DPORT_INTERRUPT_DISABLE(); ((uint32_t*)ptr)[0] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 0 * 4); ((uint32_t*)ptr)[1] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 1 * 4); ((uint32_t*)ptr)[2] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 2 * 4); ((uint32_t*)ptr)[3] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 3 * 4); ((uint32_t*)ptr)[4] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 4 * 4); ((uint32_t*)ptr)[5] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 5 * 4); ((uint32_t*)ptr)[6] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 6 * 4); ((uint32_t*)ptr)[7] = DPORT_SEQUENCE_REG_READ(SHA_TEXT_BASE + 7 * 4); DPORT_INTERRUPT_RESTORE(); } static inline void nerd_sha_hal_wait_idle() { while (DPORT_REG_READ(SHA_256_BUSY_REG)) {} } static inline void nerd_sha_ll_fill_text_block_sha256(const void *input_text) { uint32_t *data_words = (uint32_t *)input_text; uint32_t *reg_addr_buf = (uint32_t *)(SHA_TEXT_BASE); reg_addr_buf[0] = data_words[0]; reg_addr_buf[1] = data_words[1]; reg_addr_buf[2] = data_words[2]; reg_addr_buf[3] = data_words[3]; reg_addr_buf[4] = data_words[4]; reg_addr_buf[5] = data_words[5]; reg_addr_buf[6] = data_words[6]; reg_addr_buf[7] = data_words[7]; reg_addr_buf[8] = data_words[8]; reg_addr_buf[9] = data_words[9]; reg_addr_buf[10] = data_words[10]; reg_addr_buf[11] = data_words[11]; reg_addr_buf[12] = data_words[12]; reg_addr_buf[13] = data_words[13]; reg_addr_buf[14] = data_words[14]; reg_addr_buf[15] = data_words[15]; } static inline void nerd_sha_ll_fill_text_block_sha256_upper(const void *input_text, uint32_t nonce) { uint32_t *data_words = (uint32_t *)input_text; uint32_t *reg_addr_buf = (uint32_t *)(SHA_TEXT_BASE); reg_addr_buf[0] = data_words[0]; reg_addr_buf[1] = data_words[1]; reg_addr_buf[2] = data_words[2]; reg_addr_buf[3] = __builtin_bswap32(nonce); #if 1 reg_addr_buf[4] = 0x80000000; reg_addr_buf[5] = 0x00000000; reg_addr_buf[6] = 0x00000000; reg_addr_buf[7] = 0x00000000; reg_addr_buf[8] = 0x00000000; reg_addr_buf[9] = 0x00000000; reg_addr_buf[10] = 0x00000000; reg_addr_buf[11] = 0x00000000; reg_addr_buf[12] = 0x00000000; reg_addr_buf[13] = 0x00000000; reg_addr_buf[14] = 0x00000000; reg_addr_buf[15] = 0x00000280; #else reg_addr_buf[4] = data_words[4]; reg_addr_buf[5] = data_words[5]; reg_addr_buf[6] = data_words[6]; reg_addr_buf[7] = data_words[7]; reg_addr_buf[8] = data_words[8]; reg_addr_buf[9] = data_words[9]; reg_addr_buf[10] = data_words[10]; reg_addr_buf[11] = data_words[11]; reg_addr_buf[12] = data_words[12]; reg_addr_buf[13] = data_words[13]; reg_addr_buf[14] = data_words[14]; reg_addr_buf[15] = data_words[15]; #endif } static inline void nerd_sha_ll_fill_text_block_sha256_double() { uint32_t *reg_addr_buf = (uint32_t *)(SHA_TEXT_BASE); #if 0 //No change reg_addr_buf[0] = data_words[0]; reg_addr_buf[1] = data_words[1]; reg_addr_buf[2] = data_words[2]; reg_addr_buf[3] = data_words[3]; reg_addr_buf[4] = data_words[4]; reg_addr_buf[5] = data_words[5]; reg_addr_buf[6] = data_words[6]; reg_addr_buf[7] = data_words[7]; #endif reg_addr_buf[8] = 0x80000000; reg_addr_buf[9] = 0x00000000; reg_addr_buf[10] = 0x00000000; reg_addr_buf[11] = 0x00000000; reg_addr_buf[12] = 0x00000000; reg_addr_buf[13] = 0x00000000; reg_addr_buf[14] = 0x00000000; reg_addr_buf[15] = 0x00000100; } void minerWorkerHw(void * task_id) { unsigned int miner_id = (uint32_t)task_id; Serial.printf("[MINER] %d Started minerWorkerHwEsp32D Task!\n", miner_id); std::shared_ptr job; std::shared_ptr result; uint8_t hash[32]; uint8_t sha_buffer[128]; while (1) { { std::lock_guard lock(s_job_mutex); if (result) { if (s_job_result_list.size() < 16) s_job_result_list.push_back(result); result.reset(); } if (!s_job_request_list_hw.empty()) { job = s_job_request_list_hw.front(); s_job_request_list_hw.pop_front(); } else job.reset(); } if (job) { result = std::make_shared(); result->id = job->id; result->nonce = 0xFFFFFFFF; result->nonce_count = job->nonce_count; result->difficulty = job->difficulty; uint8_t job_in_work = job->id & 0xFF; memcpy(sha_buffer, job->sha_buffer, 80); esp_sha_lock_engine(SHA2_256); for (uint32_t n = 0; n < job->nonce_count; ++n) { //((uint32_t*)(sha_buffer+64+12))[0] = __builtin_bswap32(job->nonce_start+n); //sha_hal_hash_block(SHA2_256, s_test_buffer, 64/4, true); //nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256(sha_buffer); sha_ll_start_block(SHA2_256); //sha_hal_hash_block(SHA2_256, s_test_buffer+64, 64/4, false); nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256_upper(sha_buffer+64, job->nonce_start+n); sha_ll_continue_block(SHA2_256); nerd_sha_hal_wait_idle(); sha_ll_load(SHA2_256); //sha_hal_hash_block(SHA2_256, interResult, 64/4, true); nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256_double(); sha_ll_start_block(SHA2_256); nerd_sha_hal_wait_idle(); sha_ll_load(SHA2_256); if (nerd_sha_ll_read_digest_swap_if(hash)) { //~5 per second double diff_hash = diff_from_target(hash); if (diff_hash > result->difficulty) { if (isSha256Valid(hash)) { result->difficulty = diff_hash; result->nonce = job->nonce_start+n; memcpy(result->hash, hash, sizeof(hash)); } } } if ( (uint8_t)(n & 0xFF) == 0 && s_working_current_job_id != job_in_work) { result->nonce_count = n+1; break; } } esp_sha_unlock_engine(SHA2_256); } else vTaskDelay(2 / portTICK_PERIOD_MS); esp_task_wdt_reset(); } } #endif //CONFIG_IDF_TARGET_ESP32 #endif //HARDWARE_SHA265 #define DELAY 100 #define REDRAW_EVERY 10 void restoreStat() { if(!Settings.saveStats) return; esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { Serial.printf("[MONITOR] NVS partition is full or has invalid version, erasing...\n"); nvs_flash_init(); } ret = nvs_open("state", NVS_READWRITE, &stat_handle); size_t required_size = sizeof(double); nvs_get_blob(stat_handle, "best_diff", &best_diff, &required_size); nvs_get_u32(stat_handle, "Mhashes", &Mhashes); uint32_t nv_shares, nv_valids; nvs_get_u32(stat_handle, "shares", &nv_shares); nvs_get_u32(stat_handle, "valids", &nv_valids); shares = nv_shares; valids = nv_valids; nvs_get_u32(stat_handle, "templates", &templates); nvs_get_u64(stat_handle, "upTime", &upTime); uint32_t crc = crc32_reset(); crc = crc32_add(crc, &best_diff, sizeof(best_diff)); crc = crc32_add(crc, &Mhashes, sizeof(Mhashes)); crc = crc32_add(crc, &nv_shares, sizeof(nv_shares)); crc = crc32_add(crc, &nv_valids, sizeof(nv_valids)); crc = crc32_add(crc, &templates, sizeof(templates)); crc = crc32_add(crc, &upTime, sizeof(upTime)); crc = crc32_finish(crc); uint32_t nv_crc; nvs_get_u32(stat_handle, "crc32", &nv_crc); if (nv_crc != crc) { best_diff = 0.0; Mhashes = 0; shares = 0; valids = 0; templates = 0; upTime = 0; } } void saveStat() { if(!Settings.saveStats) return; Serial.printf("[MONITOR] Saving stats\n"); nvs_set_blob(stat_handle, "best_diff", &best_diff, sizeof(best_diff)); nvs_set_u32(stat_handle, "Mhashes", Mhashes); nvs_set_u32(stat_handle, "shares", shares); nvs_set_u32(stat_handle, "valids", valids); nvs_set_u32(stat_handle, "templates", templates); nvs_set_u64(stat_handle, "upTime", upTime); uint32_t crc = crc32_reset(); crc = crc32_add(crc, &best_diff, sizeof(best_diff)); crc = crc32_add(crc, &Mhashes, sizeof(Mhashes)); uint32_t nv_shares = shares; uint32_t nv_valids = valids; crc = crc32_add(crc, &nv_shares, sizeof(nv_shares)); crc = crc32_add(crc, &nv_valids, sizeof(nv_valids)); crc = crc32_add(crc, &templates, sizeof(templates)); crc = crc32_add(crc, &upTime, sizeof(upTime)); crc = crc32_finish(crc); nvs_set_u32(stat_handle, "crc32", crc); } void resetStat() { Serial.printf("[MONITOR] Resetting NVS stats\n"); templates = hashes = Mhashes = totalKHashes = elapsedKHs = upTime = shares = valids = 0; best_diff = 0.0; saveStat(); } void runMonitor(void *name) { Serial.println("[MONITOR] started"); restoreStat(); unsigned long mLastCheck = 0; resetToFirstScreen(); unsigned long frame = 0; uint32_t seconds_elapsed = 0; totalKHashes = (Mhashes * 1000) + hashes / 1000; uint32_t last_update_millis = millis(); uint32_t uptime_frac = 0; while (1) { uint32_t now_millis = millis(); if (now_millis < last_update_millis) now_millis = last_update_millis; uint32_t mElapsed = now_millis - mLastCheck; if (mElapsed >= 1000) { mLastCheck = now_millis; last_update_millis = now_millis; unsigned long currentKHashes = (Mhashes * 1000) + hashes / 1000; elapsedKHs = currentKHashes - totalKHashes; totalKHashes = currentKHashes; uptime_frac += mElapsed; while (uptime_frac >= 1000) { uptime_frac -= 1000; upTime ++; } drawCurrentScreen(mElapsed); // Monitor state when hashrate is 0.0 if (elapsedKHs == 0) { Serial.printf(">>> [i] Miner: newJob>%s / inRun>%s) - Client: connected>%s / subscribed>%s / wificonnected>%s\n", "true",//(1) ? "true" : "false", isMinerSuscribed ? "true" : "false", client.connected() ? "true" : "false", isMinerSuscribed ? "true" : "false", WiFi.status() == WL_CONNECTED ? "true" : "false"); } #ifdef DEBUG_MEMORY Serial.printf("### [Total Heap / Free heap / Min free heap]: %d / %d / %d \n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap()); Serial.printf("### Max stack usage: %d\n", uxTaskGetStackHighWaterMark(NULL)); #endif seconds_elapsed++; if(seconds_elapsed % (saveIntervals[currentIntervalIndex]) == 0){ saveStat(); seconds_elapsed = 0; if(currentIntervalIndex < saveIntervalsSize - 1) currentIntervalIndex++; } } animateCurrentScreen(frame); doLedStuff(frame); vTaskDelay(DELAY / portTICK_PERIOD_MS); frame++; } }