mirror of
https://github.com/skot/ESP-Miner.git
synced 2025-03-17 21:32:52 +01:00
Make stratum module testable and introduce first unit test
This commit is contained in:
parent
ec28d0a74d
commit
3066edb7f1
3
components/stratum/CMakeLists.txt
Normal file
3
components/stratum/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "stratum_api.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES json)
|
54
components/stratum/include/stratum_api.h
Normal file
54
components/stratum/include/stratum_api.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef STRATUM_API_H
|
||||
#define STRATUM_API_H
|
||||
|
||||
#include "cJSON.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_MERKLE_BRANCHES 32
|
||||
#define PREV_BLOCK_HASH_SIZE 32
|
||||
#define COINBASE_SIZE 100
|
||||
#define COINBASE2_SIZE 128
|
||||
|
||||
typedef struct {
|
||||
uint32_t job_id;
|
||||
uint8_t prev_block_hash[PREV_BLOCK_HASH_SIZE];
|
||||
uint8_t coinbase_1[COINBASE_SIZE];
|
||||
size_t coinbase_1_len;
|
||||
uint8_t coinbase_2[COINBASE2_SIZE];
|
||||
size_t coinbase_2_len;
|
||||
uint8_t merkle_branches[MAX_MERKLE_BRANCHES][PREV_BLOCK_HASH_SIZE];
|
||||
size_t n_merkle_branches;
|
||||
uint32_t version;
|
||||
uint32_t curtime;
|
||||
uint32_t bits;
|
||||
uint32_t target;
|
||||
uint32_t nonce;
|
||||
} work;
|
||||
|
||||
typedef enum {
|
||||
STRATUM_UNKNOWN,
|
||||
MINING_NOTIFY,
|
||||
MINING_SET_DIFFICULTY
|
||||
} stratum_method;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
stratum_method method;
|
||||
char * method_str;
|
||||
union {
|
||||
work notify_work;
|
||||
uint32_t notify_difficulty;
|
||||
};
|
||||
} stratum_message;
|
||||
|
||||
void initialize_stratum_buffer();
|
||||
|
||||
char * receive_jsonrpc_line(int sockfd);
|
||||
|
||||
int subscribe_to_stratum(int socket);
|
||||
|
||||
stratum_message parse_stratum_notify_message(const char * stratum_json);
|
||||
|
||||
int auth_to_stratum(int socket, const char * username);
|
||||
|
||||
#endif // STRATUM_API_H
|
@ -2,9 +2,12 @@
|
||||
#include "cJSON.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
|
||||
#define BUFFER_SIZE 1024
|
||||
static const char *TAG = "stratum client";
|
||||
|
||||
static char *json_rpc_buffer = NULL;
|
||||
static size_t json_rpc_buffer_size = 0;
|
||||
@ -78,6 +81,7 @@ char * receive_jsonrpc_line(int sockfd)
|
||||
|
||||
realloc_json_buffer(nbytes);
|
||||
strncat(json_rpc_buffer, recv_buffer, nbytes);
|
||||
ESP_LOGI(TAG, "Current buffer %s", json_rpc_buffer);
|
||||
} while (!strstr(json_rpc_buffer, "\n"));
|
||||
}
|
||||
buflen = strlen(json_rpc_buffer);
|
||||
@ -91,6 +95,62 @@ char * receive_jsonrpc_line(int sockfd)
|
||||
return line;
|
||||
}
|
||||
|
||||
static uint8_t hex2val(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t hex2bin(const char *hex, uint8_t *bin, size_t bin_len)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (*hex && len < bin_len) {
|
||||
bin[len] = hex2val(*hex++) << 4;
|
||||
|
||||
if (!*hex) {
|
||||
len++;
|
||||
break;
|
||||
}
|
||||
|
||||
bin[len++] |= hex2val(*hex++);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
work parse_notify_work(cJSON * params) {
|
||||
work new_work;
|
||||
new_work.job_id = (uint32_t) strtoul(cJSON_GetArrayItem(params, 0)->valuestring, NULL, 16);
|
||||
hex2bin(cJSON_GetArrayItem(params, 1)->valuestring, new_work.prev_block_hash, PREV_BLOCK_HASH_SIZE * 2);
|
||||
|
||||
char *coinb1 = cJSON_GetArrayItem(params, 2)->valuestring;
|
||||
char *coinb2 = cJSON_GetArrayItem(params, 3)->valuestring;
|
||||
hex2bin(coinb1, new_work.coinbase_1, strlen(coinb1) / 2);
|
||||
new_work.coinbase_1_len = strlen(coinb1) / 2;
|
||||
hex2bin(coinb2, new_work.coinbase_2, strlen(coinb2) / 2);
|
||||
new_work.coinbase_2_len = strlen(coinb2) / 2;
|
||||
|
||||
cJSON *merkle_branch = cJSON_GetArrayItem(params, 4);
|
||||
new_work.n_merkle_branches = cJSON_GetArraySize(merkle_branch);
|
||||
if (new_work.n_merkle_branches > MAX_MERKLE_BRANCHES) {
|
||||
printf("Too many Merkle branches.\n");
|
||||
abort();
|
||||
}
|
||||
for (size_t i = 0; i < new_work.n_merkle_branches; i++) {
|
||||
hex2bin(cJSON_GetArrayItem(merkle_branch, i)->valuestring, new_work.merkle_branches[i], PREV_BLOCK_HASH_SIZE * 2);
|
||||
}
|
||||
|
||||
return new_work;
|
||||
}
|
||||
|
||||
stratum_message parse_stratum_notify_message(const char * stratum_json)
|
||||
{
|
||||
stratum_message output;
|
||||
@ -108,6 +168,7 @@ stratum_message parse_stratum_notify_message(const char * stratum_json)
|
||||
output.method_str = strdup(method->valuestring);
|
||||
if (strcmp("mining.notify", method->valuestring) == 0) {
|
||||
output.method = MINING_NOTIFY;
|
||||
output.notify_work = parse_notify_work(cJSON_GetObjectItem(json, "params"));
|
||||
} else if (strcmp("mining.set_difficulty", method->valuestring) == 0) {
|
||||
output.method = MINING_SET_DIFFICULTY;
|
||||
} else {
|
||||
@ -123,10 +184,34 @@ int subscribe_to_stratum(int socket)
|
||||
{
|
||||
// Subscribe
|
||||
char subscribe_msg[BUFFER_SIZE];
|
||||
sprintf(subscribe_msg, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}", send_uid++);
|
||||
sprintf(subscribe_msg, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}\n", send_uid++);
|
||||
ESP_LOGI(TAG, "Subscribe: %s", subscribe_msg);
|
||||
write(socket, subscribe_msg, strlen(subscribe_msg));
|
||||
char * line;
|
||||
line = receive_jsonrpc_line(socket);
|
||||
|
||||
ESP_LOGI(TAG, "Received result %s", line);
|
||||
|
||||
free(line);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int auth_to_stratum(int socket, const char * username)
|
||||
{
|
||||
char authorize_msg[BUFFER_SIZE];
|
||||
sprintf(authorize_msg, "{\"id\": %d, \"method\": \"mining.authorize\", \"params\": [\"%s\", \"x\"]}\n",
|
||||
send_uid++, username);
|
||||
ESP_LOGI(TAG, "Authorize: %s", authorize_msg);
|
||||
|
||||
write(socket, authorize_msg, strlen(authorize_msg));
|
||||
char * line;
|
||||
line = receive_jsonrpc_line(socket);
|
||||
|
||||
ESP_LOGI(TAG, "Received result %s", line);
|
||||
|
||||
// TODO: Parse authorize results
|
||||
|
||||
free(line);
|
||||
|
||||
return 1;
|
3
components/stratum/test/CMakeLists.txt
Normal file
3
components/stratum/test/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES cmock stratum)
|
15
components/stratum/test/test_stratum_json.c
Normal file
15
components/stratum/test/test_stratum_json.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include "unity.h"
|
||||
#include "stratum_api.h"
|
||||
|
||||
TEST_CASE("Check can parse json", "[mining.notify]")
|
||||
{
|
||||
const char * json_string = "{\"id\":null,\"method\":\"mining.notify\",\"params\":"
|
||||
"[\"1b4c3d9041\","
|
||||
"\"ef4b9a48c7986466de4adc002f7337a6e121bc43000376ea0000000000000000\","
|
||||
"\"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4b03a5020cfabe6d6d379ae882651f6469f2ed6b8b40a4f9a4b41fd838a3ad6de8cba775f4e8f1d3080100000000000000\","
|
||||
"\"41903d4c1b2f736c7573682f0000000003ca890d27000000001976a9147c154ed1dc59609e3d26abb2df2ea3d587cd8c4188ac00000000000000002c6a4c2952534b424c4f434b3a4cb4cb2ddfc37c41baf5ef6b6b4899e3253a8f1dfc7e5dd68a5b5b27005014ef0000000000000000266a24aa21a9ed5caa249f1af9fbf71c986fea8e076ca34ae3514fb2f86400561b28c7b15949bf00000000\","
|
||||
"[\"ae23055e00f0f697cc3640124812d96d4fe8bdfa03484c1c638ce5a1c0e9aa81\",\"980fb87cb61021dd7afd314fcb0dabd096f3d56a7377f6f320684652e7410a21\",\"a52e9868343c55ce405be8971ff340f562ae9ab6353f07140d01666180e19b52\",\"7435bdfa004e603953b2ed39f118803934d9cf17b06d979ceb682f2251bafac2\",\"2a91f061a22d27cb8f44eea79938fb241ebeb359891aa907f05ffde7ed44e52e\",\"302401f80eb5e958155135e25200bb8ea181ad2d05e804a531c7314d86403cdc\",\"318ecb6161eb9b4cfd802bd730e2d36c167ddf102e70aa7b4158e2870dd47392\",\"1114332a9858e0cf84b2425bb1e59eaabf91dd102d114aa443d57fc1b3beb0c9\",\"f43f38095c810613ed795a44d9fab02ff25269706f454885db9be05cdf9c06e1\",\"3e2fc26b27fddc39668b59099cd9635761bb72ed92404204e12bdff08b16fb75\",\"463c19427286342120039a83218fa87ce45448e246895abac11fff0036076758\",\"03d287f655813e540ddb9c4e7aeb922478662b0f5d8e9d0cbd564b20146bab76\"],"
|
||||
"\"20000004\",\"1705c739\",\"64495522\",false]}";
|
||||
stratum_message message = parse_stratum_notify_message(json_string);
|
||||
TEST_ASSERT_EQUAL(MINING_NOTIFY, message.method);
|
||||
}
|
@ -20,6 +20,5 @@ idf_component_register(SRCS
|
||||
"crc.c"
|
||||
"pretty.c"
|
||||
"tcp_client.c"
|
||||
"stratum_api.c"
|
||||
"work_queue.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
@ -1,28 +0,0 @@
|
||||
#ifndef STRATUM_API_H
|
||||
#define STRATUM_API_H
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
typedef enum {
|
||||
STRATUM_UNKNOWN,
|
||||
MINING_NOTIFY,
|
||||
MINING_SET_DIFFICULTY
|
||||
} stratum_method;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
stratum_method method;
|
||||
char * method_str;
|
||||
} stratum_message;
|
||||
|
||||
void initialize_stratum_buffer();
|
||||
|
||||
char * receive_jsonrpc_line(int sockfd);
|
||||
|
||||
int subscribe_to_stratum(int socket);
|
||||
|
||||
stratum_message parse_stratum_notify_message(const char * stratum_json);
|
||||
|
||||
int auth_to_stratum(int socket, const char * username, const char * passwork);
|
||||
|
||||
#endif // STRATUM_API_H
|
@ -22,6 +22,7 @@
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "stratum_api.h"
|
||||
#include "work_queue.h"
|
||||
|
||||
#include "system.h"
|
||||
#include "serial.h"
|
||||
@ -41,6 +42,32 @@ static const char *TAG = "stratum client";
|
||||
TaskHandle_t sysTaskHandle = NULL;
|
||||
TaskHandle_t serialTaskHandle = NULL;
|
||||
|
||||
static work_queue g_queue;
|
||||
|
||||
static void stratum_worker_task(void *pvParameters)
|
||||
{
|
||||
int termination_flag = 0;
|
||||
while(true) {
|
||||
work next_work = queue_dequeue(&g_queue, &termination_flag);
|
||||
ESP_LOGI(TAG, "New Work Dequeued");
|
||||
// TODO: dequeue work from work_queue
|
||||
|
||||
// TODO: Construct the coinbase transaction and compute the merkle root
|
||||
// TODO: Prepare the block header and start mining
|
||||
|
||||
// TODO: Increment the nonce
|
||||
|
||||
// TODO: Update the block header with the new nonce
|
||||
// TODO: Hash the block header using SHA256 twice (double SHA256)
|
||||
// TODO: Check if the hash meets the target specified by nbits
|
||||
|
||||
// TODO: If hash meets target, submit shares
|
||||
// snprintf(submit_msg, BUFFER_SIZE,
|
||||
// "{\"id\": 4, \"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%08x\"]}\n",
|
||||
// user, job_id, ntime, extranonce2, nonce);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_client_task(void *pvParameters)
|
||||
{
|
||||
initialize_stratum_buffer();
|
||||
@ -83,12 +110,10 @@ static void tcp_client_task(void *pvParameters)
|
||||
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(TAG, "Successfully connected");
|
||||
|
||||
auth_to_stratum(sock, "johnny9.esp");
|
||||
|
||||
// Authorize
|
||||
char authorize_msg[] = "{\"id\": 2, \"method\": \"mining.authorize\", \"params\": [\"johnny9.esp\", \"x\"]}\n";
|
||||
write(sock, authorize_msg, strlen(authorize_msg));
|
||||
subscribe_to_stratum(sock);
|
||||
|
||||
while (1)
|
||||
{
|
||||
@ -100,6 +125,9 @@ static void tcp_client_task(void *pvParameters)
|
||||
ESP_LOGI(TAG, "UNKNOWN MESSAGE");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "method: %s", parsed_message.method_str);
|
||||
if (parsed_message.method == MINING_NOTIFY) {
|
||||
queue_enqueue(&g_queue, parsed_message.notify_work);
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
@ -129,5 +157,9 @@ void app_main(void)
|
||||
|
||||
xTaskCreate(SysTask, "System_Task", 4096, NULL, 10, &sysTaskHandle);
|
||||
xTaskCreate(SerialTask, "serial_test", 4096, NULL, 10, &serialTaskHandle);
|
||||
xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
|
||||
|
||||
queue_init(&g_queue);
|
||||
|
||||
xTaskCreate(tcp_client_task, "tcp_client", 8192, NULL, 5, NULL);
|
||||
xTaskCreate(stratum_worker_task, "miner", 8192, NULL, 5, NULL);
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
#define WORK_QUEUE_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include "stratum_api.h"
|
||||
|
||||
|
||||
#define QUEUE_SIZE 10
|
||||
|
||||
@ -9,20 +11,6 @@
|
||||
#define PREV_BLOCK_HASH_SIZE 32
|
||||
#define COINBASE_SIZE 100
|
||||
|
||||
typedef struct {
|
||||
uint32_t job_id;
|
||||
uint8_t prev_block_hash[PREV_BLOCK_HASH_SIZE];
|
||||
uint8_t coinbase[COINBASE_SIZE];
|
||||
size_t coinbase_len;
|
||||
uint8_t merkle_branches[MAX_MERKLE_BRANCHES][PREV_BLOCK_HASH_SIZE];
|
||||
size_t n_merkle_branches;
|
||||
uint32_t version;
|
||||
uint32_t curtime;
|
||||
uint32_t bits;
|
||||
uint32_t target;
|
||||
uint32_t nonce;
|
||||
} work;
|
||||
|
||||
typedef struct {
|
||||
work buffer[QUEUE_SIZE];
|
||||
int head;
|
||||
|
16
test/CMakeLists.txt
Normal file
16
test/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Include the components directory of the main application:
|
||||
#
|
||||
set(EXTRA_COMPONENT_DIRS "../components")
|
||||
|
||||
# Set the components to include the tests for.
|
||||
# This can be overriden from CMake cache:
|
||||
# - when invoking CMake directly: cmake -D TEST_COMPONENTS="xxxxx" ..
|
||||
# - when using idf.py: idf.py -T xxxxx build
|
||||
#
|
||||
set(TEST_COMPONENTS "stratum" CACHE STRING "List of components to test")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(unit_test_stratum)
|
2
test/main/CMakeLists.txt
Normal file
2
test/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "unit_test_stratum.c"
|
||||
INCLUDE_DIRS ".")
|
24
test/main/unit_test_stratum.c
Normal file
24
test/main/unit_test_stratum.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
|
||||
static void print_banner(const char* text);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
print_banner("Running all the registered tests");
|
||||
UNITY_BEGIN();
|
||||
unity_run_all_tests();
|
||||
UNITY_END();
|
||||
|
||||
print_banner("Starting interactive test menu");
|
||||
/* This function will not return, and will be busy waiting for UART input.
|
||||
* Make sure that task watchdog is disabled if you use this function.
|
||||
*/
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
static void print_banner(const char* text)
|
||||
{
|
||||
printf("\n#### %s #####\n\n", text);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user