diff options
| author | Your Name <you@example.com> | 2026-05-19 14:25:18 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-19 14:25:18 +0530 |
| commit | e366ceb336550a72c76efea4c98a2a08cca27bce (patch) | |
| tree | 4b45ac6f6e97b6763f81aa6d4a9b968d23e41235 /main/config.c | |
| parent | 163b8badec9359373a8fc016c2b1fe9ee38e6406 (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/config.c')
| -rw-r--r-- | main/config.c | 87 |
1 files changed, 83 insertions, 4 deletions
diff --git a/main/config.c b/main/config.c index 5e3b247..6644b3a 100644 --- a/main/config.c +++ b/main/config.c | |||
| @@ -39,6 +39,13 @@ esp_err_t tollgate_config_init(void) | |||
| 39 | strncpy(g_config.cvm_relays, "wss://relay.primal.net", sizeof(g_config.cvm_relays) - 1); | 39 | strncpy(g_config.cvm_relays, "wss://relay.primal.net", sizeof(g_config.cvm_relays) - 1); |
| 40 | strncpy(g_config.wifi_auth_mode, "WPA2", sizeof(g_config.wifi_auth_mode) - 1); | 40 | strncpy(g_config.wifi_auth_mode, "WPA2", sizeof(g_config.wifi_auth_mode) - 1); |
| 41 | g_config.display_enabled = true; | 41 | g_config.display_enabled = true; |
| 42 | g_config.nostr_sync_interval_s = 1800; | ||
| 43 | g_config.nostr_fallback_sync_interval_s = 21600; | ||
| 44 | g_config.mining_enabled = false; | ||
| 45 | g_config.mining_payout_mode = MINING_PAYOUT_AUTO; | ||
| 46 | g_config.stratum_port = 3333; | ||
| 47 | g_config.mining_port = 3334; | ||
| 48 | g_config.mining_sandbox_mint_access = true; | ||
| 42 | 49 | ||
| 43 | esp_vfs_spiffs_conf_t conf = { | 50 | esp_vfs_spiffs_conf_t conf = { |
| 44 | .base_path = "/spiffs", | 51 | .base_path = "/spiffs", |
| @@ -314,6 +321,68 @@ esp_err_t tollgate_config_init(void) | |||
| 314 | g_config.payout.mint_count = 1; | 321 | g_config.payout.mint_count = 1; |
| 315 | } | 322 | } |
| 316 | 323 | ||
| 324 | cJSON *seed_relays = cJSON_GetObjectItem(root, "nostr_seed_relays"); | ||
| 325 | if (seed_relays && cJSON_IsArray(seed_relays)) { | ||
| 326 | int srcount = cJSON_GetArraySize(seed_relays); | ||
| 327 | if (srcount > TOLLGATE_MAX_SEED_RELAYS) srcount = TOLLGATE_MAX_SEED_RELAYS; | ||
| 328 | for (int i = 0; i < srcount; i++) { | ||
| 329 | cJSON *r = cJSON_GetArrayItem(seed_relays, i); | ||
| 330 | if (r && cJSON_IsString(r)) { | ||
| 331 | strncpy(g_config.nostr_seed_relays[i], r->valuestring, | ||
| 332 | sizeof(g_config.nostr_seed_relays[i]) - 1); | ||
| 333 | g_config.nostr_seed_relay_count++; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | cJSON *sync_interval = cJSON_GetObjectItem(root, "nostr_sync_interval_s"); | ||
| 339 | if (sync_interval) g_config.nostr_sync_interval_s = sync_interval->valueint; | ||
| 340 | |||
| 341 | cJSON *fallback_interval = cJSON_GetObjectItem(root, "nostr_fallback_sync_interval_s"); | ||
| 342 | if (fallback_interval) g_config.nostr_fallback_sync_interval_s = fallback_interval->valueint; | ||
| 343 | |||
| 344 | cJSON *mining = cJSON_GetObjectItem(root, "mining"); | ||
| 345 | if (mining && cJSON_IsObject(mining)) { | ||
| 346 | cJSON *m_en = cJSON_GetObjectItem(mining, "enabled"); | ||
| 347 | if (m_en && cJSON_IsBool(m_en)) g_config.mining_enabled = cJSON_IsTrue(m_en); | ||
| 348 | |||
| 349 | cJSON *m_mode = cJSON_GetObjectItem(mining, "payout_mode"); | ||
| 350 | if (m_mode && cJSON_IsString(m_mode)) { | ||
| 351 | if (strcmp(m_mode->valuestring, "pool") == 0) g_config.mining_payout_mode = MINING_PAYOUT_POOL; | ||
| 352 | else if (strcmp(m_mode->valuestring, "upstream") == 0) g_config.mining_payout_mode = MINING_PAYOUT_UPSTREAM; | ||
| 353 | else if (strcmp(m_mode->valuestring, "proxy_only") == 0) g_config.mining_payout_mode = MINING_PAYOUT_PROXY_ONLY; | ||
| 354 | } | ||
| 355 | |||
| 356 | cJSON *m_host = cJSON_GetObjectItem(mining, "stratum_host"); | ||
| 357 | if (m_host && cJSON_IsString(m_host)) strncpy(g_config.stratum_host, m_host->valuestring, sizeof(g_config.stratum_host) - 1); | ||
| 358 | |||
| 359 | cJSON *m_port = cJSON_GetObjectItem(mining, "stratum_port"); | ||
| 360 | if (m_port) g_config.stratum_port = (uint16_t)m_port->valueint; | ||
| 361 | |||
| 362 | cJSON *m_user = cJSON_GetObjectItem(mining, "stratum_user"); | ||
| 363 | if (m_user && cJSON_IsString(m_user)) strncpy(g_config.stratum_user, m_user->valuestring, sizeof(g_config.stratum_user) - 1); | ||
| 364 | |||
| 365 | cJSON *m_pass = cJSON_GetObjectItem(mining, "stratum_pass"); | ||
| 366 | if (m_pass && cJSON_IsString(m_pass)) strncpy(g_config.stratum_pass, m_pass->valuestring, sizeof(g_config.stratum_pass) - 1); | ||
| 367 | |||
| 368 | cJSON *m_fb_host = cJSON_GetObjectItem(mining, "stratum_fallback_host"); | ||
| 369 | if (m_fb_host && cJSON_IsString(m_fb_host)) strncpy(g_config.stratum_fallback_host, m_fb_host->valuestring, sizeof(g_config.stratum_fallback_host) - 1); | ||
| 370 | |||
| 371 | cJSON *m_fb_port = cJSON_GetObjectItem(mining, "stratum_fallback_port"); | ||
| 372 | if (m_fb_port) g_config.stratum_fallback_port = (uint16_t)m_fb_port->valueint; | ||
| 373 | |||
| 374 | cJSON *m_mport = cJSON_GetObjectItem(mining, "mining_port"); | ||
| 375 | if (m_mport) g_config.mining_port = (uint16_t)m_mport->valueint; | ||
| 376 | |||
| 377 | cJSON *m_hp = cJSON_GetObjectItem(mining, "hashprice_sats_per_ghs_day"); | ||
| 378 | if (m_hp) g_config.hashprice_sats_per_ghs_day = (uint64_t)m_hp->valuedouble; | ||
| 379 | |||
| 380 | cJSON *m_sandbox = cJSON_GetObjectItem(mining, "sandbox_mint_access"); | ||
| 381 | if (m_sandbox && cJSON_IsBool(m_sandbox)) g_config.mining_sandbox_mint_access = cJSON_IsTrue(m_sandbox); | ||
| 382 | } | ||
| 383 | |||
| 384 | cJSON_Delete(root); | ||
| 385 | |||
| 317 | if (g_config.payout.recipient_count == 0) { | 386 | if (g_config.payout.recipient_count == 0) { |
| 318 | strncpy(g_config.payout.recipients[0].lightning_address, "TollGate@coinos.io", | 387 | strncpy(g_config.payout.recipients[0].lightning_address, "TollGate@coinos.io", |
| 319 | sizeof(g_config.payout.recipients[0].lightning_address) - 1); | 388 | sizeof(g_config.payout.recipients[0].lightning_address) - 1); |
| @@ -321,8 +390,6 @@ esp_err_t tollgate_config_init(void) | |||
| 321 | g_config.payout.recipient_count = 1; | 390 | g_config.payout.recipient_count = 1; |
| 322 | } | 391 | } |
| 323 | 392 | ||
| 324 | cJSON_Delete(root); | ||
| 325 | |||
| 326 | if (g_config.accepted_mint_count == 0 && g_config.mint_url[0] != '\0') { | 393 | if (g_config.accepted_mint_count == 0 && g_config.mint_url[0] != '\0') { |
| 327 | strncpy(g_config.accepted_mints[0], g_config.mint_url, | 394 | strncpy(g_config.accepted_mints[0], g_config.mint_url, |
| 328 | sizeof(g_config.accepted_mints[0]) - 1); | 395 | sizeof(g_config.accepted_mints[0]) - 1); |
| @@ -332,7 +399,11 @@ esp_err_t tollgate_config_init(void) | |||
| 332 | if (g_config.nostr_relay_count == 0) { | 399 | if (g_config.nostr_relay_count == 0) { |
| 333 | strncpy(g_config.nostr_relays[0], "wss://relay.damus.io", sizeof(g_config.nostr_relays[0]) - 1); | 400 | strncpy(g_config.nostr_relays[0], "wss://relay.damus.io", sizeof(g_config.nostr_relays[0]) - 1); |
| 334 | strncpy(g_config.nostr_relays[1], "wss://nos.lol", sizeof(g_config.nostr_relays[1]) - 1); | 401 | strncpy(g_config.nostr_relays[1], "wss://nos.lol", sizeof(g_config.nostr_relays[1]) - 1); |
| 335 | g_config.nostr_relay_count = 2; | 402 | strncpy(g_config.nostr_relays[2], "wss://relay.anzenkodo.workers.dev", |
| 403 | sizeof(g_config.nostr_relays[2]) - 1); | ||
| 404 | strncpy(g_config.nostr_relays[3], "wss://nostr.koning-degraaf.nl", | ||
| 405 | sizeof(g_config.nostr_relays[3]) - 1); | ||
| 406 | g_config.nostr_relay_count = 4; | ||
| 336 | } | 407 | } |
| 337 | 408 | ||
| 338 | if (g_config.nostr_seed_relay_count == 0) { | 409 | if (g_config.nostr_seed_relay_count == 0) { |
| @@ -344,7 +415,15 @@ esp_err_t tollgate_config_init(void) | |||
| 344 | sizeof(g_config.nostr_seed_relays[2]) - 1); | 415 | sizeof(g_config.nostr_seed_relays[2]) - 1); |
| 345 | strncpy(g_config.nostr_seed_relays[3], "wss://relay.nostr.band", | 416 | strncpy(g_config.nostr_seed_relays[3], "wss://relay.nostr.band", |
| 346 | sizeof(g_config.nostr_seed_relays[3]) - 1); | 417 | sizeof(g_config.nostr_seed_relays[3]) - 1); |
| 347 | g_config.nostr_seed_relay_count = 4; | 418 | strncpy(g_config.nostr_seed_relays[4], "wss://relay.anzenkodo.workers.dev", |
| 419 | sizeof(g_config.nostr_seed_relays[4]) - 1); | ||
| 420 | strncpy(g_config.nostr_seed_relays[5], "wss://nostr.koning-degraaf.nl", | ||
| 421 | sizeof(g_config.nostr_seed_relays[5]) - 1); | ||
| 422 | strncpy(g_config.nostr_seed_relays[6], "wss://knostr.neutrine.com", | ||
| 423 | sizeof(g_config.nostr_seed_relays[6]) - 1); | ||
| 424 | strncpy(g_config.nostr_seed_relays[7], "wss://nostr.einundzwanzig.space", | ||
| 425 | sizeof(g_config.nostr_seed_relays[7]) - 1); | ||
| 426 | g_config.nostr_seed_relay_count = 8; | ||
| 348 | } | 427 | } |
| 349 | 428 | ||
| 350 | ESP_LOGI(TAG, "Config loaded: nsec=%s...%s, %d WiFi networks, %d accepted mints, price=%d sats/%dms", | 429 | ESP_LOGI(TAG, "Config loaded: nsec=%s...%s, %d WiFi networks, %d accepted mints, price=%d sats/%dms", |