Refactor stratum code

This commit is contained in:
Erik Olof Gunnar Andersson 2025-02-10 19:26:14 +01:00
parent deaa291675
commit b843bcbbb1
24 changed files with 548 additions and 492 deletions

View File

@ -120,23 +120,19 @@ void ASIC_set_job_difficulty_mask(GlobalState * GLOBAL_STATE, uint8_t mask) {
}
// .send_work_fn = BM1366_send_work,
void ASIC_send_work(GlobalState * GLOBAL_STATE, void * next_job) {
int ASIC_send_work(GlobalState * GLOBAL_STATE, void * next_job) {
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
BM1397_send_work(GLOBAL_STATE, next_job);
break;
return BM1397_send_work(GLOBAL_STATE, next_job);
case DEVICE_ULTRA:
BM1366_send_work(GLOBAL_STATE, next_job);
break;
return BM1366_send_work(GLOBAL_STATE, next_job);
case DEVICE_SUPRA:
BM1368_send_work(GLOBAL_STATE, next_job);
break;
return BM1368_send_work(GLOBAL_STATE, next_job);
case DEVICE_GAMMA:
case DEVICE_GAMMATURBO:
BM1370_send_work(GLOBAL_STATE, next_job);
break;
return BM1370_send_work(GLOBAL_STATE, next_job);
default:
return;
return -1;
}
}

View File

@ -389,9 +389,8 @@ void BM1366_set_job_difficulty_mask(int difficulty)
static uint8_t id = 0;
void BM1366_send_work(void * pvParameters, bm_job * next_bm_job)
int BM1366_send_work(void * pvParameters, bm_job * next_bm_job)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
BM1366_job job;
@ -405,22 +404,20 @@ void BM1366_send_work(void * pvParameters, bm_job * next_bm_job)
memcpy(job.prev_block_hash, next_bm_job->prev_block_hash_be, 32);
memcpy(&job.version, &next_bm_job->version, 4);
//debug sent jobs - this can get crazy if the interval is short
#if BM1366_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X (%d)", job.job_id, next_bm_job->connection_id);
#endif
if (GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] != NULL) {
free_bm_job(GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id]);
}
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] = next_bm_job;
pthread_mutex_lock(&GLOBAL_STATE->valid_jobs_lock);
GLOBAL_STATE->valid_jobs[job.job_id] = 1;
pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock);
//debug sent jobs - this can get crazy if the interval is short
#if BM1366_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X", job.job_id);
#endif
_send_BM1366((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t *)&job, sizeof(BM1366_job), BM1366_DEBUG_WORK);
return job.job_id;
}
asic_result * BM1366_receive_work(void)
@ -470,7 +467,6 @@ static uint32_t reverse_uint32(uint32_t val)
task_result * BM1366_proccess_work(void * pvParameters)
{
asic_result * asic_result = BM1366_receive_work();
if (asic_result == NULL) {
@ -485,11 +481,6 @@ task_result * BM1366_proccess_work(void * pvParameters)
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
if (GLOBAL_STATE->valid_jobs[job_id] == 0) {
ESP_LOGW(TAG, "Invalid job found, 0x%02X", job_id);
return NULL;
}
uint32_t rolled_version = GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->version | version_bits;
result.job_id = job_id;

View File

@ -314,7 +314,7 @@ void BM1368_set_job_difficulty_mask(int difficulty)
static uint8_t id = 0;
void BM1368_send_work(void * pvParameters, bm_job * next_bm_job)
int BM1368_send_work(void * pvParameters, bm_job * next_bm_job)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
@ -329,21 +329,19 @@ void BM1368_send_work(void * pvParameters, bm_job * next_bm_job)
memcpy(job.prev_block_hash, next_bm_job->prev_block_hash_be, 32);
memcpy(&job.version, &next_bm_job->version, 4);
#if BM1368_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X (%d)", job.job_id, next_bm_job->connection_id);
#endif
if (GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] != NULL) {
free_bm_job(GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id]);
}
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] = next_bm_job;
pthread_mutex_lock(&GLOBAL_STATE->valid_jobs_lock);
GLOBAL_STATE->valid_jobs[job.job_id] = 1;
pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock);
#if BM1368_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X", job.job_id);
#endif
_send_BM1368((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t *)&job, sizeof(BM1368_job), BM1368_DEBUG_WORK);
return job.job_id;
}
asic_result * BM1368_receive_work(void)
@ -407,11 +405,6 @@ task_result * BM1368_proccess_work(void * pvParameters)
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
if (GLOBAL_STATE->valid_jobs[job_id] == 0) {
ESP_LOGW(TAG, "Invalid job found, 0x%02X", job_id);
return NULL;
}
uint32_t rolled_version = GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->version | version_bits;
result.job_id = job_id;

View File

@ -420,7 +420,7 @@ void BM1370_set_job_difficulty_mask(int difficulty)
static uint8_t id = 0;
void BM1370_send_work(void * pvParameters, bm_job * next_bm_job)
int BM1370_send_work(void * pvParameters, bm_job * next_bm_job)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
@ -436,22 +436,20 @@ void BM1370_send_work(void * pvParameters, bm_job * next_bm_job)
memcpy(job.prev_block_hash, next_bm_job->prev_block_hash_be, 32);
memcpy(&job.version, &next_bm_job->version, 4);
//debug sent jobs - this can get crazy if the interval is short
#if BM1370_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X (%d)", job.job_id, next_bm_job->connection_id);
#endif
if (GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] != NULL) {
free_bm_job(GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id]);
}
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] = next_bm_job;
pthread_mutex_lock(&GLOBAL_STATE->valid_jobs_lock);
GLOBAL_STATE->valid_jobs[job.job_id] = 1;
pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock);
//debug sent jobs - this can get crazy if the interval is short
#if BM1370_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X", job.job_id);
#endif
_send_BM1370((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t *)&job, sizeof(BM1370_job), BM1370_DEBUG_WORK);
return job.job_id;
}
asic_result * BM1370_receive_work(void)
@ -523,11 +521,6 @@ task_result * BM1370_proccess_work(void * pvParameters)
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
if (GLOBAL_STATE->valid_jobs[job_id] == 0) {
ESP_LOGW(TAG, "Invalid job nonce found, 0x%02X", job_id);
return NULL;
}
uint32_t rolled_version = GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->version | version_bits;
result.job_id = job_id;

View File

@ -360,7 +360,7 @@ void BM1397_set_job_difficulty_mask(int difficulty)
static uint8_t id = 0;
void BM1397_send_work(void *pvParameters, bm_job *next_bm_job)
int BM1397_send_work(void *pvParameters, bm_job *next_bm_job)
{
GlobalState *GLOBAL_STATE = (GlobalState *)pvParameters;
@ -386,6 +386,10 @@ void BM1397_send_work(void *pvParameters, bm_job *next_bm_job)
memcpy(job.midstate3, next_bm_job->midstate3, 32);
}
#if BM1397_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X (%d)", job.job_id, next_bm_job->connection_id);
#endif
if (GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] != NULL)
{
free_bm_job(GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id]);
@ -393,15 +397,9 @@ void BM1397_send_work(void *pvParameters, bm_job *next_bm_job)
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job.job_id] = next_bm_job;
pthread_mutex_lock(&GLOBAL_STATE->valid_jobs_lock);
GLOBAL_STATE->valid_jobs[job.job_id] = 1;
pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock);
#if BM1397_DEBUG_JOBS
ESP_LOGI(TAG, "Send Job: %02X", job.job_id);
#endif
_send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t *)&job, sizeof(job_packet), BM1397_DEBUG_WORK);
return job.job_id;
}
asic_result *BM1397_receive_work(void)
@ -456,11 +454,6 @@ task_result *BM1397_proccess_work(void *pvParameters)
uint8_t rx_midstate_index = asic_result->job_id & 0x03;
GlobalState *GLOBAL_STATE = (GlobalState *)pvParameters;
if (GLOBAL_STATE->valid_jobs[rx_job_id] == 0)
{
ESP_LOGW(TAG, "Invalid job nonce found, id=%d", rx_job_id);
return NULL;
}
uint32_t rolled_version = GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[rx_job_id]->version;
for (int i = 0; i < rx_midstate_index; i++)

View File

@ -17,7 +17,7 @@ uint16_t ASIC_get_small_core_count(GlobalState * GLOBAL_STATE);
task_result * ASIC_proccess_work(GlobalState * GLOBAL_STATE);
int ASIC_set_max_baud(GlobalState * GLOBAL_STATE);
void ASIC_set_job_difficulty_mask(GlobalState * GLOBAL_STATE, uint8_t mask);
void ASIC_send_work(GlobalState * GLOBAL_STATE, void * next_job);
int ASIC_send_work(GlobalState * GLOBAL_STATE, void * next_job);
void ASIC_set_version_mask(GlobalState * GLOBAL_STATE, uint32_t mask);
bool ASIC_set_frequency(GlobalState * GLOBAL_STATE, float target_frequency);
esp_err_t ASIC_set_device_model(GlobalState * GLOBAL_STATE);

View File

@ -10,10 +10,10 @@
#define CRC5_MASK 0x1F
#define BM1366_ASIC_DIFFICULTY 256
#define BM1366_SERIALTX_DEBUG false
#define BM1366_SERIALRX_DEBUG false
#define BM1366_SERIALTX_DEBUG true
#define BM1366_SERIALRX_DEBUG true
#define BM1366_DEBUG_WORK false //causes insane amount of debug output
#define BM1366_DEBUG_JOBS false //causes insane amount of debug output
#define BM1366_DEBUG_JOBS true //causes insane amount of debug output
static const uint64_t BM1366_CORE_COUNT = 112;
static const uint64_t BM1366_SMALL_CORE_COUNT = 894;
@ -36,7 +36,7 @@ typedef struct __attribute__((__packed__))
} BM1366_job;
uint8_t BM1366_init(uint64_t frequency, uint16_t asic_count);
void BM1366_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
int BM1366_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
void BM1366_set_job_difficulty_mask(int);
void BM1366_set_version_mask(uint32_t version_mask);
int BM1366_set_max_baud(void);

View File

@ -36,7 +36,7 @@ typedef struct __attribute__((__packed__))
} BM1368_job;
uint8_t BM1368_init(uint64_t frequency, uint16_t asic_count);
void BM1368_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
int BM1368_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
void BM1368_set_job_difficulty_mask(int);
void BM1368_set_version_mask(uint32_t version_mask);
int BM1368_set_max_baud(void);

