mirror of
https://github.com/BitMaker-hub/NerdMiner_v2.git
synced 2025-10-10 13:15:25 +02:00
1277 lines
44 KiB
C++
1277 lines
44 KiB
C++
#include <Arduino.h>
|
|
#include <ArduinoJson.h>
|
|
#include <WiFi.h>
|
|
#include <esp_task_wdt.h>
|
|
#include <nvs_flash.h>
|
|
#include <nvs.h>
|
|
//#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 <mutex>
|
|
#include <list>
|
|
#include <map>
|
|
#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 <sha/sha_dma.h>
|
|
#include <hal/sha_hal.h>
|
|
#include <hal/sha_ll.h>
|
|
|
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
|
#include <sha/sha_parallel_engine.h>
|
|
#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<std::shared_ptr<JobRequest>> s_job_request_list_sw;
|
|
#ifdef HARDWARE_SHA265
|
|
std::list<std::shared_ptr<JobRequest>> s_job_request_list_hw;
|
|
#endif
|
|
std::list<std::shared_ptr<JobResult>> s_job_result_list;
|
|
static volatile uint8_t s_working_current_job_id = 0xFF;
|
|
|
|
static void JobPush(std::list<std::shared_ptr<JobRequest>> &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<JobRequest> job = std::make_shared<JobRequest>();
|
|
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<uint32_t, std::shared_ptr<Submition>> & submition_map)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> 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<uint32_t, std::shared_ptr<Submition>> s_submition_map;
|
|
|
|
#ifdef I2C_SLAVE
|
|
std::vector<uint8_t> 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<std::mutex> 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<std::mutex> 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<std::shared_ptr<JobResult>> 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<uint32_t> 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<JobResult> result = std::make_shared<JobResult>();
|
|
((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<std::mutex> 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<JobResult> 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> submition = std::make_shared<Submition>();
|
|
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<JobRequest> job;
|
|
std::shared_ptr<JobResult> result;
|
|
uint8_t hash[32];
|
|
uint32_t wdt_counter = 0;
|
|
while (1)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> 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<JobResult>();
|
|
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<JobRequest> job;
|
|
std::shared_ptr<JobResult> 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<std::mutex> 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<JobResult>();
|
|
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<JobRequest> job;
|
|
std::shared_ptr<JobResult> result;
|
|
uint8_t hash[32];
|
|
uint8_t sha_buffer[128];
|
|
|
|
while (1)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> 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<JobResult>();
|
|
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++;
|
|
}
|
|
}
|