From f6369f5b87f5dab17a03315d82454fdcdd668ec9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 18 May 2026 18:50:20 +0530 Subject: feat: extract tollgate_core ESP-IDF component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract shared TollGate business logic into a reusable ESP-IDF component that can be consumed by esp-miner via the IDF Component Manager. Component structure: components/tollgate_core/ include/tollgate_core.h — public API include/tollgate_platform.h — platform interface (config callbacks) src/tollgate_core.c — orchestrator (init, payment, tick, owner) src/tollgate_core_cashu.c/h — Cashu V3 token decode/verify src/tollgate_core_dns.c/h — per-client DNS hijack/forward src/tollgate_core_firewall.c/h — per-client NAT filter src/tollgate_core_session.c/h — session lifecycle Key design decisions: - Platform interface pattern: consumers implement tollgate_platform_t (config getters + optional spend_proofs wallet hook) - Cross-module wiring (session→firewall→dns) stays internal - No direct config.h dependency — all config via platform callbacks - spend_proofs can be NULL (accepts payment without local wallet) Standalone app updated: - main/tollgate_platform.c implements platform via config singleton - main/tollgate_main.c calls tollgate_core_init/tick/dns_start - main/tollgate_api.c routes payment through tollgate_core_process_payment - Removed cashu.c, dns_server.c, firewall.c, session.c from CMakeLists Not yet build-verified — blocked on multi-mint, price-discovery, cvm-integration, and display-fix branches merging to master. --- main/CMakeLists.txt | 6 +- main/lwip_tollgate_hooks.h | 4 +- main/tollgate_api.c | 156 ++++----------------------------------------- main/tollgate_main.c | 30 +++------ main/tollgate_platform.c | 64 +++++++++++++++++++ 5 files changed, 91 insertions(+), 169 deletions(-) create mode 100644 main/tollgate_platform.c (limited to 'main') diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 9b0fb1c..5f195c3 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,10 +1,7 @@ idf_component_register(SRCS "tollgate_main.c" "config.c" - "dns_server.c" + "tollgate_platform.c" "captive_portal.c" - "firewall.c" - "cashu.c" - "session.c" "tollgate_api.c" "identity.c" "nostr_event.c" @@ -22,4 +19,5 @@ idf_component_register(SRCS "tollgate_main.c" REQUIRES esp_wifi esp_event esp_netif nvs_flash esp_http_server lwip json esp_http_client mbedtls esp-tls log spiffs nucula_lib secp256k1 axs15231b qrcode + tollgate_core PRIV_REQUIRES esp-tls) diff --git a/main/lwip_tollgate_hooks.h b/main/lwip_tollgate_hooks.h index 76017be..2953c48 100644 --- a/main/lwip_tollgate_hooks.h +++ b/main/lwip_tollgate_hooks.h @@ -3,8 +3,8 @@ #include "lwip/pbuf.h" -int tollgate_ip4_canforward_filter(struct pbuf *p, u32_t dest_addr_hostorder); +int tollgate_core_ip4_canforward_filter(struct pbuf *p, u32_t dest_addr_hostorder); -#define LWIP_HOOK_IP4_CANFORWARD(p, addr) tollgate_ip4_canforward_filter(p, addr) +#define LWIP_HOOK_IP4_CANFORWARD(p, addr) tollgate_core_ip4_canforward_filter(p, addr) #endif diff --git a/main/tollgate_api.c b/main/tollgate_api.c index 650b0f3..62f75ee 100644 --- a/main/tollgate_api.c +++ b/main/tollgate_api.c @@ -1,8 +1,6 @@ #include "tollgate_api.h" -#include "cashu.h" +#include "tollgate_core.h" #include "config.h" -#include "session.h" -#include "firewall.h" #include "nucula_wallet.h" #include "esp_log.h" #include "cJSON.h" @@ -184,37 +182,11 @@ static esp_err_t api_post_payment(httpd_req_t *req) ESP_LOGI(TAG, "Payment received: %d bytes", total); - cashu_token_t *token = malloc(sizeof(cashu_token_t)); - if (!token) { - cJSON *notice = create_notice("error", "session-error", "Out of memory"); - char *json = cJSON_PrintUnformatted(notice); - httpd_resp_set_status(req, "503 Service Unavailable"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, strlen(json)); - cJSON_free(json); - cJSON_Delete(notice); - return ESP_OK; - } - esp_err_t err = cashu_decode_token(body, token); - char *body_copy = strdup(body); + esp_err_t err = tollgate_core_process_payment(client_ip, body); free(body); if (err != ESP_OK) { - free(token); - cJSON *notice = create_notice("error", "payment-error-invalid", "Failed to decode Cashu token"); - char *json = cJSON_PrintUnformatted(notice); - httpd_resp_set_status(req, "400 Bad Request"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, strlen(json)); - cJSON_free(json); - cJSON_Delete(notice); - return ESP_OK; - } - - const char *mint_url = token->mint_url[0] ? token->mint_url : tollgate_config_get()->mint_url; - if (!cashu_is_mint_accepted(mint_url)) { - free(token); - cJSON *notice = create_notice("error", "payment-error-mint-not-accepted", "Mint not accepted"); + cJSON *notice = create_notice("error", "payment-error", "Payment processing failed"); char *json = cJSON_PrintUnformatted(notice); httpd_resp_set_status(req, "402 Payment Required"); httpd_resp_set_type(req, "application/json"); @@ -224,97 +196,14 @@ static esp_err_t api_post_payment(httpd_req_t *req) return ESP_OK; } - cashu_proof_state_t *states = malloc(CASHU_MAX_PROOFS * sizeof(cashu_proof_state_t)); - if (!states) { - free(token); - cJSON *notice = create_notice("error", "session-error", "Out of memory"); - char *json = cJSON_PrintUnformatted(notice); - httpd_resp_set_status(req, "503 Service Unavailable"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, strlen(json)); - cJSON_free(json); - cJSON_Delete(notice); - return ESP_OK; - } - int state_count = 0; - err = cashu_check_proof_states(mint_url, token, states, &state_count); - ESP_LOGI(TAG, "Stack HWM after checkstate: %u", uxTaskGetStackHighWaterMark(NULL)); - if (err != ESP_OK) { - free(states); - free(token); - cJSON *notice = create_notice("error", "payment-error-verification", "Failed to verify token with mint"); - char *json = cJSON_PrintUnformatted(notice); - httpd_resp_set_status(req, "502 Bad Gateway"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, strlen(json)); - cJSON_free(json); - cJSON_Delete(notice); - return ESP_OK; - } - - for (int i = 0; i < state_count; i++) { - if (states[i].spent) { - free(states); - free(token); - cJSON *notice = create_notice("error", "payment-error-token-spent", "Token already spent"); - char *json = cJSON_PrintUnformatted(notice); - httpd_resp_set_status(req, "402 Payment Required"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, strlen(json)); - cJSON_free(json); - cJSON_Delete(notice); - return ESP_OK; - } - } - const tollgate_config_t *cfg = tollgate_config_get(); - bool is_bytes = (strcmp(cfg->metric, "bytes") == 0); - uint64_t step_size = is_bytes ? (uint64_t)cfg->step_size_bytes : (uint64_t)cfg->step_size_ms; - uint64_t allotment = cashu_calculate_allotment(token->total_amount, cfg->price_per_step, - cfg->metric, step_size); - if (allotment == 0) { - free(states); - free(token); - cJSON *notice = create_notice("error", "payment-error-insufficient", "Token value too low"); - char *json = cJSON_PrintUnformatted(notice); - httpd_resp_set_status(req, "402 Payment Required"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, strlen(json)); - cJSON_free(json); - cJSON_Delete(notice); - return ESP_OK; - } - - session_t *session; - if (is_bytes) { - session = session_create_bytes(client_ip, allotment); - } else { - session = session_create(client_ip, allotment); - } - if (!session) { - free(states); - free(token); - cJSON *notice = create_notice("error", "session-error", "Failed to create session"); - char *json = cJSON_PrintUnformatted(notice); - httpd_resp_set_status(req, "503 Service Unavailable"); - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, json, strlen(json)); - cJSON_free(json); - cJSON_Delete(notice); - return ESP_OK; - } - + uint64_t allotment = 0; cJSON *session_event = create_session_event(client_ip, allotment); char *json = cJSON_PrintUnformatted(session_event); httpd_resp_set_type(req, "application/json"); httpd_resp_send(req, json, strlen(json)); cJSON_free(json); cJSON_Delete(session_event); - - nucula_wallet_receive(body_copy); - - free(states); - free(token); return ESP_OK; } @@ -323,29 +212,15 @@ static esp_err_t api_get_usage(httpd_req_t *req) uint32_t client_ip = 0; get_client_ip(req, &client_ip); - session_t *session = session_find_by_ip(client_ip); - if (!session || !session->active) { - httpd_resp_set_type(req, "text/plain"); - httpd_resp_send(req, "-1/-1", 5); - return ESP_OK; - } - - const tollgate_config_t *cfg = tollgate_config_get(); - bool is_bytes = (strcmp(cfg->metric, "bytes") == 0); - - char resp[64]; - if (is_bytes) { - int64_t remaining = (int64_t)session->allotment_bytes - (int64_t)session->bytes_consumed; - if (remaining < 0) remaining = 0; - snprintf(resp, sizeof(resp), "%lld/%llu", (long long)remaining, (unsigned long long)session->allotment_bytes); + char *status_json = tollgate_core_get_status_json(); + if (status_json) { + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, status_json, strlen(status_json)); + cJSON_free(status_json); } else { - int64_t elapsed = (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS - session->start_time_ms; - int64_t remaining = session->allotment_ms - elapsed; - if (remaining < 0) remaining = 0; - snprintf(resp, sizeof(resp), "%lld/%llu", (long long)remaining, (unsigned long long)session->allotment_ms); + httpd_resp_set_type(req, "text/plain"); + httpd_resp_send(req, "{}", 2); } - httpd_resp_set_type(req, "text/plain"); - httpd_resp_send(req, resp, strlen(resp)); return ESP_OK; } @@ -354,15 +229,10 @@ static esp_err_t api_get_whoami(httpd_req_t *req) uint32_t client_ip = 0; char resp[96]; if (get_client_ip(req, &client_ip) == ESP_OK) { - char mac[18] = {0}; esp_ip4_addr_t ip = { .addr = client_ip }; - if (firewall_get_mac_for_ip(client_ip, mac, sizeof(mac)) == ESP_OK) { - snprintf(resp, sizeof(resp), "ip=" IPSTR " mac=%s", IP2STR(&ip), mac); - } else { - snprintf(resp, sizeof(resp), "ip=" IPSTR " mac=unknown", IP2STR(&ip)); - } + snprintf(resp, sizeof(resp), "ip=" IPSTR, IP2STR(&ip)); } else { - snprintf(resp, sizeof(resp), "ip=unknown mac=unknown"); + snprintf(resp, sizeof(resp), "ip=unknown"); } httpd_resp_set_type(req, "text/plain"); httpd_resp_send(req, resp, strlen(resp)); diff --git a/main/tollgate_main.c b/main/tollgate_main.c index c0ff65f..697dae3 100644 --- a/main/tollgate_main.c +++ b/main/tollgate_main.c @@ -13,10 +13,8 @@ #include "dhcpserver/dhcpserver.h" #include "config.h" #include "identity.h" -#include "dns_server.h" +#include "tollgate_core.h" #include "captive_portal.h" -#include "firewall.h" -#include "session.h" #include "tollgate_api.h" #include "nucula_wallet.h" #include "wifistr.h" @@ -73,11 +71,13 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, ESP_LOGI(TAG, "Station connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x", event->mac[0], event->mac[1], event->mac[2], event->mac[3], event->mac[4], event->mac[5]); + tollgate_core_client_connected(event->mac, 0); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) { wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data; ESP_LOGI(TAG, "Station disconnected: MAC=%02x:%02x:%02x:%02x:%02x:%02x", event->mac[0], event->mac[1], event->mac[2], event->mac[3], event->mac[4], event->mac[5]); + tollgate_core_client_disconnected(event->mac); } } @@ -115,13 +115,6 @@ static void ip_event_handler(void *arg, esp_event_base_t event_base, } } -static void wallet_init_task(void *pvParameters) -{ - const tollgate_config_t *cfg = tollgate_config_get(); - nucula_wallet_init(cfg->mint_url); - vTaskDelete(NULL); -} - static void publish_wifistr_task(void *pvParameters) { vTaskDelay(pdMS_TO_TICKS(5000)); @@ -139,7 +132,6 @@ static void start_services(void) return; } - esp_netif_get_ip_info(s_ap_netif, &(esp_netif_ip_info_t){0}); esp_netif_ip_info_t ap_ip_info; esp_netif_get_ip_info(s_ap_netif, &ap_ip_info); @@ -147,21 +139,20 @@ static void start_services(void) const ip_addr_t *dns_addr = dns_getserver(0); upstream_dns.addr = dns_addr->addr; - firewall_init(ap_ip_info.ip); - session_manager_init(); - const tollgate_config_t *cfg = tollgate_config_get(); + const tollgate_platform_t *platform = tollgate_get_platform(); + tollgate_core_init(platform, ap_ip_info.ip); + nucula_wallet_init(cfg->mint_url); lightning_payout_init(&cfg->payout); - dns_server_start(ap_ip_info.ip, upstream_dns); + tollgate_core_dns_start(upstream_dns); captive_portal_start(cfg->ap_ip_str); tollgate_api_start(); xTaskCreate(publish_wifistr_task, "wifistr_init", 16384, NULL, 3, NULL); - const tollgate_config_t *cfg2 = tollgate_config_get(); - if (cfg2->cvm_enabled) { + if (cfg->cvm_enabled) { cvm_server_init(); cvm_server_start(); } @@ -186,9 +177,8 @@ static void stop_services(void) captive_portal_stop(); tollgate_api_stop(); - dns_server_stop(); + tollgate_core_dns_stop(); cvm_server_stop(); - firewall_revoke_all(); s_services_running = false; if (s_services_mutex) xSemaphoreGive(s_services_mutex); ESP_LOGI(TAG, "=== TollGate services stopped ==="); @@ -316,7 +306,7 @@ void app_main(void) while (1) { vTaskDelay(pdMS_TO_TICKS(1000)); - session_tick(); + tollgate_core_tick(); tollgate_client_tick(); lightning_payout_tick(); } diff --git a/main/tollgate_platform.c b/main/tollgate_platform.c new file mode 100644 index 0000000..c992ea1 --- /dev/null +++ b/main/tollgate_platform.c @@ -0,0 +1,64 @@ +#include "tollgate_platform.h" +#include "tollgate_core.h" +#include "config.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "tollgate_platform"; + +static uint16_t platform_get_price_sats(void) +{ + const tollgate_config_t *cfg = tollgate_config_get(); + return cfg ? (uint16_t)cfg->price_per_step : 21; +} + +static int32_t platform_get_step_ms(void) +{ + const tollgate_config_t *cfg = tollgate_config_get(); + return cfg ? (int32_t)cfg->step_size_ms : 60000; +} + +static const char *platform_get_mint_url(void) +{ + const tollgate_config_t *cfg = tollgate_config_get(); + return cfg ? cfg->mint_url : "https://testnut.cashu.space"; +} + +static const char *platform_get_metric(void) +{ + const tollgate_config_t *cfg = tollgate_config_get(); + return cfg ? cfg->metric : "milliseconds"; +} + +static int32_t platform_get_step_bytes(void) +{ + const tollgate_config_t *cfg = tollgate_config_get(); + return cfg ? (int32_t)cfg->step_size_bytes : 22020096; +} + +static int64_t platform_get_time_ms(void) +{ + return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS; +} + +static bool platform_spend_proofs(const char *raw_token_json) +{ + (void)raw_token_json; + return true; +} + +const tollgate_platform_t *tollgate_get_platform(void) +{ + static const tollgate_platform_t platform = { + .get_price_sats = platform_get_price_sats, + .get_step_ms = platform_get_step_ms, + .get_mint_url = platform_get_mint_url, + .get_metric = platform_get_metric, + .get_step_bytes = platform_get_step_bytes, + .get_time_ms = platform_get_time_ms, + .spend_proofs = platform_spend_proofs, + }; + return &platform; +} -- cgit v1.2.3