Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,11 @@ void runStratumWorker(void *name) {

//Prepare data for new jobs
mMiner=calculateMiningData(mWorker, mJob);
// Only job_id and ntime are needed for share submission.
// Release potentially very large coinbase strings now.
mJob.coinb1.clear();
mJob.coinb2.clear();
mJob.merkle_branch.clear();

memset(mMiner.bytearray_blockheader+80, 0, 128-80);
mMiner.bytearray_blockheader[80] = 0x80;
Expand Down
82 changes: 52 additions & 30 deletions src/stratum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,11 @@ unsigned long getNextId(unsigned long 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 verifyPayload(const String& line){
return !line.isEmpty();
}

bool checkError(const StaticJsonDocument<BUFFER_JSON_DOC> doc) {
bool checkError(const StaticJsonDocument<BUFFER_JSON_DOC>& doc) {

if (!doc.containsKey("error")) return false;

Expand Down Expand Up @@ -87,7 +83,7 @@ bool tx_mining_subscribe(WiFiClient& client, mining_subscribe& mSubscribe)

bool parse_mining_subscribe(String line, mining_subscribe& mSubscribe)
{
if(!verifyPayload(&line)) return false;
if(!verifyPayload(line)) return false;
Serial.print(" Receiving: "); Serial.println(line);

DeserializationError error = deserializeJson(doc, line);
Expand Down Expand Up @@ -138,14 +134,25 @@ bool tx_mining_auth(WiFiClient& client, const char * user, const char * pass)
}


stratum_method parse_mining_method(String line)
stratum_method parse_mining_method(const String& line)
{
if(!verifyPayload(&line)) return STRATUM_PARSE_ERROR;
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;
// Only retain the fields needed to classify a message. A mining.notify
// can contain a very large coinbase transaction, which must not consume
// the fixed JSON document merely to inspect the method name.
StaticJsonDocument<64> filter;
filter["method"] = true;
filter["error"] = true;
DeserializationError error = deserializeJson(
doc, line, DeserializationOption::Filter(filter));

if (error) {
Serial.print(" JSON classification error: "); Serial.println(error.c_str());
return STRATUM_PARSE_ERROR;
}
if (checkError(doc)) return STRATUM_PARSE_ERROR;

if (!doc.containsKey("method")) {
// "error":null means success
Expand All @@ -165,25 +172,40 @@ stratum_method parse_mining_method(String line)
return result;
}

bool parse_mining_notify(String line, mining_job& mJob)
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(!verifyPayload(line)) return false;

if (error) return false;
// Zero-copy mode keeps the large coinbase strings in the input buffer.
// The values needed after this function returns are copied into mJob.
DeserializationError error = deserializeJson(doc, line.begin());

if (error) {
Serial.print(" Mining notify JSON error: "); Serial.println(error.c_str());
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
JsonArray params = doc["params"];
if (params.size() < 9 || !params[4].is<JsonArray>()) return false;

mJob.job_id = String((const char*) params[0]);
mJob.prev_block_hash = String((const char*) params[1]);
mJob.coinb1 = String((const char*) params[2]);
mJob.coinb2 = String((const char*) params[3]);
mJob.merkle_branch.clear();
JsonArray merkleBranch = params[4];
mJob.merkle_branch.reserve(merkleBranch.size());
for (JsonVariant merkleElement : merkleBranch) {
const char* value = merkleElement.as<const char*>();
if (!value) return false;
mJob.merkle_branch.emplace_back(value);
}
mJob.version = String((const char*) params[5]);
mJob.nbits = String((const char*) params[6]);
mJob.ntime = String((const char*) params[7]);
mJob.clean_jobs = params[8]; //bool

#ifdef DEBUG_MINING
Serial.print(" job_id: "); Serial.println(mJob.job_id);
Expand All @@ -205,7 +227,7 @@ bool parse_mining_notify(String line, mining_job& mJob)
}


bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job mJob, unsigned long nonce, unsigned long &submit_id)
bool tx_mining_submit(WiFiClient& client, const mining_subscribe& mWorker, const mining_job& mJob, unsigned long nonce, unsigned long &submit_id)
{
char payload[BUFFER] = {0};

Expand All @@ -230,7 +252,7 @@ bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job m
bool parse_mining_set_difficulty(String line, double& difficulty)
{
Serial.println(" Parsing Method [SET DIFFICULTY]");
if(!verifyPayload(&line)) return false;
if(!verifyPayload(line)) return false;

DeserializationError error = deserializeJson(doc, line);

Expand Down Expand Up @@ -268,4 +290,4 @@ unsigned long parse_extract_id(const String &line)
unsigned long id = doc["id"];

return id;
}
}
15 changes: 8 additions & 7 deletions src/stratum.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <vector>

#define MAX_MERKLE_BRANCHES 32
#define HASH_SIZE 32
Expand All @@ -30,7 +31,7 @@ typedef struct {
String coinb1;
String coinb2;
String nbits;
JsonArray merkle_branch;
std::vector<String> merkle_branch;
String version;
uint32_t target;
String ntime;
Expand All @@ -46,8 +47,8 @@ typedef enum {
} stratum_method;

unsigned long getNextId(unsigned long id);
bool verifyPayload (String* line);
bool checkError(const StaticJsonDocument<BUFFER_JSON_DOC> doc);
bool verifyPayload(const String& line);
bool checkError(const StaticJsonDocument<BUFFER_JSON_DOC>& doc);

//Method Mining.subscribe
mining_subscribe init_mining_subscribe(void);
Expand All @@ -56,16 +57,16 @@ 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);
stratum_method parse_mining_method(const 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, unsigned long &submit_id);
bool tx_mining_submit(WiFiClient& client, const mining_subscribe& mWorker, const mining_job& mJob, unsigned long nonce, unsigned long &submit_id);

//Difficulty Methods
bool tx_suggest_difficulty(WiFiClient& client, double difficulty);
bool parse_mining_set_difficulty(String line, double& difficulty);

unsigned long parse_extract_id(const String &line);

#endif // STRATUM_API_H
#endif // STRATUM_API_H
65 changes: 37 additions & 28 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,28 @@ miner_data init_miner_data(void){
return newMinerData;
}

miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){
static bool sha256UpdateHex(mbedtls_sha256_context& ctx, const String& hexString)
{
if ((hexString.length() & 1) != 0) {
Serial.println(" Invalid odd-length hex string in coinbase");
return false;
}

uint8_t bytes[64];
for (size_t offset = 0; offset < hexString.length(); offset += sizeof(bytes) * 2) {
size_t hexLength = min(static_cast<size_t>(sizeof(bytes) * 2),
static_cast<size_t>(hexString.length() - offset));
size_t byteLength = hexLength / 2;
const char* input = hexString.c_str() + offset;
for (size_t i = 0; i < byteLength; ++i) {
bytes[i] = (hex(input[i * 2]) << 4) | hex(input[i * 2 + 1]);
}
mbedtls_sha256_update_ret(&ctx, bytes, byteLength);
}
return true;
}

miner_data calculateMiningData(mining_subscribe& mWorker, const mining_job& mJob){

miner_data mMiner = init_miner_data();

Expand Down Expand Up @@ -226,37 +247,26 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){
}
//mWorker.extranonce2 = "00000002";

//get coinbase - coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest()
// Use char buffer instead of String concatenation to avoid memory leaks
static char coinbase_buffer[512]; // Static buffer to avoid repeated allocation
snprintf(coinbase_buffer, sizeof(coinbase_buffer), "%s%s%s%s",
mJob.coinb1.c_str(), mWorker.extranonce1.c_str(),
mWorker.extranonce2.c_str(), mJob.coinb2.c_str());
Serial.print(" coinbase: "); Serial.println(coinbase_buffer);
size_t str_len = strlen(coinbase_buffer)/2;
uint8_t bytearray[str_len];

size_t res = to_byte_array(coinbase_buffer, str_len*2, bytearray);

#ifdef DEBUG_MINING
Serial.print(" extranonce2: "); Serial.println(mWorker.extranonce2);
Serial.print(" coinbase: "); Serial.println(coinbase_buffer);
Serial.print(" coinbase bytes - size: "); Serial.println(res);
for (size_t i = 0; i < res; i++)
Serial.printf("%02x", bytearray[i]);
Serial.println("---");
#endif

// Hash the coinbase as a stream. Pool jobs can contain transactions far
// larger than the old fixed 512-byte text buffer.
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);
bool validCoinbase =
sha256UpdateHex(ctx, mJob.coinb1) &&
sha256UpdateHex(ctx, mWorker.extranonce1) &&
sha256UpdateHex(ctx, mWorker.extranonce2) &&
sha256UpdateHex(ctx, mJob.coinb2);
mbedtls_sha256_finish_ret(&ctx, interResult);

if (!validCoinbase) {
memset(interResult, 0, sizeof(interResult));
}

mbedtls_sha256_starts_ret(&ctx,0);
mbedtls_sha256_update_ret(&ctx, interResult, 32);
mbedtls_sha256_finish_ret(&ctx, shaResult);
Expand All @@ -275,7 +285,7 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){

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];
const char* merkle_element = mJob.merkle_branch[k].c_str();
uint8_t bytearray[32];
size_t res = to_byte_array(merkle_element, 64, bytearray);

Expand Down Expand Up @@ -321,16 +331,16 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){
Serial.printf("%02x", mMiner.merkle_result[i]);
snprintf(&merkle_root[i*2], 3, "%02x", mMiner.merkle_result[i]);
}
merkle_root[65] = 0;
merkle_root[64] = 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;
size_t str_len = blockheader.length()/2;

//uint8_t bytearray_blockheader[str_len];
res = to_byte_array(blockheader.c_str(), str_len*2, mMiner.bytearray_blockheader);
size_t res = to_byte_array(blockheader.c_str(), str_len*2, mMiner.bytearray_blockheader);

#ifdef DEBUG_MINING
Serial.println(" blockheader: "); Serial.print(blockheader);
Expand Down Expand Up @@ -605,4 +615,3 @@ uint32_t crc32_finish(uint32_t crc32)
{
return crc32 ^ 0xFFFFFFFF;
}

4 changes: 2 additions & 2 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ 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);
bool isSha256Valid(const void* sha256);
miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob);
miner_data calculateMiningData(mining_subscribe& mWorker, const mining_job& mJob);
bool checkValid(unsigned char* hash, unsigned char* target);
void suffix_string(double val, char *buf, size_t bufsiz, int sigdigits);

Expand All @@ -31,4 +31,4 @@ uint32_t crc32_finish(uint32_t crc32);



#endif // UTILS_API_H
#endif // UTILS_API_H