View File

@ -36,7 +36,7 @@ typedef struct __attribute__((__packed__))
} BM1370_job;
uint8_t BM1370_init(uint64_t frequency, uint16_t asic_count);
void BM1370_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
int BM1370_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
void BM1370_set_job_difficulty_mask(int);
void BM1370_set_version_mask(uint32_t version_mask);
int BM1370_set_max_baud(void);

View File

@ -50,7 +50,7 @@ typedef struct __attribute__((__packed__))
} job_packet;
uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count);
void BM1397_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
int BM1397_send_work(void * GLOBAL_STATE, bm_job * next_bm_job);
void BM1397_set_job_difficulty_mask(int);
void BM1397_set_version_mask(uint32_t version_mask);
int BM1397_set_max_baud(void);

View File

@ -15,6 +15,8 @@ typedef struct
uint32_t target; // aka difficulty, aka nbits
uint32_t starting_nonce;
uint8_t connection_id;
uint8_t num_midstates;
uint8_t midstate[32];
uint8_t midstate1[32];

View File

@ -28,6 +28,7 @@ static const int STRATUM_ID_SUBSCRIBE = 2;
typedef struct
{
uint8_t connection_id;
char *job_id;
char *prev_block_hash;
char *coinbase_1;
@ -62,21 +63,28 @@ typedef struct
char * error_str;
} StratumApiV1Message;
void STRATUM_V1_initialize_buffer();
char *STRATUM_V1_receive_jsonrpc_line(int sockfd);
typedef struct {
char *data;
size_t size;
} StratumApiV1Buffer;
int STRATUM_V1_subscribe(int socket, int send_uid, char * model);
StratumApiV1Buffer *STRATUM_V1_buffer_create();
void STRATUM_V1_buffer_init(StratumApiV1Buffer *buf);
void STRATUM_V1_parse(StratumApiV1Message *message, const char *stratum_json);
char *STRATUM_V1_receive_jsonrpc_line(const char * POOL_TAG, int sockfd, StratumApiV1Buffer *json_buf);
int STRATUM_V1_subscribe(const char * POOL_TAG,int socket, int send_uid, char * model);
void STRATUM_V1_parse(const char * POOL_TAG, StratumApiV1Message *message, const char *stratum_json);
void STRATUM_V1_free_mining_notify(mining_notify *params);
int STRATUM_V1_authenticate(int socket, int send_uid, const char *username, const char *pass);
int STRATUM_V1_authenticate(const char * POOL_TAG, int socket, int send_uid, const char *username, const char *pass);
int STRATUM_V1_configure_version_rolling(int socket, int send_uid, uint32_t * version_mask);
int STRATUM_V1_configure_version_rolling(const char * POOL_TAG,int socket, int send_uid);
int STRATUM_V1_suggest_difficulty(int socket, int send_uid, uint32_t difficulty);
int STRATUM_V1_suggest_difficulty(const char * POOL_TAG, int socket, int send_uid, uint32_t difficulty);
int STRATUM_V1_submit_share(int socket, int send_uid, const char *username, const char *jobid,
const char *extranonce_2, const uint32_t ntime, const uint32_t nonce,

View File

@ -4,6 +4,7 @@
#include "mining.h"
#include "utils.h"
#include "mbedtls/sha256.h"
#include "esp_log.h"
void free_bm_job(bm_job *job)
{
@ -61,6 +62,7 @@ bm_job construct_bm_job(mining_notify *params, const char *merkle_root, const ui
new_job.target = params->target;
new_job.ntime = params->ntime;
new_job.pool_diff = params->difficulty;
new_job.connection_id = params->connection_id;
hex2bin(merkle_root, new_job.merkle_root, 32);

View File

@ -16,93 +16,95 @@
#define BUFFER_SIZE 1024
static const char * TAG = "stratum_api";
static char * json_rpc_buffer = NULL;
static size_t json_rpc_buffer_size = 0;
static void debug_stratum_tx(const char * POOL_TAG, const char *);
int _parse_stratum_subscribe_result_message(const char * POOL_TAG, const char * result_json_str, char ** extranonce, int * extranonce2_len);
static void debug_stratum_tx(const char *);
int _parse_stratum_subscribe_result_message(const char * result_json_str, char ** extranonce, int * extranonce2_len);
void STRATUM_V1_initialize_buffer()
{
json_rpc_buffer = malloc(BUFFER_SIZE);
json_rpc_buffer_size = BUFFER_SIZE;
if (json_rpc_buffer == NULL) {
printf("Error: Failed to allocate memory for buffer\n");
StratumApiV1Buffer *STRATUM_V1_buffer_create() {
StratumApiV1Buffer *buf = malloc(sizeof(StratumApiV1Buffer));
if (!buf) {
fprintf(stderr, "Error: Failed to allocate memory for StratumApiV1Buffer\n");
exit(1);
}
memset(json_rpc_buffer, 0, BUFFER_SIZE);
STRATUM_V1_buffer_init(buf);
return buf;
}
void cleanup_stratum_buffer()
{
free(json_rpc_buffer);
void STRATUM_V1_buffer_init(StratumApiV1Buffer *buf) {
buf->data = malloc(BUFFER_SIZE);
buf->size = BUFFER_SIZE;
if (!buf->data) {
fprintf(stderr, "Error: Failed to allocate memory for buffer\n");
exit(1);
}
memset(buf->data, 0, buf->size);
}
static void realloc_json_buffer(size_t len)
{
size_t old, new;
void STRATUM_V1_buffer_clear(StratumApiV1Buffer *buf) {
memset(buf->data, 0, BUFFER_SIZE);
buf->size = 0;
}
old = strlen(json_rpc_buffer);
new = old + len + 1;
void STRATUM_V1_buffer_realloc(StratumApiV1Buffer *buf, size_t additional_len) {
size_t old_size = strlen(buf->data);
size_t new_size = old_size + additional_len + 1;
if (new < json_rpc_buffer_size) {
if (new_size <= buf->size) {
return;
}
new = new + (BUFFER_SIZE - (new % BUFFER_SIZE));
void * new_sockbuf = realloc(json_rpc_buffer, new);
new_size += BUFFER_SIZE - (new_size % BUFFER_SIZE);
char *new_data = realloc(buf->data, new_size);
if (new_sockbuf == NULL) {
if (!new_data) {
fprintf(stderr, "Error: realloc failed in recalloc_sock()\n");
ESP_LOGI(TAG, "Restarting System because of ERROR: realloc failed in recalloc_sock");
vTaskDelay(1000 / portTICK_PERIOD_MS);
esp_restart();
}
json_rpc_buffer = new_sockbuf;
memset(json_rpc_buffer + old, 0, new - old);
json_rpc_buffer_size = new;
buf->data = new_data;
memset(buf->data + old_size, 0, new_size - old_size);
buf->size = new_size;
}
char * STRATUM_V1_receive_jsonrpc_line(int sockfd)
char *STRATUM_V1_receive_jsonrpc_line(const char * POOL_TAG, int sockfd, StratumApiV1Buffer *json_buf)
{
if (json_rpc_buffer == NULL) {
STRATUM_V1_initialize_buffer();
}
char *line, *tok = NULL;
char recv_buffer[BUFFER_SIZE];
int nbytes;
size_t buflen = 0;
if (!strstr(json_rpc_buffer, "\n")) {
if (!strstr(json_buf->data, "\n")) {
do {
memset(recv_buffer, 0, BUFFER_SIZE);
nbytes = recv(sockfd, recv_buffer, BUFFER_SIZE - 1, 0);
if (nbytes == -1) {
ESP_LOGI(TAG, "Error: recv (errno %d: %s)", errno, strerror(errno));
if (json_rpc_buffer) {
free(json_rpc_buffer);
json_rpc_buffer=0;
}
return 0;
if (nbytes <= 0)
{
ESP_LOGI(POOL_TAG, "Error: recv (errno %d: %s)", errno, strerror(errno));
STRATUM_V1_buffer_clear(json_buf);
return NULL;
}
realloc_json_buffer(nbytes);
strncat(json_rpc_buffer, recv_buffer, nbytes);
} while (!strstr(json_rpc_buffer, "\n"));
STRATUM_V1_buffer_realloc(json_buf, nbytes);
strncat(json_buf->data, recv_buffer, nbytes);
} while (!strstr(json_buf->data, "\n"));
}
buflen = strlen(json_rpc_buffer);
tok = strtok(json_rpc_buffer, "\n");
buflen = strlen(json_buf->data);
tok = strtok(json_buf->data, "\n");
line = strdup(tok);
int len = strlen(line);
if (buflen > len + 1)
memmove(json_rpc_buffer, json_rpc_buffer + len + 1, buflen - len + 1);
memmove(json_buf->data, json_buf->data + len + 1, buflen - len + 1);
else
strcpy(json_rpc_buffer, "");
strcpy(json_buf->data, "");
return line;
}
void STRATUM_V1_parse(StratumApiV1Message * message, const char * stratum_json)
void STRATUM_V1_parse(const char * POOL_TAG, StratumApiV1Message * message, const char * stratum_json)
{
cJSON * json = cJSON_Parse(stratum_json);
@ -127,7 +129,7 @@ void STRATUM_V1_parse(StratumApiV1Message * message, const char * stratum_json)
} else if (strcmp("client.reconnect", method_json->valuestring) == 0) {
result = CLIENT_RECONNECT;
} else {
ESP_LOGI(TAG, "unhandled method in stratum message: %s", stratum_json);
ESP_LOGI(POOL_TAG, "unhandled method in stratum message: %s", stratum_json);
}
//if there is no method, then it is a result
@ -184,7 +186,7 @@ void STRATUM_V1_parse(StratumApiV1Message * message, const char * stratum_json)
cJSON * extranonce2_len_json = cJSON_GetArrayItem(result_json, 2);
if (extranonce2_len_json == NULL) {
ESP_LOGE(TAG, "Unable to parse extranonce2_len: %s", result_json->valuestring);
ESP_LOGE(POOL_TAG, "Unable to parse extranonce2_len: %s", result_json->valuestring);
message->response_success = false;
goto done;
}
@ -192,7 +194,7 @@ void STRATUM_V1_parse(StratumApiV1Message * message, const char * stratum_json)
cJSON * extranonce_json = cJSON_GetArrayItem(result_json, 1);
if (extranonce_json == NULL) {
ESP_LOGE(TAG, "Unable parse extranonce: %s", result_json->valuestring);
ESP_LOGE(POOL_TAG, "Unable parse extranonce: %s", result_json->valuestring);
message->response_success = false;
goto done;
}
@ -201,8 +203,8 @@ void STRATUM_V1_parse(StratumApiV1Message * message, const char * stratum_json)
message->response_success = true;
//print the extranonce_str
ESP_LOGI(TAG, "extranonce_str: %s", message->extranonce_str);
ESP_LOGI(TAG, "extranonce_2_len: %d", message->extranonce_2_len);
ESP_LOGI(POOL_TAG, "extranonce_str: %s", message->extranonce_str);
ESP_LOGI(POOL_TAG, "extranonce_2_len: %d", message->extranonce_2_len);
//if the id is STRATUM_ID_CONFIGURE parse it
} else if (parsed_id == STRATUM_ID_CONFIGURE) {
@ -210,13 +212,13 @@ void STRATUM_V1_parse(StratumApiV1Message * message, const char * stratum_json)
if (mask != NULL) {
result = STRATUM_RESULT_VERSION_MASK;
message->version_mask = strtoul(mask->valuestring, NULL, 16);
ESP_LOGI(TAG, "Set version mask: %08lx", message->version_mask);
ESP_LOGI(POOL_TAG, "Set version mask: %08lx", message->version_mask);
} else {
ESP_LOGI(TAG, "error setting version mask: %s", stratum_json);
ESP_LOGI(POOL_TAG, "error setting version mask: %s", stratum_json);
}
} else {
ESP_LOGI(TAG, "unhandled result in stratum message: %s", stratum_json);
ESP_LOGI(POOL_TAG, "unhandled result in stratum message: %s", stratum_json);
}
}
@ -278,29 +280,29 @@ void STRATUM_V1_free_mining_notify(mining_notify * params)
free(params);
}
int _parse_stratum_subscribe_result_message(const char * result_json_str, char ** extranonce, int * extranonce2_len)
int _parse_stratum_subscribe_result_message(const char * POOL_TAG, const char * result_json_str, char ** extranonce, int * extranonce2_len)
{
cJSON * root = cJSON_Parse(result_json_str);
if (root == NULL) {
ESP_LOGE(TAG, "Unable to parse %s", result_json_str);
ESP_LOGE(POOL_TAG, "Unable to parse %s", result_json_str);
return -1;
}
cJSON * result = cJSON_GetObjectItem(root, "result");
if (result == NULL) {
ESP_LOGE(TAG, "Unable to parse subscribe result %s", result_json_str);
ESP_LOGE(POOL_TAG, "Unable to parse subscribe result %s", result_json_str);
return -1;
}
cJSON * extranonce2_len_json = cJSON_GetArrayItem(result, 2);
if (extranonce2_len_json == NULL) {
ESP_LOGE(TAG, "Unable to parse extranonce2_len: %s", result->valuestring);
ESP_LOGE(POOL_TAG, "Unable to parse extranonce2_len: %s", result->valuestring);
return -1;
}
*extranonce2_len = extranonce2_len_json->valueint;
cJSON * extranonce_json = cJSON_GetArrayItem(result, 1);
if (extranonce_json == NULL) {
ESP_LOGE(TAG, "Unable parse extranonce: %s", result->valuestring);
ESP_LOGE(POOL_TAG, "Unable parse extranonce: %s", result->valuestring);
return -1;
}
*extranonce = malloc(strlen(extranonce_json->valuestring) + 1);
@ -311,33 +313,33 @@ int _parse_stratum_subscribe_result_message(const char * result_json_str, char *
return 0;
}
int STRATUM_V1_subscribe(int socket, int send_uid, char * model)
int STRATUM_V1_subscribe(const char * POOL_TAG, int socket, int send_uid, char * model)
{
// Subscribe
char subscribe_msg[BUFFER_SIZE];
const esp_app_desc_t *app_desc = esp_app_get_description();
const char *version = app_desc->version;
sprintf(subscribe_msg, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": [\"bitaxe/%s/%s\"]}\n", send_uid, model, version);
debug_stratum_tx(subscribe_msg);
debug_stratum_tx(POOL_TAG, subscribe_msg);
return write(socket, subscribe_msg, strlen(subscribe_msg));
}
int STRATUM_V1_suggest_difficulty(int socket, int send_uid, uint32_t difficulty)
int STRATUM_V1_suggest_difficulty(const char * POOL_TAG, int socket, int send_uid, uint32_t difficulty)
{
char difficulty_msg[BUFFER_SIZE];
sprintf(difficulty_msg, "{\"id\": %d, \"method\": \"mining.suggest_difficulty\", \"params\": [%ld]}\n", send_uid, difficulty);
debug_stratum_tx(difficulty_msg);
debug_stratum_tx(POOL_TAG, difficulty_msg);
return write(socket, difficulty_msg, strlen(difficulty_msg));
}
int STRATUM_V1_authenticate(int socket, int send_uid, const char * username, const char * pass)
int STRATUM_V1_authenticate(const char * POOL_TAG, int socket, int send_uid, const char * username, const char * pass)
{
char authorize_msg[BUFFER_SIZE];
sprintf(authorize_msg, "{\"id\": %d, \"method\": \"mining.authorize\", \"params\": [\"%s\", \"%s\"]}\n", send_uid, username,
pass);
debug_stratum_tx(authorize_msg);
debug_stratum_tx(POOL_TAG, authorize_msg);
return write(socket, authorize_msg, strlen(authorize_msg));
}
@ -356,31 +358,31 @@ int STRATUM_V1_submit_share(int socket, int send_uid, const char * username, con
sprintf(submit_msg,
"{\"id\": %d, \"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%08lx\", \"%08lx\", \"%08lx\"]}\n",
send_uid, username, jobid, extranonce_2, ntime, nonce, version);
debug_stratum_tx(submit_msg);
debug_stratum_tx(TAG, submit_msg);
return write(socket, submit_msg, strlen(submit_msg));
}
int STRATUM_V1_configure_version_rolling(int socket, int send_uid, uint32_t * version_mask)
int STRATUM_V1_configure_version_rolling(const char * POOL_TAG, int socket, int send_uid)
{
char configure_msg[BUFFER_SIZE * 2];
sprintf(configure_msg,
"{\"id\": %d, \"method\": \"mining.configure\", \"params\": [[\"version-rolling\"], {\"version-rolling.mask\": "
"\"ffffffff\"}]}\n",
send_uid);
debug_stratum_tx(configure_msg);
debug_stratum_tx(POOL_TAG, configure_msg);
return write(socket, configure_msg, strlen(configure_msg));
}
static void debug_stratum_tx(const char * msg)
static void debug_stratum_tx(const char * POOL_TAG, const char * msg)
{
//remove the trailing newline
char * newline = strchr(msg, '\n');
if (newline != NULL) {
*newline = '\0';
}
ESP_LOGI(TAG, "tx: %s", msg);
ESP_LOGI(POOL_TAG, "tx: %s", msg);
//put it back!
if (newline != NULL) {

View File

@ -13,7 +13,7 @@ TEST_CASE("Parse stratum method", "[stratum]")
"[\"ae23055e00f0f697cc3640124812d96d4fe8bdfa03484c1c638ce5a1c0e9aa81\",\"980fb87cb61021dd7afd314fcb0dabd096f3d56a7377f6f320684652e7410a21\",\"a52e9868343c55ce405be8971ff340f562ae9ab6353f07140d01666180e19b52\",\"7435bdfa004e603953b2ed39f118803934d9cf17b06d979ceb682f2251bafac2\",\"2a91f061a22d27cb8f44eea79938fb241ebeb359891aa907f05ffde7ed44e52e\",\"302401f80eb5e958155135e25200bb8ea181ad2d05e804a531c7314d86403cdc\",\"318ecb6161eb9b4cfd802bd730e2d36c167ddf102e70aa7b4158e2870dd47392\",\"1114332a9858e0cf84b2425bb1e59eaabf91dd102d114aa443d57fc1b3beb0c9\",\"f43f38095c810613ed795a44d9fab02ff25269706f454885db9be05cdf9c06e1\",\"3e2fc26b27fddc39668b59099cd9635761bb72ed92404204e12bdff08b16fb75\",\"463c19427286342120039a83218fa87ce45448e246895abac11fff0036076758\",\"03d287f655813e540ddb9c4e7aeb922478662b0f5d8e9d0cbd564b20146bab76\"],"
"\"20000004\",\"1705c739\",\"64495522\",false]}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string_standard);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string_standard);
TEST_ASSERT_EQUAL(MINING_NOTIFY, stratum_api_v1_message.method);
TEST_ASSERT_EQUAL_INT(0, stratum_api_v1_message.should_abandon_work);
}
@ -30,7 +30,7 @@ TEST_CASE("Parse stratum mining.notify abandon work", "[stratum]")
"[\"ae23055e00f0f697cc3640124812d96d4fe8bdfa03484c1c638ce5a1c0e9aa81\",\"980fb87cb61021dd7afd314fcb0dabd096f3d56a7377f6f320684652e7410a21\",\"a52e9868343c55ce405be8971ff340f562ae9ab6353f07140d01666180e19b52\",\"7435bdfa004e603953b2ed39f118803934d9cf17b06d979ceb682f2251bafac2\",\"2a91f061a22d27cb8f44eea79938fb241ebeb359891aa907f05ffde7ed44e52e\",\"302401f80eb5e958155135e25200bb8ea181ad2d05e804a531c7314d86403cdc\",\"318ecb6161eb9b4cfd802bd730e2d36c167ddf102e70aa7b4158e2870dd47392\",\"1114332a9858e0cf84b2425bb1e59eaabf91dd102d114aa443d57fc1b3beb0c9\",\"f43f38095c810613ed795a44d9fab02ff25269706f454885db9be05cdf9c06e1\",\"3e2fc26b27fddc39668b59099cd9635761bb72ed92404204e12bdff08b16fb75\",\"463c19427286342120039a83218fa87ce45448e246895abac11fff0036076758\",\"03d287f655813e540ddb9c4e7aeb922478662b0f5d8e9d0cbd564b20146bab76\"],"
"\"20000004\",\"1705c739\",\"64495522\",false]}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string_abandon_work_false);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string_abandon_work_false);
TEST_ASSERT_EQUAL(MINING_NOTIFY, stratum_api_v1_message.method);
TEST_ASSERT_EQUAL_INT(0, stratum_api_v1_message.should_abandon_work);
@ -42,7 +42,7 @@ TEST_CASE("Parse stratum mining.notify abandon work", "[stratum]")
"[\"ae23055e00f0f697cc3640124812d96d4fe8bdfa03484c1c638ce5a1c0e9aa81\",\"980fb87cb61021dd7afd314fcb0dabd096f3d56a7377f6f320684652e7410a21\",\"a52e9868343c55ce405be8971ff340f562ae9ab6353f07140d01666180e19b52\",\"7435bdfa004e603953b2ed39f118803934d9cf17b06d979ceb682f2251bafac2\",\"2a91f061a22d27cb8f44eea79938fb241ebeb359891aa907f05ffde7ed44e52e\",\"302401f80eb5e958155135e25200bb8ea181ad2d05e804a531c7314d86403cdc\",\"318ecb6161eb9b4cfd802bd730e2d36c167ddf102e70aa7b4158e2870dd47392\",\"1114332a9858e0cf84b2425bb1e59eaabf91dd102d114aa443d57fc1b3beb0c9\",\"f43f38095c810613ed795a44d9fab02ff25269706f454885db9be05cdf9c06e1\",\"3e2fc26b27fddc39668b59099cd9635761bb72ed92404204e12bdff08b16fb75\",\"463c19427286342120039a83218fa87ce45448e246895abac11fff0036076758\",\"03d287f655813e540ddb9c4e7aeb922478662b0f5d8e9d0cbd564b20146bab76\"],"
"\"20000004\",\"1705c739\",\"64495522\",true]}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string_abandon_work);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string_abandon_work);
TEST_ASSERT_EQUAL(MINING_NOTIFY, stratum_api_v1_message.method);
TEST_ASSERT_EQUAL_INT(1, stratum_api_v1_message.should_abandon_work);
@ -54,7 +54,7 @@ TEST_CASE("Parse stratum mining.notify abandon work", "[stratum]")
"[\"ae23055e00f0f697cc3640124812d96d4fe8bdfa03484c1c638ce5a1c0e9aa81\",\"980fb87cb61021dd7afd314fcb0dabd096f3d56a7377f6f320684652e7410a21\",\"a52e9868343c55ce405be8971ff340f562ae9ab6353f07140d01666180e19b52\",\"7435bdfa004e603953b2ed39f118803934d9cf17b06d979ceb682f2251bafac2\",\"2a91f061a22d27cb8f44eea79938fb241ebeb359891aa907f05ffde7ed44e52e\",\"302401f80eb5e958155135e25200bb8ea181ad2d05e804a531c7314d86403cdc\",\"318ecb6161eb9b4cfd802bd730e2d36c167ddf102e70aa7b4158e2870dd47392\",\"1114332a9858e0cf84b2425bb1e59eaabf91dd102d114aa443d57fc1b3beb0c9\",\"f43f38095c810613ed795a44d9fab02ff25269706f454885db9be05cdf9c06e1\",\"3e2fc26b27fddc39668b59099cd9635761bb72ed92404204e12bdff08b16fb75\",\"463c19427286342120039a83218fa87ce45448e246895abac11fff0036076758\",\"03d287f655813e540ddb9c4e7aeb922478662b0f5d8e9d0cbd564b20146bab76\"],"
"\"20000004\",\"1705c739\",\"64495522\",\"64495522\",true]}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string_abandon_work_length_9);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string_abandon_work_length_9);
TEST_ASSERT_EQUAL(MINING_NOTIFY, stratum_api_v1_message.method);
TEST_ASSERT_EQUAL_INT(1, stratum_api_v1_message.should_abandon_work);
}
@ -63,7 +63,7 @@ TEST_CASE("Parse stratum set_difficulty params", "[mining.set_difficulty]")
{
const char *json_string = "{\"id\":null,\"method\":\"mining.set_difficulty\",\"params\":[1638]}";
StratumApiV1Message stratum_api_v1_message = {};
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL(MINING_SET_DIFFICULTY, stratum_api_v1_message.method);
TEST_ASSERT_EQUAL(1638, stratum_api_v1_message.new_difficulty);
}
@ -78,7 +78,7 @@ TEST_CASE("Parse stratum notify params", "[mining.notify]")
"\"41903d4c1b2f736c7573682f0000000003ca890d27000000001976a9147c154ed1dc59609e3d26abb2df2ea3d587cd8c4188ac00000000000000002c6a4c2952534b424c4f434b3a4cb4cb2ddfc37c41baf5ef6b6b4899e3253a8f1dfc7e5dd68a5b5b27005014ef0000000000000000266a24aa21a9ed5caa249f1af9fbf71c986fea8e076ca34ae3514fb2f86400561b28c7b15949bf00000000\","
"[\"ae23055e00f0f697cc3640124812d96d4fe8bdfa03484c1c638ce5a1c0e9aa81\",\"980fb87cb61021dd7afd314fcb0dabd096f3d56a7377f6f320684652e7410a21\",\"a52e9868343c55ce405be8971ff340f562ae9ab6353f07140d01666180e19b52\",\"7435bdfa004e603953b2ed39f118803934d9cf17b06d979ceb682f2251bafac2\",\"2a91f061a22d27cb8f44eea79938fb241ebeb359891aa907f05ffde7ed44e52e\",\"302401f80eb5e958155135e25200bb8ea181ad2d05e804a531c7314d86403cdc\",\"318ecb6161eb9b4cfd802bd730e2d36c167ddf102e70aa7b4158e2870dd47392\",\"1114332a9858e0cf84b2425bb1e59eaabf91dd102d114aa443d57fc1b3beb0c9\",\"f43f38095c810613ed795a44d9fab02ff25269706f454885db9be05cdf9c06e1\",\"3e2fc26b27fddc39668b59099cd9635761bb72ed92404204e12bdff08b16fb75\",\"463c19427286342120039a83218fa87ce45448e246895abac11fff0036076758\",\"03d287f655813e540ddb9c4e7aeb922478662b0f5d8e9d0cbd564b20146bab76\"],"
"\"20000004\",\"1705c739\",\"64495522\",false]}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL_STRING("1d2e0c4d3d", stratum_api_v1_message.mining_notification->job_id);
TEST_ASSERT_EQUAL_STRING("ef4b9a48c7986466de4adc002f7337a6e121bc43000376ea0000000000000000", stratum_api_v1_message.mining_notification->prev_block_hash);
TEST_ASSERT_EQUAL_STRING("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4b03a5020cfabe6d6d379ae882651f6469f2ed6b8b40a4f9a4b41fd838a3ad6de8cba775f4e8f1d3080100000000000000", stratum_api_v1_message.mining_notification->coinbase_1);
@ -109,7 +109,7 @@ TEST_CASE("Parse stratum mining.set_version_mask params", "[stratum]")
{
StratumApiV1Message stratum_api_v1_message = {};
const char *json_string = "{\"id\":1,\"method\":\"mining.set_version_mask\",\"params\":[\"1fffe000\"]}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL(1, stratum_api_v1_message.message_id);
TEST_ASSERT_EQUAL(MINING_SET_VERSION_MASK, stratum_api_v1_message.method);
TEST_ASSERT_EQUAL_HEX32(0x1fffe000, stratum_api_v1_message.version_mask);
@ -119,14 +119,14 @@ TEST_CASE("Parse stratum result success", "[stratum]")
{
StratumApiV1Message stratum_api_v1_setup_message = {};
const char* resp1 = "{\"id\":4,\"error\":null,\"result\":true}";
STRATUM_V1_parse(&stratum_api_v1_setup_message, resp1);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_setup_message, resp1);
TEST_ASSERT_EQUAL(4, stratum_api_v1_setup_message.message_id);
TEST_ASSERT_EQUAL(STRATUM_RESULT_SETUP, stratum_api_v1_setup_message.method);
TEST_ASSERT_TRUE(stratum_api_v1_setup_message.response_success);
StratumApiV1Message stratum_api_v1_message = {};
const char* json_string = "{\"id\":5,\"error\":null,\"result\":true}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL(5, stratum_api_v1_message.message_id);
TEST_ASSERT_EQUAL(STRATUM_RESULT, stratum_api_v1_message.method);
TEST_ASSERT_TRUE(stratum_api_v1_message.response_success);
@ -136,7 +136,7 @@ TEST_CASE("Parse stratum result success with large id", "[stratum]")
{
StratumApiV1Message stratum_api_v1_message = {};
const char *json_string = "{\"id\":32769,\"error\":null,\"result\":true}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL(32769, stratum_api_v1_message.message_id);
TEST_ASSERT_EQUAL(STRATUM_RESULT, stratum_api_v1_message.method);
TEST_ASSERT_TRUE(stratum_api_v1_message.response_success);
@ -146,7 +146,7 @@ TEST_CASE("Parse stratum result success with larger id", "[stratum]")
{
StratumApiV1Message stratum_api_v1_message = {};
const char *json_string = "{\"id\":65536,\"error\":null,\"result\":true}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL(65536, stratum_api_v1_message.message_id);
TEST_ASSERT_EQUAL(STRATUM_RESULT, stratum_api_v1_message.method);
TEST_ASSERT_TRUE(stratum_api_v1_message.response_success);
@ -156,7 +156,7 @@ TEST_CASE("Parse stratum result error", "[stratum]")
{
StratumApiV1Message stratum_api_v1_setup_message = {};
const char* resp1 = "{\"id\":4,\"result\":null,\"error\":[21,\"Job not found\",\"\"]}";
STRATUM_V1_parse(&stratum_api_v1_setup_message, resp1);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_setup_message, resp1);
TEST_ASSERT_EQUAL(4, stratum_api_v1_setup_message.message_id);
TEST_ASSERT_EQUAL(STRATUM_RESULT_SETUP, stratum_api_v1_setup_message.method);
TEST_ASSERT_FALSE(stratum_api_v1_setup_message.response_success);
@ -164,7 +164,7 @@ TEST_CASE("Parse stratum result error", "[stratum]")
StratumApiV1Message stratum_api_v1_message = {};
const char* json_string = "{\"id\":5,\"result\":null,\"error\":[21,\"Job not found\",\"\"]}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL(5, stratum_api_v1_message.message_id);
TEST_ASSERT_EQUAL(STRATUM_RESULT, stratum_api_v1_message.method);
TEST_ASSERT_FALSE(stratum_api_v1_message.response_success);
@ -175,7 +175,7 @@ TEST_CASE("Parse stratum result alternative error", "[stratum]")
{
StratumApiV1Message stratum_api_v1_message = {};
const char *json_string = "{\"reject-reason\":\"Above target 2\",\"result\":false,\"error\":null,\"id\":8}";
STRATUM_V1_parse(&stratum_api_v1_message, json_string);
STRATUM_V1_parse("STRATUM_TEST", &stratum_api_v1_message, json_string);
TEST_ASSERT_EQUAL(8, stratum_api_v1_message.message_id);
TEST_ASSERT_EQUAL(STRATUM_RESULT, stratum_api_v1_message.method);
TEST_ASSERT_FALSE(stratum_api_v1_message.response_success);

View File

@ -12,6 +12,7 @@
#include "power_management_task.h"
#include "serial.h"
#include "stratum_api.h"
#include "stratum_task.h"
#include "work_queue.h"
#define STRATUM_USER CONFIG_STRATUM_USER
@ -45,7 +46,7 @@ typedef enum
// task_result * (*receive_result_fn)(void * GLOBAL_STATE);
// int (*set_max_baud_fn)(void);
// void (*set_difficulty_mask_fn)(int);
// void (*send_work_fn)(void * GLOBAL_STATE, bm_job * next_bm_job);
// int (*send_work_fn)(void * GLOBAL_STATE, bm_job * next_bm_job);
// void (*set_version_mask)(uint32_t);
// } AsicFunctions;
@ -114,31 +115,15 @@ typedef struct
double asic_job_frequency_ms;
uint32_t ASIC_difficulty;
work_queue stratum_queue;
work_queue ASIC_jobs_queue;
bm1397Module BM1397_MODULE;
SystemModule SYSTEM_MODULE;
AsicTaskModule ASIC_TASK_MODULE;
PowerManagementModule POWER_MANAGEMENT_MODULE;
SelfTestModule SELF_TEST_MODULE;
char * extranonce_str;
int extranonce_2_len;
int abandon_work;
uint8_t * valid_jobs;
pthread_mutex_t valid_jobs_lock;
uint32_t stratum_difficulty;
uint32_t version_mask;
bool new_stratum_version_rolling_msg;
int sock;
// A message ID that must be unique per request that expects a response.
// For requests not expecting a response (called notifications), this is null.
int send_uid;
work_queue ASIC_jobs_queue;
StratumConnection connections[MAX_STRATUM_POOLS];
uint8_t current_connection_id;
bool ASIC_initalized;
bool psram_is_available;

View File

@ -544,7 +544,7 @@ static esp_err_t GET_system_info(httpd_req_t * req)
cJSON_AddNumberToObject(root, "hashRate", GLOBAL_STATE->SYSTEM_MODULE.current_hashrate);
cJSON_AddStringToObject(root, "bestDiff", GLOBAL_STATE->SYSTEM_MODULE.best_diff_string);
cJSON_AddStringToObject(root, "bestSessionDiff", GLOBAL_STATE->SYSTEM_MODULE.best_session_diff_string);
cJSON_AddNumberToObject(root, "stratumDiff", GLOBAL_STATE->stratum_difficulty);
cJSON_AddNumberToObject(root, "stratumDiff", GLOBAL_STATE->connections[GLOBAL_STATE->current_connection_id].stratum_difficulty);
cJSON_AddNumberToObject(root, "isUsingFallbackStratum", GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback);

View File

@ -1,3 +1,4 @@
#include <pthread.h>
#include "esp_event.h"
#include "esp_log.h"
@ -23,10 +24,6 @@
#include "asic.h"
static GlobalState GLOBAL_STATE = {
.extranonce_str = NULL,
.extranonce_2_len = 0,
.abandon_work = 0,
.version_mask = 0,
.ASIC_initalized = false
};
@ -123,12 +120,9 @@ void app_main(void)
free(wifi_pass);
free(hostname);
GLOBAL_STATE.new_stratum_version_rolling_msg = false;
wifi_softap_off();
if (GLOBAL_STATE.valid_model) {
queue_init(&GLOBAL_STATE.stratum_queue);
queue_init(&GLOBAL_STATE.ASIC_jobs_queue);
SERIAL_init();
@ -137,8 +131,12 @@ void app_main(void)
SERIAL_clear_buffer();
GLOBAL_STATE.ASIC_initalized = true;
GLOBAL_STATE.current_connection_id = 0;
xTaskCreate(stratum_task, "stratum admin", 8192, (void *) &GLOBAL_STATE, 5, NULL);
xTaskCreate(stratum_task_primary, "stratum primary task", 8192, (void *) &GLOBAL_STATE, 5, NULL);
xTaskCreate(stratum_task_secondary, "stratum secondary task", 8192, (void *) &GLOBAL_STATE, 5, NULL);
xTaskCreate(stratum_task_watchdog, "stratum watchdog", 8192, (void *) &GLOBAL_STATE, 5, NULL);
// xTaskCreate(stratum_task, "stratum admin", 8192, (void *) &GLOBAL_STATE, 5, NULL);
xTaskCreate(create_jobs_task, "stratum miner", 8192, (void *) &GLOBAL_STATE, 10, NULL);
xTaskCreate(ASIC_task, "asic", 8192, (void *) &GLOBAL_STATE, 10, NULL);
xTaskCreate(ASIC_result_task, "asic result", 8192, (void *) &GLOBAL_STATE, 15, NULL);

View File

@ -390,11 +390,9 @@ void self_test(void * pvParameters)
}
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs = malloc(sizeof(bm_job *) * 128);
GLOBAL_STATE->valid_jobs = malloc(sizeof(uint8_t) * 128);
for (int i = 0; i < 128; i++) {
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[i] = NULL;
GLOBAL_STATE->valid_jobs[i] = 0;
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
@ -488,7 +486,6 @@ void self_test(void * pvParameters)
}
free(GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs);
free(GLOBAL_STATE->valid_jobs);
if (test_core_voltage(GLOBAL_STATE) != ESP_OK) {
tests_done(GLOBAL_STATE, TESTS_FAILED);

View File

@ -28,8 +28,14 @@ void ASIC_result_task(void *pvParameters)
}
uint8_t job_id = asic_result->job_id;
StratumConnection *current_connection = &GLOBAL_STATE->connections[GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->connection_id];
if (current_connection->state != STRATUM_CONNECTED)
{
ESP_LOGW(TAG, "Connection for job 0x%02X no longer open", job_id);
continue;
}
if (GLOBAL_STATE->valid_jobs[job_id] == 0)
if (current_connection->jobs[job_id] == 0)
{
ESP_LOGW(TAG, "Invalid job nonce found, 0x%02X", job_id);
continue;
@ -46,11 +52,10 @@ void ASIC_result_task(void *pvParameters)
if (nonce_diff >= GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->pool_diff)
{
char * user = GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback ? GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_user : GLOBAL_STATE->SYSTEM_MODULE.pool_user;
int ret = STRATUM_V1_submit_share(
GLOBAL_STATE->sock,
GLOBAL_STATE->send_uid++,
user,
current_connection->sock,
current_connection->send_uid++,
current_connection->username,
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->jobid,
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->extranonce2,
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->ntime,
@ -59,7 +64,7 @@ void ASIC_result_task(void *pvParameters)
if (ret < 0) {
ESP_LOGI(TAG, "Unable to write share to socket. Closing connection. Ret: %d (errno %d: %s)", ret, errno, strerror(errno));
stratum_close_connection(GLOBAL_STATE);
stratum_close_connection(current_connection);
}
}

View File

@ -22,11 +22,11 @@ void ASIC_task(void *pvParameters)
GLOBAL_STATE->ASIC_TASK_MODULE.semaphore = xSemaphoreCreateBinary();
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs = malloc(sizeof(bm_job *) * 128);
GLOBAL_STATE->valid_jobs = malloc(sizeof(uint8_t) * 128);
// GLOBAL_STATE->stratum_context.jobs = malloc(sizeof(uint8_t) * 128);
for (int i = 0; i < 128; i++)
{
GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[i] = NULL;
GLOBAL_STATE->valid_jobs[i] = 0;
// GLOBAL_STATE->stratum_context.jobs[i] = 0;
}
ESP_LOGI(TAG, "ASIC Job Interval: %.2f ms", GLOBAL_STATE->asic_job_frequency_ms);
@ -35,17 +35,30 @@ void ASIC_task(void *pvParameters)
while (1)
{
bm_job *next_bm_job = (bm_job *)queue_dequeue(&GLOBAL_STATE->ASIC_jobs_queue);
StratumConnection *current_connection = &GLOBAL_STATE->connections[next_bm_job->connection_id];
if (next_bm_job->pool_diff != GLOBAL_STATE->stratum_difficulty)
if (next_bm_job->connection_id != GLOBAL_STATE->current_connection_id)
{
ESP_LOGI(TAG, "New pool difficulty %lu", next_bm_job->pool_diff);
GLOBAL_STATE->stratum_difficulty = next_bm_job->pool_diff;
continue;
}
//(*GLOBAL_STATE->ASIC_functions.send_work_fn)(GLOBAL_STATE, next_bm_job); // send the job to the ASIC
ASIC_send_work(GLOBAL_STATE, next_bm_job);
if (current_connection->state != STRATUM_CONNECTED)
{
ESP_LOGW(TAG, "Connection for job no longer open. Skipping.");
continue;
}
if (current_connection->stratum_difficulty != next_bm_job->pool_diff)
{
ESP_LOGI(TAG, "New pool difficulty %lu", next_bm_job->pool_diff);
current_connection->stratum_difficulty = next_bm_job->pool_diff;
}
pthread_mutex_lock(&current_connection->jobs_lock);
int job_id = ASIC_send_work(GLOBAL_STATE, next_bm_job);
current_connection->jobs[job_id] = 1;
pthread_mutex_unlock(&current_connection->jobs_lock);
// Time to execute the above code is ~0.3ms
// Delay for ASIC(s) to finish the job

View File

@ -15,7 +15,7 @@ static const char *TAG = "create_jobs_task";
#define QUEUE_LOW_WATER_MARK 10 // Adjust based on your requirements
static bool should_generate_more_work(GlobalState *GLOBAL_STATE);
static void generate_work(GlobalState *GLOBAL_STATE, mining_notify *notification, uint32_t extranonce_2);
static void generate_work(GlobalState *GLOBAL_STATE, StratumConnection *active_connection, mining_notify *notification, uint32_t extranonce_2);
void create_jobs_task(void *pvParameters)
{
@ -23,42 +23,56 @@ void create_jobs_task(void *pvParameters)
while (1)
{
mining_notify *mining_notification = (mining_notify *)queue_dequeue(&GLOBAL_STATE->stratum_queue);
if (mining_notification == NULL) {
uint16_t current_connection_id = GLOBAL_STATE->current_connection_id;
StratumConnection *active_connection = &GLOBAL_STATE->connections[current_connection_id];
if (active_connection->state != STRATUM_CONNECTED)
{
ESP_LOGD(TAG, "Connection ID %d not ready.", current_connection_id);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
mining_notify *mining_notification = (mining_notify *)queue_dequeue(&active_connection->stratum_queue);
if (mining_notification == NULL)
{
ESP_LOGE(TAG, "Failed to dequeue mining notification");
vTaskDelay(100 / portTICK_PERIOD_MS); // Wait a bit before trying again
vTaskDelay(100 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "New Work Dequeued %s", mining_notification->job_id);
if (GLOBAL_STATE->new_stratum_version_rolling_msg) {
ESP_LOGI(TAG, "Set chip version rolls %i", (int)(GLOBAL_STATE->version_mask >> 13));
//(GLOBAL_STATE->ASIC_functions.set_version_mask)(GLOBAL_STATE->version_mask);
ASIC_set_version_mask(GLOBAL_STATE, GLOBAL_STATE->version_mask);
GLOBAL_STATE->new_stratum_version_rolling_msg = false;
if (active_connection->new_stratum_version_rolling_msg) {
ESP_LOGI(TAG, "Set chip version rolls %i", (int)(active_connection->version_mask >> 13));
ASIC_set_version_mask(GLOBAL_STATE, active_connection->version_mask);
active_connection->new_stratum_version_rolling_msg = false;
}
uint32_t extranonce_2 = 0;
while (GLOBAL_STATE->stratum_queue.count < 1 && GLOBAL_STATE->abandon_work == 0)
while (active_connection->stratum_queue.count < 1 && active_connection->abandon_work == 0)
{
if (active_connection->state != STRATUM_CONNECTED)
break;
else if (current_connection_id != GLOBAL_STATE->current_connection_id)
break;
if (should_generate_more_work(GLOBAL_STATE))
{
generate_work(GLOBAL_STATE, mining_notification, extranonce_2);
generate_work(GLOBAL_STATE, active_connection, mining_notification, extranonce_2);
// Increase extranonce_2 for the next job.
extranonce_2++;
}
else
{
// If no more work needed, wait a bit before checking again.
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
if (GLOBAL_STATE->abandon_work == 1)
if (active_connection->abandon_work == 1)
{
GLOBAL_STATE->abandon_work = 0;
active_connection->abandon_work = 0;
ASIC_jobs_queue_clear(&GLOBAL_STATE->ASIC_jobs_queue);
xSemaphoreGive(GLOBAL_STATE->ASIC_TASK_MODULE.semaphore);
}
@ -72,15 +86,21 @@ static bool should_generate_more_work(GlobalState *GLOBAL_STATE)
return GLOBAL_STATE->ASIC_jobs_queue.count < QUEUE_LOW_WATER_MARK;
}
static void generate_work(GlobalState *GLOBAL_STATE, mining_notify *notification, uint32_t extranonce_2)
static void generate_work(GlobalState *GLOBAL_STATE, StratumConnection *active_connection, mining_notify *notification, uint32_t extranonce_2)
{
char *extranonce_2_str = extranonce_2_generate(extranonce_2, GLOBAL_STATE->extranonce_2_len);
char *extranonce_2_str = extranonce_2_generate(extranonce_2, active_connection->extranonce_2_len);
if (extranonce_2_str == NULL) {
ESP_LOGE(TAG, "Failed to generate extranonce_2");
return;
}
char *coinbase_tx = construct_coinbase_tx(notification->coinbase_1, notification->coinbase_2, GLOBAL_STATE->extranonce_str, extranonce_2_str);
if (active_connection->extranonce_str == NULL)
{
ESP_LOGW(TAG, "active_connection->extranonce_str == NULL");
return;
}
char *coinbase_tx = construct_coinbase_tx(notification->coinbase_1, notification->coinbase_2, active_connection->extranonce_str, extranonce_2_str);
if (coinbase_tx == NULL) {
ESP_LOGE(TAG, "Failed to construct coinbase_tx");
free(extranonce_2_str);
@ -95,7 +115,7 @@ static void generate_work(GlobalState *GLOBAL_STATE, mining_notify *notification
return;
}
bm_job next_job = construct_bm_job(notification, merkle_root, GLOBAL_STATE->version_mask);
bm_job next_job = construct_bm_job(notification, merkle_root, active_connection->version_mask);
bm_job *queued_next_job = malloc(sizeof(bm_job));
if (queued_next_job == NULL) {
@ -109,7 +129,7 @@ static void generate_work(GlobalState *GLOBAL_STATE, mining_notify *notification
memcpy(queued_next_job, &next_job, sizeof(bm_job));
queued_next_job->extranonce2 = extranonce_2_str; // Transfer ownership
queued_next_job->jobid = strdup(notification->job_id);
queued_next_job->version_mask = GLOBAL_STATE->version_mask;
queued_next_job->version_mask = active_connection->version_mask;
queue_enqueue(&GLOBAL_STATE->ASIC_jobs_queue, queued_next_job);

View File

@ -23,18 +23,14 @@
#define FALLBACK_STRATUM_PW CONFIG_FALLBACK_STRATUM_PW
#define STRATUM_DIFFICULTY CONFIG_STRATUM_DIFFICULTY
#define MAX_RETRY_ATTEMPTS 3
#define MAX_CRITICAL_RETRY_ATTEMPTS 5
#define BUFFER_SIZE 1024
static const char * TAG = "stratum_task";
static StratumApiV1Message stratum_api_v1_message = {};
static SystemTaskModule SYSTEM_TASK_MODULE = {.stratum_difficulty = 8192};
static int addr_family = AF_INET;
static int ip_protocol = IPPROTO_IP;
static const char * primary_stratum_url;
static uint16_t primary_stratum_port;
void stratum_process(const char * POOL_TAG, GlobalState * GLOBAL_STATE, StratumConnection * connection);
void stratum_task(const char * POOL_TAG, GlobalState * GLOBAL_STATE, StratumConnection * connection);
void stratum_task_init_connection(StratumConnection * connection);
struct timeval tcp_snd_timeout = {
.tv_sec = 5,
@ -48,313 +44,334 @@ struct timeval tcp_rcv_timeout = {
bool is_wifi_connected() {
wifi_ap_record_t ap_info;
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
return true;
} else {
return false;
return esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK;
}
void stratum_task_primary(void * pvParameters)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
StratumConnection * connection = &GLOBAL_STATE->connections[0];
stratum_task_init_connection(connection);
connection->id = 0;
connection->host = GLOBAL_STATE->SYSTEM_MODULE.pool_url;
connection->port = GLOBAL_STATE->SYSTEM_MODULE.pool_port;
connection->username = GLOBAL_STATE->SYSTEM_MODULE.pool_user;
connection->password = GLOBAL_STATE->SYSTEM_MODULE.pool_pass;
ESP_LOGI(TAG, "Opening connection to primary pool: %s:%d", connection->host, connection->port);
stratum_task("primary_pool", GLOBAL_STATE, connection);
}
void stratum_task_secondary(void * pvParameters)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
StratumConnection * connection = &GLOBAL_STATE->connections[1];
stratum_task_init_connection(connection);
connection->id = 1;
connection->host = GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_url;
connection->port = GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_port;
connection->username = GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_user;
connection->password = GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_pass;
ESP_LOGI(TAG, "Opening connection to secondary pool: %s:%d", connection->host, connection->port);
stratum_task("secondary_pool", GLOBAL_STATE, connection);
}
void stratum_task_watchdog(void *pvParameters)
{
ESP_LOGI(TAG, "Starting Stratum Watchdog.");
GlobalState *GLOBAL_STATE = (GlobalState *)pvParameters;
while (1)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Always fail back over to the Primary when it is back up.
if (GLOBAL_STATE->current_connection_id != 0 &&
GLOBAL_STATE->connections[0].state == STRATUM_CONNECTED)
{
GLOBAL_STATE->current_connection_id = 0;
GLOBAL_STATE->connections[0].new_stratum_version_rolling_msg = true;
GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback = false;
ESP_LOGI(TAG, "Switching back to primary pool.");
continue;
}
// No need to do anything if the current connection is stable.
if (GLOBAL_STATE->connections[GLOBAL_STATE->current_connection_id].state == STRATUM_CONNECTED)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
// Loop through all connections to try to find a healthy one.
for (int i = 1; i < MAX_STRATUM_POOLS; i++)
{
if (GLOBAL_STATE->current_connection_id == i)
continue;
// Lets wait until this pool is done retrying before moving forward.
if (GLOBAL_STATE->connections[i].state == STRATUM_CONNECTING)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
break;
}
if (GLOBAL_STATE->connections[i].state == STRATUM_CONNECTED)
{
GLOBAL_STATE->current_connection_id = i;
GLOBAL_STATE->connections[i].new_stratum_version_rolling_msg = true;
ESP_LOGI(TAG, "Switching to pool: %s", GLOBAL_STATE->connections[i].host);
GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback = true;
break;
}
}
}
}
void cleanQueue(GlobalState * GLOBAL_STATE) {
ESP_LOGI(TAG, "Clean Jobs: clearing queue");
GLOBAL_STATE->abandon_work = 1;
queue_clear(&GLOBAL_STATE->stratum_queue);
pthread_mutex_lock(&GLOBAL_STATE->valid_jobs_lock);
ASIC_jobs_queue_clear(&GLOBAL_STATE->ASIC_jobs_queue);
for (int i = 0; i < 128; i = i + 4) {
GLOBAL_STATE->valid_jobs[i] = 0;
}
pthread_mutex_unlock(&GLOBAL_STATE->valid_jobs_lock);
void stratum_reset_uid(StratumConnection * connection)
{
connection->send_uid = 1;
}
void stratum_reset_uid(GlobalState * GLOBAL_STATE)
void stratum_close_connection(StratumConnection * connection)
{
ESP_LOGI(TAG, "Resetting stratum uid");
GLOBAL_STATE->send_uid = 1;
}
void stratum_close_connection(GlobalState * GLOBAL_STATE)
{
if (GLOBAL_STATE->sock < 0) {
if (connection->sock < 0)
{
ESP_LOGE(TAG, "Socket already shutdown, not shutting down again..");
return;
}
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(GLOBAL_STATE->sock, SHUT_RDWR);
close(GLOBAL_STATE->sock);
cleanQueue(GLOBAL_STATE);
vTaskDelay(1000 / portTICK_PERIOD_MS);
close(connection->sock);
}
void stratum_primary_heartbeat(void * pvParameters)
void stratum_clear_queue(const char * POOL_TAG, StratumConnection * connection)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
ESP_LOGD(POOL_TAG, "Clearing queues for connection.");
ESP_LOGI(TAG, "Starting heartbeat thread for primary pool: %s:%d", primary_stratum_url, primary_stratum_port);
vTaskDelay(10000 / portTICK_PERIOD_MS);
connection->abandon_work = 1;
queue_clear(&connection->stratum_queue);
int addr_family = AF_INET;
int ip_protocol = IPPROTO_IP;
pthread_mutex_lock(&connection->jobs_lock);
for (int i = 0; i < 128; i++) {
connection->jobs[i] = 0;
}
pthread_mutex_unlock(&connection->jobs_lock);
}
struct timeval tcp_timeout = {
.tv_sec = 5,
.tv_usec = 0
};
while (1)
void stratum_task_init_connection(StratumConnection * connection)
{
queue_init(&connection->stratum_queue);
connection->extranonce_str = NULL,
connection->extranonce_2_len = 0,
connection->version_mask = 0,
connection->abandon_work = 0;
connection->buf = STRATUM_V1_buffer_create();
connection->stratum_difficulty = 1024;
connection->send_uid = 1;
connection->sock = -1;
connection->retry_attempts = 0;
connection->state = STRATUM_CONNECTING;
connection->message = malloc(sizeof(StratumApiV1Message));
connection->jobs = malloc(sizeof(uint8_t) * 128);
for (int i = 0; i < 128; i++)
{
if (GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback == false) {
vTaskDelay(10000 / portTICK_PERIOD_MS);
continue;
}
char host_ip[INET_ADDRSTRLEN];
ESP_LOGD(TAG, "Running Heartbeat on: %s!", primary_stratum_url);
if (!is_wifi_connected()) {
ESP_LOGD(TAG, "Heartbeat. Failed WiFi check!");
vTaskDelay(10000 / portTICK_PERIOD_MS);
continue;
}
struct hostent *primary_dns_addr = gethostbyname(primary_stratum_url);
if (primary_dns_addr == NULL) {
ESP_LOGD(TAG, "Heartbeat. Failed DNS check for: %s!", primary_stratum_url);
vTaskDelay(60000 / portTICK_PERIOD_MS);
continue;
}
inet_ntop(AF_INET, (void *)primary_dns_addr->h_addr_list[0], host_ip, sizeof(host_ip));
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(host_ip);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(primary_stratum_port);
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (sock < 0) {
ESP_LOGD(TAG, "Heartbeat. Failed socket create check!");
vTaskDelay(60000 / portTICK_PERIOD_MS);
continue;
}
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
if (err != 0)
{
ESP_LOGD(TAG, "Heartbeat. Failed connect check: %s:%d (errno %d: %s)", host_ip, primary_stratum_port, errno, strerror(errno));
close(sock);
vTaskDelay(60000 / portTICK_PERIOD_MS);
continue;
}
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO , &tcp_timeout, sizeof(tcp_timeout)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt SO_RCVTIMEO ");
}
int send_uid = 1;
STRATUM_V1_subscribe(sock, send_uid++, GLOBAL_STATE->asic_model_str);
STRATUM_V1_authenticate(sock, send_uid++, GLOBAL_STATE->SYSTEM_MODULE.pool_user, GLOBAL_STATE->SYSTEM_MODULE.pool_pass);
char recv_buffer[BUFFER_SIZE];
memset(recv_buffer, 0, BUFFER_SIZE);
int bytes_received = recv(sock, recv_buffer, BUFFER_SIZE - 1, 0);
shutdown(sock, SHUT_RDWR);
close(sock);
if (bytes_received == -1) {
vTaskDelay(60000 / portTICK_PERIOD_MS);
continue;
}
if (strstr(recv_buffer, "mining.notify") != NULL) {
ESP_LOGI(TAG, "Heartbeat successful and in fallback mode. Switching back to primary.");
GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback = false;
stratum_close_connection(GLOBAL_STATE);
continue;
}
vTaskDelay(60000 / portTICK_PERIOD_MS);
connection->jobs[i] = 0;
}
}
void stratum_task(void * pvParameters)
void stratum_handle_disconnect(const char * POOL_TAG, StratumConnection * connection)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
stratum_close_connection(connection);
primary_stratum_url = GLOBAL_STATE->SYSTEM_MODULE.pool_url;
primary_stratum_port = GLOBAL_STATE->SYSTEM_MODULE.pool_port;
char * stratum_url = GLOBAL_STATE->SYSTEM_MODULE.pool_url;
uint16_t port = GLOBAL_STATE->SYSTEM_MODULE.pool_port;
if (connection->state == STRATUM_CONNECTED)
{
ESP_LOGE(POOL_TAG, "Pool disconnected.");
}
STRATUM_V1_initialize_buffer();
char host_ip[20];
int addr_family = AF_INET;
int ip_protocol = IPPROTO_IP;
int retry_attempts = 0;
int retry_critical_attempts = 0;
stratum_clear_queue(POOL_TAG, connection);
xTaskCreate(stratum_primary_heartbeat, "stratum primary heartbeat", 8192, pvParameters, 1, NULL);
connection->retry_attempts++;
ESP_LOGI(TAG, "Opening connection to pool: %s:%d", stratum_url, port);
if (connection->retry_attempts <= 3)
{
connection->state = STRATUM_CONNECTING;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
else
{
int delay_ms = 1000 + (connection->retry_attempts * 1000);
if (delay_ms > 30 * 1000) delay_ms = 30000;
connection->state = STRATUM_DISCONNECTED;
vTaskDelay(delay_ms / portTICK_PERIOD_MS);
}
}
void stratum_task(const char * POOL_TAG, GlobalState * GLOBAL_STATE, StratumConnection * connection)
{
while (1) {
connection->state = STRATUM_CONNECTING;
connection->send_uid = 1;
if (!is_wifi_connected()) {
ESP_LOGI(TAG, "WiFi disconnected, attempting to reconnect...");
ESP_LOGI(POOL_TAG, "WiFi disconnected, attempting to reconnect...");
connection->state = STRATUM_CONNECTING;
vTaskDelay(10000 / portTICK_PERIOD_MS);
continue;
}
if (retry_attempts >= MAX_RETRY_ATTEMPTS)
{
if (GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_url == NULL || GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_url[0] == '\0') {
ESP_LOGI(TAG, "Unable to switch to fallback. No url configured. (retries: %d)...", retry_attempts);
GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback = false;
retry_attempts = 0;
continue;
}
GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback = !GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback;
ESP_LOGI(TAG, "Switching target due to too many failures (retries: %d)...", retry_attempts);
retry_attempts = 0;
}
stratum_url = GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback ? GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_url : GLOBAL_STATE->SYSTEM_MODULE.pool_url;
port = GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback ? GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_port : GLOBAL_STATE->SYSTEM_MODULE.pool_port;
struct hostent *dns_addr = gethostbyname(stratum_url);
struct hostent *dns_addr = gethostbyname(connection->host);
if (dns_addr == NULL) {
retry_attempts++;
vTaskDelay(1000 / portTICK_PERIOD_MS);
ESP_LOGE(POOL_TAG, "Unable to resolve DNS.");
stratum_handle_disconnect(POOL_TAG, connection);
continue;
}
inet_ntop(AF_INET, (void *)dns_addr->h_addr_list[0], host_ip, sizeof(host_ip));
ESP_LOGI(TAG, "Connecting to: stratum+tcp://%s:%d (%s)", stratum_url, port, host_ip);
inet_ntop(AF_INET, (void *)dns_addr->h_addr_list[0], connection->ip_address, sizeof(connection->ip_address));
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(host_ip);
dest_addr.sin_addr.s_addr = inet_addr(connection->ip_address);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(port);
dest_addr.sin_port = htons(connection->port);
GLOBAL_STATE->sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (GLOBAL_STATE->sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
if (++retry_critical_attempts > MAX_CRITICAL_RETRY_ATTEMPTS) {
ESP_LOGE(TAG, "Max retry attempts reached, restarting...");
esp_restart();
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
connection->sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (connection->sock < 0) {
ESP_LOGE(POOL_TAG, "Unable to create the socket.");
close(connection->sock);
stratum_handle_disconnect(POOL_TAG, connection);
continue;
}
retry_critical_attempts = 0;
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, port);
int err = connect(GLOBAL_STATE->sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
int err = connect(connection->sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
if (err != 0)
{
retry_attempts++;
ESP_LOGE(TAG, "Socket unable to connect to %s:%d (errno %d: %s)", stratum_url, port, errno, strerror(errno));
// close the socket
shutdown(GLOBAL_STATE->sock, SHUT_RDWR);
close(GLOBAL_STATE->sock);
// instead of restarting, retry this every 5 seconds
vTaskDelay(5000 / portTICK_PERIOD_MS);
ESP_LOGE(POOL_TAG, "Unable to connect to socket.");
close(connection->sock);
stratum_handle_disconnect(POOL_TAG, connection);
continue;
}
if (setsockopt(GLOBAL_STATE->sock, SOL_SOCKET, SO_SNDTIMEO, &tcp_snd_timeout, sizeof(tcp_snd_timeout)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt SO_SNDTIMEO");
if (setsockopt(connection->sock, SOL_SOCKET, SO_SNDTIMEO, &tcp_snd_timeout, sizeof(tcp_snd_timeout)) != 0) {
ESP_LOGE(POOL_TAG, "Fail to setsockopt SO_SNDTIMEO");
}
if (setsockopt(GLOBAL_STATE->sock, SOL_SOCKET, SO_RCVTIMEO , &tcp_rcv_timeout, sizeof(tcp_rcv_timeout)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt SO_RCVTIMEO ");
if (setsockopt(connection->sock, SOL_SOCKET, SO_RCVTIMEO , &tcp_rcv_timeout, sizeof(tcp_rcv_timeout)) != 0) {
ESP_LOGE(POOL_TAG, "Fail to setsockopt SO_RCVTIMEO ");
}
stratum_reset_uid(GLOBAL_STATE);
cleanQueue(GLOBAL_STATE);
stratum_reset_uid(connection);
///// Start Stratum Action
// mining.configure - ID: 1
STRATUM_V1_configure_version_rolling(GLOBAL_STATE->sock, GLOBAL_STATE->send_uid++, &GLOBAL_STATE->version_mask);
STRATUM_V1_configure_version_rolling(POOL_TAG, connection->sock, connection->send_uid++);
STRATUM_V1_subscribe(POOL_TAG, connection->sock, connection->send_uid++, GLOBAL_STATE->asic_model_str);
STRATUM_V1_authenticate(POOL_TAG, connection->sock, connection->send_uid++, connection->username, connection->password);
STRATUM_V1_suggest_difficulty(POOL_TAG, connection->sock, connection->send_uid++, STRATUM_DIFFICULTY);
// mining.subscribe - ID: 2
STRATUM_V1_subscribe(GLOBAL_STATE->sock, GLOBAL_STATE->send_uid++, GLOBAL_STATE->asic_model_str);
connection->abandon_work = 0;
char * username = GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback ? GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_user : GLOBAL_STATE->SYSTEM_MODULE.pool_user;
char * password = GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback ? GLOBAL_STATE->SYSTEM_MODULE.fallback_pool_pass : GLOBAL_STATE->SYSTEM_MODULE.pool_pass;
stratum_process(POOL_TAG, GLOBAL_STATE, connection);
//mining.authorize - ID: 3
STRATUM_V1_authenticate(GLOBAL_STATE->sock, GLOBAL_STATE->send_uid++, username, password);
if (GLOBAL_STATE->current_connection_id == connection->id)
ASIC_jobs_queue_clear(&GLOBAL_STATE->ASIC_jobs_queue);
}
}
//mining.suggest_difficulty - ID: 4
STRATUM_V1_suggest_difficulty(GLOBAL_STATE->sock, GLOBAL_STATE->send_uid++, STRATUM_DIFFICULTY);
void stratum_process(const char * POOL_TAG, GlobalState * GLOBAL_STATE, StratumConnection * connection)
{
while (1) {
char * line = STRATUM_V1_receive_jsonrpc_line(POOL_TAG, connection->sock, connection->buf);
if (!line) {
ESP_LOGE(POOL_TAG, "Failed to receive JSON-RPC line, reconnecting...");
stratum_handle_disconnect(POOL_TAG, connection);
break;
}
// Everything is set up, lets make sure we don't abandon work unnecessarily.
GLOBAL_STATE->abandon_work = 0;
ESP_LOGI(POOL_TAG, "rx: %s", line);
STRATUM_V1_parse(POOL_TAG, connection->message, line);
free(line);
while (1) {
char * line = STRATUM_V1_receive_jsonrpc_line(GLOBAL_STATE->sock);
if (!line) {
ESP_LOGE(TAG, "Failed to receive JSON-RPC line, reconnecting...");
retry_attempts++;
stratum_close_connection(GLOBAL_STATE);
break;
if (connection->message->method == MINING_NOTIFY)
{
if (GLOBAL_STATE->current_connection_id == connection->id)
SYSTEM_notify_new_ntime(GLOBAL_STATE, connection->message->mining_notification->ntime);
if (connection->message->should_abandon_work && connection->stratum_queue.count > 0)
{
ESP_LOGI(POOL_TAG, "Abandoning the Stratum queues.");
stratum_clear_queue(POOL_TAG, connection);
if (GLOBAL_STATE->current_connection_id == connection->id)
ASIC_jobs_queue_clear(&GLOBAL_STATE->ASIC_jobs_queue);
}
if (connection->stratum_queue.count >= QUEUE_SIZE)
{
mining_notify * next_notify_json_str = (mining_notify *) queue_dequeue(&connection->stratum_queue);
STRATUM_V1_free_mining_notify(next_notify_json_str);
}
ESP_LOGI(TAG, "rx: %s", line); // debug incoming stratum messages
STRATUM_V1_parse(&stratum_api_v1_message, line);
free(line);
connection->message->mining_notification->difficulty = connection->stratum_difficulty;
connection->message->mining_notification->connection_id = connection->id;
queue_enqueue(&connection->stratum_queue, connection->message->mining_notification);
}
else if (connection->message->method == MINING_SET_DIFFICULTY)
{
if (connection->stratum_difficulty != connection->message->new_difficulty)
{
connection->stratum_difficulty = connection->message->new_difficulty;
ESP_LOGI(POOL_TAG, "Set stratum difficulty: %ld", connection->stratum_difficulty);
}
}
else if (connection->message->method == MINING_SET_VERSION_MASK ||
connection->message->method == STRATUM_RESULT_VERSION_MASK)
{
ESP_LOGI(POOL_TAG, "Set version mask: %08lx", connection->message->version_mask);
connection->version_mask = connection->message->version_mask;
connection->new_stratum_version_rolling_msg = true;
}
else if (connection->message->method == STRATUM_RESULT_SUBSCRIBE)
{
connection->extranonce_str = connection->message->extranonce_str;
connection->extranonce_2_len = connection->message->extranonce_2_len;
}
else if (connection->message->method == CLIENT_RECONNECT)
{
ESP_LOGE(POOL_TAG, "Pool requested client reconnect...");
stratum_clear_queue(POOL_TAG, connection);
stratum_close_connection(connection);
break;
}
else if (connection->message->method == STRATUM_RESULT)
{
if (connection->message->response_success)
{
ESP_LOGI(POOL_TAG, "message result accepted");
SYSTEM_notify_accepted_share(GLOBAL_STATE);
}
else
{
ESP_LOGW(POOL_TAG, "message result rejected: %s", connection->message->error_str ? connection->message->error_str : "unknown");
SYSTEM_notify_rejected_share(GLOBAL_STATE);
}
}
else if (connection->message->method == STRATUM_RESULT_SETUP)
{
if (connection->message->response_success)
{
connection->retry_attempts = 0;
connection->state = STRATUM_CONNECTED;
if (stratum_api_v1_message.method == MINING_NOTIFY) {
SYSTEM_notify_new_ntime(GLOBAL_STATE, stratum_api_v1_message.mining_notification->ntime);
if (stratum_api_v1_message.should_abandon_work &&
(GLOBAL_STATE->stratum_queue.count > 0 || GLOBAL_STATE->ASIC_jobs_queue.count > 0)) {
cleanQueue(GLOBAL_STATE);
}
if (GLOBAL_STATE->stratum_queue.count == QUEUE_SIZE) {
mining_notify * next_notify_json_str = (mining_notify *) queue_dequeue(&GLOBAL_STATE->stratum_queue);
STRATUM_V1_free_mining_notify(next_notify_json_str);
}
stratum_api_v1_message.mining_notification->difficulty = SYSTEM_TASK_MODULE.stratum_difficulty;
queue_enqueue(&GLOBAL_STATE->stratum_queue, stratum_api_v1_message.mining_notification);
} else if (stratum_api_v1_message.method == MINING_SET_DIFFICULTY) {
if (stratum_api_v1_message.new_difficulty != SYSTEM_TASK_MODULE.stratum_difficulty) {
SYSTEM_TASK_MODULE.stratum_difficulty = stratum_api_v1_message.new_difficulty;
ESP_LOGI(TAG, "Set stratum difficulty: %ld", SYSTEM_TASK_MODULE.stratum_difficulty);
}
} else if (stratum_api_v1_message.method == MINING_SET_VERSION_MASK ||
stratum_api_v1_message.method == STRATUM_RESULT_VERSION_MASK) {
// 1fffe000
ESP_LOGI(TAG, "Set version mask: %08lx", stratum_api_v1_message.version_mask);
GLOBAL_STATE->version_mask = stratum_api_v1_message.version_mask;
GLOBAL_STATE->new_stratum_version_rolling_msg = true;
} else if (stratum_api_v1_message.method == STRATUM_RESULT_SUBSCRIBE) {
GLOBAL_STATE->extranonce_str = stratum_api_v1_message.extranonce_str;
GLOBAL_STATE->extranonce_2_len = stratum_api_v1_message.extranonce_2_len;
} else if (stratum_api_v1_message.method == CLIENT_RECONNECT) {
ESP_LOGE(TAG, "Pool requested client reconnect...");
stratum_close_connection(GLOBAL_STATE);
break;
} else if (stratum_api_v1_message.method == STRATUM_RESULT) {
if (stratum_api_v1_message.response_success) {
ESP_LOGI(TAG, "message result accepted");
SYSTEM_notify_accepted_share(GLOBAL_STATE);
} else {
ESP_LOGW(TAG, "message result rejected: %s", stratum_api_v1_message.error_str);
SYSTEM_notify_rejected_share(GLOBAL_STATE, stratum_api_v1_message.error_str);
}
} else if (stratum_api_v1_message.method == STRATUM_RESULT_SETUP) {
// Reset retry attempts after successfully receiving data.
retry_attempts = 0;
if (stratum_api_v1_message.response_success) {
ESP_LOGI(TAG, "setup message accepted");
} else {
ESP_LOGE(TAG, "setup message rejected: %s", stratum_api_v1_message.error_str);
}
ESP_LOGI(POOL_TAG, "setup message accepted");
}
else
{
ESP_LOGE(POOL_TAG, "setup message rejected: %s", connection->message->error_str ? connection->message->error_str : "unknown");
}
}
}
vTaskDelete(NULL);
}

View File

@ -1,12 +1,53 @@
#ifndef STRATUM_TASK_H_
#define STRATUM_TASK_H_
#include <pthread.h>
#include "work_queue.h"
#define MAX_STRATUM_POOLS 2
typedef enum {
STRATUM_CONNECTING,
STRATUM_CONNECTED,
STRATUM_DISCONNECTED,
} connection_state_t;
typedef struct
{
uint32_t stratum_difficulty;
} SystemTaskModule;
uint8_t id;
void stratum_task(void *pvParameters);
void stratum_close_connection(GlobalState * GLOBAL_STATE);
connection_state_t state;
uint32_t stratum_difficulty;
uint32_t version_mask;
bool new_stratum_version_rolling_msg;
char * extranonce_str;
int extranonce_2_len;
int send_uid;
int sock;
char * username;
char * password;
char * host;
char ip_address[20];
uint16_t port;
uint32_t retry_attempts;
work_queue stratum_queue;
uint8_t * jobs;
pthread_mutex_t jobs_lock;
bool abandon_work;
StratumApiV1Message *message;
StratumApiV1Buffer *buf;
} StratumConnection;
void stratum_task_watchdog(void * pvParameters);
void stratum_task_primary(void * pvParameters);
void stratum_task_secondary(void * pvParameters);
void stratum_close_connection(StratumConnection * connection);
#endif