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