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/tollgate_api.c | 156 +++++----------------------------------------------- 1 file changed, 13 insertions(+), 143 deletions(-) (limited to 'main/tollgate_api.c') 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)); -- cgit v1.2.3