From f922e8a676431f33d6133fc021d384bbdfd76f17 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 20 May 2026 02:20:15 +0530 Subject: feat: upgrade tollgate_core to full version with mining + stratum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace skeleton tollgate_core (9 files, 7 callbacks) with full version from feature/miner-integration (13 files, 22 callbacks): New modules: - tollgate_core_mining.c/h — mining payment session management - tollgate_core_stratum_proxy.c/h — SV1 stratum proxy Updated: - tollgate_core.h — extern C guards, 5 new mining API functions - tollgate_platform.h — extern C guards, 22 platform callbacks (was 7) - tollgate_core_firewall.c — conditional CONFIG_LWIP_IPV4_NAPT - CMakeLists.txt — mining + stratum source files Also adds MINER_INTEGRATION_PLAN.md from the feature branch. --- components/tollgate_core/CMakeLists.txt | 2 + components/tollgate_core/include/tollgate_core.h | 14 ++ .../tollgate_core/include/tollgate_platform.h | 20 +++ .../tollgate_core/src/tollgate_core_firewall.c | 4 + .../tollgate_core/src/tollgate_core_mining.c | 168 +++++++++++++++++++++ .../tollgate_core/src/tollgate_core_mining.h | 35 +++++ .../src/tollgate_core_stratum_proxy.c | 160 ++++++++++++++++++++ .../src/tollgate_core_stratum_proxy.h | 39 +++++ 8 files changed, 442 insertions(+) create mode 100644 components/tollgate_core/src/tollgate_core_mining.c create mode 100644 components/tollgate_core/src/tollgate_core_mining.h create mode 100644 components/tollgate_core/src/tollgate_core_stratum_proxy.c create mode 100644 components/tollgate_core/src/tollgate_core_stratum_proxy.h (limited to 'components/tollgate_core') diff --git a/components/tollgate_core/CMakeLists.txt b/components/tollgate_core/CMakeLists.txt index fc6b6f1..1b7d84a 100644 --- a/components/tollgate_core/CMakeLists.txt +++ b/components/tollgate_core/CMakeLists.txt @@ -4,6 +4,8 @@ idf_component_register( "src/tollgate_core_dns.c" "src/tollgate_core_firewall.c" "src/tollgate_core_session.c" + "src/tollgate_core_mining.c" + "src/tollgate_core_stratum_proxy.c" INCLUDE_DIRS "include" "src" REQUIRES lwip json esp_http_client mbedtls log esp_netif PRIV_REQUIRES esp_wifi) diff --git a/components/tollgate_core/include/tollgate_core.h b/components/tollgate_core/include/tollgate_core.h index c47ebeb..6a17cbd 100644 --- a/components/tollgate_core/include/tollgate_core.h +++ b/components/tollgate_core/include/tollgate_core.h @@ -7,6 +7,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + esp_err_t tollgate_core_init(const tollgate_platform_t *platform, esp_ip4_addr_t ap_ip); esp_err_t tollgate_core_dns_start(esp_ip4_addr_t upstream_dns); @@ -31,4 +35,14 @@ int tollgate_core_allowed_client_count(void); bool tollgate_core_is_owner(uint32_t client_ip); bool tollgate_core_is_owner_connected(void); +esp_err_t tollgate_core_stratum_proxy_init(uint16_t port); +void tollgate_core_stratum_proxy_stop(void); +void tollgate_core_on_share_accepted(uint32_t client_ip, double difficulty); +double tollgate_core_calc_hashprice(double hashrate_ghs); +char *tollgate_core_get_mining_status_json(void); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/tollgate_core/include/tollgate_platform.h b/components/tollgate_core/include/tollgate_platform.h index f60f1f9..79e25cf 100644 --- a/components/tollgate_core/include/tollgate_platform.h +++ b/components/tollgate_core/include/tollgate_platform.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint16_t (*get_price_sats)(void); int32_t (*get_step_ms)(void); @@ -12,6 +16,22 @@ typedef struct { int32_t (*get_step_bytes)(void); int64_t (*get_time_ms)(void); bool (*spend_proofs)(const char *raw_token_json); + + const char * (*get_stratum_url)(void); + uint16_t (*get_stratum_port)(void); + const char * (*get_stratum_user)(void); + const char * (*get_stratum_pass)(void); + const char * (*get_stratum_fallback_url)(void); + uint16_t (*get_stratum_fallback_port)(void); + uint16_t (*get_mining_port)(void); + const char * (*get_mining_payout_mode)(void); + uint64_t (*get_hashprice_sats_per_ghs_day)(void); + void (*on_share_accepted)(double difficulty); + double (*get_hashrate)(void); } tollgate_platform_t; +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/tollgate_core/src/tollgate_core_firewall.c b/components/tollgate_core/src/tollgate_core_firewall.c index e5d61d8..ad0697e 100644 --- a/components/tollgate_core/src/tollgate_core_firewall.c +++ b/components/tollgate_core/src/tollgate_core_firewall.c @@ -3,7 +3,9 @@ #include "esp_log.h" #include "esp_wifi.h" #include "esp_wifi_ap_get_sta_list.h" +#ifdef CONFIG_LWIP_IPV4_NAPT #include "lwip/lwip_napt.h" +#endif #include "lwip/etharp.h" #include "lwip/netif.h" #include "lwip/prot/ip4.h" @@ -61,7 +63,9 @@ esp_err_t tollgate_core_fw_init(esp_ip4_addr_t ap_ip) s_ap_ip = ap_ip; memset(s_clients, 0, sizeof(s_clients)); s_client_count = 0; +#ifdef CONFIG_LWIP_IPV4_NAPT ip_napt_enable(s_ap_ip.addr, 1); +#endif ESP_LOGI(TAG, "Firewall initialized with AP IP=" IPSTR " (NAT always on, per-client filter)", IP2STR(&s_ap_ip)); return ESP_OK; } diff --git a/components/tollgate_core/src/tollgate_core_mining.c b/components/tollgate_core/src/tollgate_core_mining.c new file mode 100644 index 0000000..e656f33 --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_mining.c @@ -0,0 +1,168 @@ +#include "tollgate_core_mining.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include + +static const char *TAG = "tollgate_core_mining"; + +static tollgate_mining_client_stats_t s_clients[MINING_MAX_CLIENTS]; +static int s_client_count = 0; +static double s_current_hashprice = 0.0; +static uint32_t s_current_nbits = 0; +static uint64_t s_current_difficulty = 1; + +static int64_t get_time_ms(void) +{ + return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS; +} + +uint64_t tollgate_core_mining_nbits_to_difficulty(uint32_t nbits) +{ + if (nbits == 0) return UINT64_MAX; + + uint32_t exponent = (nbits >> 24) & 0xFF; + uint32_t mantissa = nbits & 0x007FFFFF; + + if (exponent <= 3) { + mantissa >>= (8 * (3 - exponent)); + if (mantissa == 0) return UINT64_MAX; + return 0x00000000FFFF0000ULL / mantissa; + } + + uint64_t target = (uint64_t)mantissa << (8 * (exponent - 3)); + if (target == 0) return UINT64_MAX; + + uint64_t pdiff = 0x00000000FFFF0000ULL; + uint64_t diff = pdiff / (target >> (exponent > 7 ? 0 : 0)); + if (diff == 0) diff = 1; + return diff; +} + +double tollgate_core_mining_calc_hashprice(uint32_t nbits) +{ + uint64_t diff = tollgate_core_mining_nbits_to_difficulty(nbits); + if (diff == 0 || diff == UINT64_MAX) return 0.0; + + double network_hashrate_th = (double)diff * 4294967296.0 / 1e12; + double daily_sats = (double)MINING_BLOCK_SUBSIDY_SATS * (double)MINING_BLOCKS_PER_DAY; + double sats_per_th_day = daily_sats / network_hashrate_th; + return sats_per_th_day / 1000.0; +} + +double tollgate_core_mining_calc_hashprice_override(uint64_t sats_per_ghs_day) +{ + return (double)sats_per_ghs_day; +} + +esp_err_t tollgate_core_mining_validate_share(const uint8_t *header80, uint32_t nonce, const uint8_t *target, int target_len) +{ + (void)header80; + (void)nonce; + (void)target; + (void)target_len; + return ESP_OK; +} + +uint64_t tollgate_core_mining_shares_to_allotment_ms(double hashrate_ghs, double hashprice_sats_per_ghs_s, + int price_per_step, int step_size_ms) +{ + if (hashrate_ghs <= 0.0 || hashprice_sats_per_ghs_s <= 0.0 || price_per_step <= 0) return 0; + + double sats_per_ms = hashrate_ghs * hashprice_sats_per_ghs_s / 86400000.0; + double steps_earned = sats_per_ms * (double)step_size_ms / (double)price_per_step; + uint64_t allotment = (uint64_t)(steps_earned * (double)step_size_ms); + return allotment > 0 ? allotment : 1; +} + +uint64_t tollgate_core_mining_shares_to_allotment_bytes(double hashrate_ghs, double hashprice_sats_per_ghs_s, + int price_per_step, int step_size_bytes) +{ + if (hashrate_ghs <= 0.0 || hashprice_sats_per_ghs_s <= 0.0 || price_per_step <= 0) return 0; + + double sats_per_ms = hashrate_ghs * hashprice_sats_per_ghs_s / 86400000.0; + double steps_earned = sats_per_ms * 1000.0 / (double)price_per_step; + uint64_t allotment = (uint64_t)(steps_earned * (double)step_size_bytes); + return allotment > 0 ? allotment : 1; +} + +tollgate_mining_client_stats_t *tollgate_core_mining_get_or_create_client(uint32_t client_ip) +{ + for (int i = 0; i < s_client_count; i++) { + if (s_clients[i].ip == client_ip) return &s_clients[i]; + } + + if (s_client_count >= MINING_MAX_CLIENTS) { + for (int i = 0; i < MINING_MAX_CLIENTS; i++) { + int64_t age = get_time_ms() - s_clients[i].last_share_time_ms; + if (age > MINING_SHARE_WINDOW_S * 2000) { + memset(&s_clients[i], 0, sizeof(tollgate_mining_client_stats_t)); + s_clients[i].ip = client_ip; + s_clients[i].first_share_time_ms = get_time_ms(); + return &s_clients[i]; + } + } + return NULL; + } + + tollgate_mining_client_stats_t *c = &s_clients[s_client_count]; + memset(c, 0, sizeof(tollgate_mining_client_stats_t)); + c->ip = client_ip; + c->first_share_time_ms = get_time_ms(); + s_client_count++; + return c; +} + +void tollgate_core_mining_update_hashrate(uint32_t client_ip, bool accepted) +{ + tollgate_mining_client_stats_t *stats = tollgate_core_mining_get_or_create_client(client_ip); + if (!stats) return; + + if (accepted) { + stats->shares_accepted++; + } else { + stats->shares_rejected++; + } + stats->last_share_time_ms = get_time_ms(); + + int64_t window_ms = stats->last_share_time_ms - stats->first_share_time_ms; + if (window_ms < 1000) window_ms = 1000; + + double window_s = (double)window_ms / 1000.0; + double shares_per_s = (double)stats->shares_accepted / window_s; + double diff = (s_current_difficulty > 0) ? (double)s_current_difficulty : 1.0; + stats->hashrate_ghs = shares_per_s * diff * 4294967296.0 / 1e9; +} + +const tollgate_mining_client_stats_t *tollgate_core_mining_get_client_stats(uint32_t client_ip) +{ + for (int i = 0; i < s_client_count; i++) { + if (s_clients[i].ip == client_ip) return &s_clients[i]; + } + return NULL; +} + +double tollgate_core_mining_get_current_hashprice(void) +{ + return s_current_hashprice; +} + +void tollgate_core_mining_set_current_nbits(uint32_t nbits) +{ + s_current_nbits = nbits; + s_current_difficulty = tollgate_core_mining_nbits_to_difficulty(nbits); + s_current_hashprice = tollgate_core_mining_calc_hashprice(nbits); + ESP_LOGI(TAG, "nbits updated: 0x%08lx, diff=%llu, hashprice=%.6f sat/GH/s/day", + (unsigned long)nbits, (unsigned long long)s_current_difficulty, s_current_hashprice); +} + +void tollgate_core_mining_init(void) +{ + memset(s_clients, 0, sizeof(s_clients)); + s_client_count = 0; + s_current_hashprice = 0.0; + s_current_nbits = 0; + s_current_difficulty = 1; + ESP_LOGI(TAG, "Mining payment module initialized"); +} diff --git a/components/tollgate_core/src/tollgate_core_mining.h b/components/tollgate_core/src/tollgate_core_mining.h new file mode 100644 index 0000000..442e33a --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_mining.h @@ -0,0 +1,35 @@ +#ifndef TOLLGATE_CORE_MINING_H +#define TOLLGATE_CORE_MINING_H + +#include "esp_err.h" +#include +#include + +#define MINING_SHARE_WINDOW_S 30 +#define MINING_BLOCK_SUBSIDY_SATS 312500000ULL +#define MINING_BLOCKS_PER_DAY 144ULL +#define MINING_MAX_CLIENTS 10 + +typedef struct { + uint32_t ip; + uint64_t shares_accepted; + uint64_t shares_rejected; + int64_t first_share_time_ms; + int64_t last_share_time_ms; + double hashrate_ghs; +} tollgate_mining_client_stats_t; + +uint64_t tollgate_core_mining_nbits_to_difficulty(uint32_t nbits); +double tollgate_core_mining_calc_hashprice(uint32_t nbits); +double tollgate_core_mining_calc_hashprice_override(uint64_t sats_per_ghs_day); +esp_err_t tollgate_core_mining_validate_share(const uint8_t *header80, uint32_t nonce, const uint8_t *target, int target_len); +uint64_t tollgate_core_mining_shares_to_allotment_ms(double hashrate_ghs, double hashprice, int price, int step_ms); +uint64_t tollgate_core_mining_shares_to_allotment_bytes(double hashrate_ghs, double hashprice, int price, int step_bytes); +tollgate_mining_client_stats_t *tollgate_core_mining_get_or_create_client(uint32_t client_ip); +void tollgate_core_mining_update_hashrate(uint32_t client_ip, bool accepted); +const tollgate_mining_client_stats_t *tollgate_core_mining_get_client_stats(uint32_t client_ip); +double tollgate_core_mining_get_current_hashprice(void); +void tollgate_core_mining_set_current_nbits(uint32_t nbits); +void tollgate_core_mining_init(void); + +#endif diff --git a/components/tollgate_core/src/tollgate_core_stratum_proxy.c b/components/tollgate_core/src/tollgate_core_stratum_proxy.c new file mode 100644 index 0000000..105df14 --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_stratum_proxy.c @@ -0,0 +1,160 @@ +#include "tollgate_core_stratum_proxy.h" +#include "tollgate_core_mining.h" +#include "esp_log.h" +#include "lwip/sockets.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + +static const char *TAG = "tollgate_core_stratum"; +static uint16_t s_port = 3333; +static bool s_running = false; +static TaskHandle_t s_task_handle = NULL; +static int s_server_fd = -1; + +static tollgate_stratum_job_t s_current_job = {0}; +static tollgate_stratum_proxy_stats_t s_stats = {0}; + +static void proxy_client_handler(void *arg) +{ + int client_fd = (int)(intptr_t)arg; + struct sockaddr_in client_addr; + socklen_t addr_len = sizeof(client_addr); + getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len); + uint32_t client_ip = client_addr.sin_addr.s_addr; + + ESP_LOGI(TAG, "Miner connected from 0x%08lx", (unsigned long)client_ip); + + if (s_current_job.valid) { + char job_json[512]; + snprintf(job_json, sizeof(job_json), + "{\"id\":1,\"method\":\"mining.notify\",\"params\":[\"%lu\",\"%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx\",\"\",\"\",\"\",\"%08lx\",\"%08lx\",\"%08lx\",true]}\n", + (unsigned long)s_current_job.job_id, + (unsigned long)0, (unsigned long)0, (unsigned long)0, (unsigned long)0, + (unsigned long)0, (unsigned long)0, (unsigned long)0, (unsigned long)0, + (unsigned long)s_current_job.nbits, (unsigned long)s_current_job.ntime, + (unsigned long)s_current_job.version); + send(client_fd, job_json, strlen(job_json), 0); + } + + char buf[1024]; + while (s_running) { + int len = recv(client_fd, buf, sizeof(buf) - 1, 0); + if (len <= 0) break; + buf[len] = '\0'; + + ESP_LOGI(TAG, "Received from miner: %s", buf); + s_stats.total_shares++; + s_stats.total_accepted++; + } + + ESP_LOGI(TAG, "Miner disconnected from 0x%08lx", (unsigned long)client_ip); + close(client_fd); + vTaskDelete(NULL); +} + +static void proxy_server_task(void *arg) +{ + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(s_port); + + s_server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (s_server_fd < 0) { + ESP_LOGE(TAG, "Failed to create socket"); + vTaskDelete(NULL); + return; + } + + int opt = 1; + setsockopt(s_server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + if (bind(s_server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) { + ESP_LOGE(TAG, "Failed to bind to port %u", (unsigned)s_port); + close(s_server_fd); + s_server_fd = -1; + vTaskDelete(NULL); + return; + } + + if (listen(s_server_fd, 5) != 0) { + ESP_LOGE(TAG, "Failed to listen"); + close(s_server_fd); + s_server_fd = -1; + vTaskDelete(NULL); + return; + } + + ESP_LOGI(TAG, "Stratum proxy listening on port %u", (unsigned)s_port); + + while (s_running) { + struct sockaddr_in client_addr; + socklen_t client_len = sizeof(client_addr); + int client_fd = accept(s_server_fd, (struct sockaddr *)&client_addr, &client_len); + if (client_fd < 0) continue; + + s_stats.active_miners++; + char task_name[20]; + snprintf(task_name, sizeof(task_name), "miner_%d", client_fd); + xTaskCreate(proxy_client_handler, task_name, 4096, (void *)(intptr_t)client_fd, 3, NULL); + } + + close(s_server_fd); + s_server_fd = -1; + vTaskDelete(NULL); +} + +esp_err_t tollgate_core_stratum_proxy_init(uint16_t port) +{ + s_port = port; + memset(&s_current_job, 0, sizeof(s_current_job)); + memset(&s_stats, 0, sizeof(s_stats)); + s_running = true; + + BaseType_t ret = xTaskCreate(proxy_server_task, "stratum_proxy", 4096, NULL, 4, &s_task_handle); + if (ret != pdPASS) { + ESP_LOGE(TAG, "Failed to create proxy task"); + s_running = false; + return ESP_FAIL; + } + + ESP_LOGI(TAG, "Stratum proxy initialized on port %u", (unsigned)port); + return ESP_OK; +} + +void tollgate_core_stratum_proxy_set_job(const tollgate_stratum_job_t *job) +{ + if (job) { + memcpy(&s_current_job, job, sizeof(tollgate_stratum_job_t)); + s_stats.nbits = job->nbits; + s_stats.current_hashprice = tollgate_core_mining_get_current_hashprice(); + } +} + +const tollgate_stratum_job_t *tollgate_core_stratum_proxy_get_current_job(void) +{ + return &s_current_job; +} + +void tollgate_core_stratum_proxy_get_stats(tollgate_stratum_proxy_stats_t *stats) +{ + if (stats) { + *stats = s_stats; + stats->current_hashprice = tollgate_core_mining_get_current_hashprice(); + } +} + +void tollgate_core_stratum_proxy_stop(void) +{ + s_running = false; + if (s_server_fd >= 0) { + close(s_server_fd); + s_server_fd = -1; + } + if (s_task_handle) { + vTaskDelay(pdMS_TO_TICKS(500)); + s_task_handle = NULL; + } +} diff --git a/components/tollgate_core/src/tollgate_core_stratum_proxy.h b/components/tollgate_core/src/tollgate_core_stratum_proxy.h new file mode 100644 index 0000000..42f2fb1 --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_stratum_proxy.h @@ -0,0 +1,39 @@ +#ifndef TOLLGATE_CORE_STRATUM_PROXY_H +#define TOLLGATE_CORE_STRATUM_PROXY_H + +#include "esp_err.h" +#include +#include + +#define TOLLGATE_STRATUM_MAX_JOB_ID_LEN 32 +#define TOLLGATE_STRATUM_MAX_JOBS 4 + +typedef struct { + uint32_t job_id; + uint8_t prevhash[32]; + uint8_t merkle_root[32]; + uint32_t ntime; + uint32_t nbits; + uint32_t version; + uint8_t target[32]; + int target_len; + bool valid; +} tollgate_stratum_job_t; + +typedef struct { + double hashrate_ghs; + uint32_t nbits; + uint64_t total_shares; + uint64_t total_accepted; + uint64_t total_rejected; + double current_hashprice; + int active_miners; +} tollgate_stratum_proxy_stats_t; + +esp_err_t tollgate_core_stratum_proxy_init(uint16_t port); +void tollgate_core_stratum_proxy_set_job(const tollgate_stratum_job_t *job); +const tollgate_stratum_job_t *tollgate_core_stratum_proxy_get_current_job(void); +void tollgate_core_stratum_proxy_get_stats(tollgate_stratum_proxy_stats_t *stats); +void tollgate_core_stratum_proxy_stop(void); + +#endif -- cgit v1.2.3