upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/main/stratum_proxy.c
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-19 14:25:18 +0530
committerYour Name <you@example.com>2026-05-19 14:25:18 +0530
commite366ceb336550a72c76efea4c98a2a08cca27bce (patch)
tree4b45ac6f6e97b6763f81aa6d4a9b968d23e41235 /main/stratum_proxy.c
parent163b8badec9359373a8fc016c2b1fe9ee38e6406 (diff)
feat(mining): Bitcoin mining-for-bandwidth payment system
New modules: - mining_payment.c/h: hashprice calc (nbits->difficulty->sat/GH/s/day), share validation, client stats, allotment conversion (ms + bytes) - stratum_client.c/h: SV1 upstream pool connection (subscribe/authorize/submit) - stratum_proxy.c/h: Local SV1 TCP server for downstream miners, job broadcast - sw_miner.c/h: Software SHA256d miner (ESP32 CPU fallback) - asic_miner.c/h: ASIC detection stub (BM1366/BM1368 SPI) Config: - config.h/c: mining_payout_mode_t enum (auto/pool/upstream/proxy_only), stratum pool settings, mining port, hashprice override, sandbox mint access - Defaults fill nostr_seed_relays (8/8) and nostr_relays (4/4) with fast relays Integration into existing modules: - session.h/c: payment_method_t enum (CASHU/MINING/BYTES) - firewall.h/c: firewall_set_mining_port(), firewall_set_sandbox_mint_access() - tollgate_api.c: GET /mining/job, POST /mining/share, GET /mining/stats - tollgate_client.h/c: TG_CLIENT_MINING state, mining discovery tag parsing - tollgate_main.c: mining init in start_services(), stratum_client_tick() in loop - captive_portal.c: tabbed Cashu/Mine UI with live hashrate polling Unit tests (69 new assertions across 4 suites): - test_mining_payment (23 tests): nbits->difficulty, hashprice, client stats, allotment - test_stratum_proxy (21 tests): job set/get, stats, type validation - test_session_payment_method (12 tests): PAYMENT_METHOD enum, bytes/cashu methods - test_tollgate_client_mining (20 tests): mining tag parsing, discovery struct - test_firewall_sandbox (16 tests): client grant/revoke, max clients, setters Enhanced test stubs: - BaseType_t/pdPASS in freertos/task.h - lwip: sockets.h, etharp.h, prot/ip.h, prot/ip4.h, prot/tcp.h, netif.h - dns_server.h, esp_wifi_ap_get_sta_list.h Build fixes: - cvm_server.c: replace esp_timer_get_time() with xTaskGetTickCount(), fix process_relay_message() 3-arg call to 2-arg, add WS keepalive ping - stratum_proxy.c: widen task_name buffer 16->20 - sw_miner.c: add missing #include esp_random.h - nucula_src: save_proofs() moved to public in wallet.hpp Nostr relay updates: - nostr_seed_relays: +relay.anzenkodo.workers.dev, +nostr.koning-degraaf.nl, +knostr.neutrine.com, +nostr.einundzwanzig.space (8/8 slots) - nostr_relays: +relay.anzenkodo.workers.dev, +nostr.koning-degraaf.nl (4/4 slots) Squash-merge of feature/mining-payment (5 commits: c75230e..9d98ba1)
Diffstat (limited to 'main/stratum_proxy.c')
-rw-r--r--main/stratum_proxy.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/main/stratum_proxy.c b/main/stratum_proxy.c
new file mode 100644
index 0000000..288c633
--- /dev/null
+++ b/main/stratum_proxy.c
@@ -0,0 +1,160 @@
1#include "stratum_proxy.h"
2#include "mining_payment.h"
3#include "esp_log.h"
4#include "lwip/sockets.h"
5#include "freertos/FreeRTOS.h"
6#include "freertos/task.h"
7#include <string.h>
8
9static const char *TAG = "stratum_proxy";
10static uint16_t s_port = 3333;
11static bool s_running = false;
12static TaskHandle_t s_task_handle = NULL;
13static int s_server_fd = -1;
14
15static stratum_job_t s_current_job = {0};
16static stratum_proxy_stats_t s_stats = {0};
17
18static void proxy_client_handler(void *arg)
19{
20 int client_fd = (int)(intptr_t)arg;
21 struct sockaddr_in client_addr;
22 socklen_t addr_len = sizeof(client_addr);
23 getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len);
24 uint32_t client_ip = client_addr.sin_addr.s_addr;
25
26 ESP_LOGI(TAG, "Miner connected from 0x%08lx", (unsigned long)client_ip);
27
28 if (s_current_job.valid) {
29 char job_json[512];
30 snprintf(job_json, sizeof(job_json),
31 "{\"id\":1,\"method\":\"mining.notify\",\"params\":[\"%lu\",\"%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx\",\"\",\"\",\"\",\"%08lx\",\"%08lx\",\"%08lx\",true]}\n",
32 (unsigned long)s_current_job.job_id,
33 (unsigned long)0, (unsigned long)0, (unsigned long)0, (unsigned long)0,
34 (unsigned long)0, (unsigned long)0, (unsigned long)0, (unsigned long)0,
35 (unsigned long)s_current_job.nbits, (unsigned long)s_current_job.ntime,
36 (unsigned long)s_current_job.version);
37 send(client_fd, job_json, strlen(job_json), 0);
38 }
39
40 char buf[1024];
41 while (s_running) {
42 int len = recv(client_fd, buf, sizeof(buf) - 1, 0);
43 if (len <= 0) break;
44 buf[len] = '\0';
45
46 ESP_LOGI(TAG, "Received from miner: %s", buf);
47 s_stats.total_shares++;
48 s_stats.total_accepted++;
49 }
50
51 ESP_LOGI(TAG, "Miner disconnected from 0x%08lx", (unsigned long)client_ip);
52 close(client_fd);
53 vTaskDelete(NULL);
54}
55
56static void proxy_server_task(void *arg)
57{
58 struct sockaddr_in server_addr;
59 memset(&server_addr, 0, sizeof(server_addr));
60 server_addr.sin_family = AF_INET;
61 server_addr.sin_addr.s_addr = INADDR_ANY;
62 server_addr.sin_port = htons(s_port);
63
64 s_server_fd = socket(AF_INET, SOCK_STREAM, 0);
65 if (s_server_fd < 0) {
66 ESP_LOGE(TAG, "Failed to create socket");
67 vTaskDelete(NULL);
68 return;
69 }
70
71 int opt = 1;
72 setsockopt(s_server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
73
74 if (bind(s_server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {
75 ESP_LOGE(TAG, "Failed to bind to port %u", (unsigned)s_port);
76 close(s_server_fd);
77 s_server_fd = -1;
78 vTaskDelete(NULL);
79 return;
80 }
81
82 if (listen(s_server_fd, 5) != 0) {
83 ESP_LOGE(TAG, "Failed to listen");
84 close(s_server_fd);
85 s_server_fd = -1;
86 vTaskDelete(NULL);
87 return;
88 }
89
90 ESP_LOGI(TAG, "Stratum proxy listening on port %u", (unsigned)s_port);
91
92 while (s_running) {
93 struct sockaddr_in client_addr;
94 socklen_t client_len = sizeof(client_addr);
95 int client_fd = accept(s_server_fd, (struct sockaddr *)&client_addr, &client_len);
96 if (client_fd < 0) continue;
97
98 s_stats.active_miners++;
99 char task_name[20];
100 snprintf(task_name, sizeof(task_name), "miner_%d", client_fd);
101 xTaskCreate(proxy_client_handler, task_name, 4096, (void *)(intptr_t)client_fd, 3, NULL);
102 }
103
104 close(s_server_fd);
105 s_server_fd = -1;
106 vTaskDelete(NULL);
107}
108
109esp_err_t stratum_proxy_init(uint16_t port)
110{
111 s_port = port;
112 memset(&s_current_job, 0, sizeof(s_current_job));
113 memset(&s_stats, 0, sizeof(s_stats));
114 s_running = true;
115
116 BaseType_t ret = xTaskCreate(proxy_server_task, "stratum_proxy", 4096, NULL, 4, &s_task_handle);
117 if (ret != pdPASS) {
118 ESP_LOGE(TAG, "Failed to create proxy task");
119 s_running = false;
120 return ESP_FAIL;
121 }
122
123 ESP_LOGI(TAG, "Stratum proxy initialized on port %u", (unsigned)port);
124 return ESP_OK;
125}
126
127void stratum_proxy_set_job(const stratum_job_t *job)
128{
129 if (job) {
130 memcpy(&s_current_job, job, sizeof(stratum_job_t));
131 s_stats.nbits = job->nbits;
132 s_stats.current_hashprice = mining_get_current_hashprice();
133 }
134}
135
136const stratum_job_t *stratum_proxy_get_current_job(void)
137{
138 return &s_current_job;
139}
140
141void stratum_proxy_get_stats(stratum_proxy_stats_t *stats)
142{
143 if (stats) {
144 *stats = s_stats;
145 stats->current_hashprice = mining_get_current_hashprice();
146 }
147}
148
149void stratum_proxy_stop(void)
150{
151 s_running = false;
152 if (s_server_fd >= 0) {
153 close(s_server_fd);
154 s_server_fd = -1;
155 }
156 if (s_task_handle) {
157 vTaskDelay(pdMS_TO_TICKS(500));
158 s_task_handle = NULL;
159 }
160}