diff options
| author | Your Name <you@example.com> | 2026-05-23 02:32:24 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-23 02:32:24 +0530 |
| commit | 6771956f1647f377007ab61a59b116de6d536706 (patch) | |
| tree | e8b6c74164040339134e33b8616c27c23ec1ade6 /components/tollgate_esp/src | |
| parent | 10c69b0ddc34a1fafada3f8b68a622b4aa2bea77 (diff) | |
feat: create tollgate_core standalone C library + tollgate_esp IDF component
Phase 1-4: Extract core business logic into reusable library
tollgate_core/ — standalone C library (zero ESP-IDF deps):
- tollgate_core.c — orchestrator (init, process_payment, process_share, tick)
- tollgate_cashu.c — V3 token decode, checkstate via platform callback, allotment math
- tollgate_session.c — session lifecycle state machine (create/find/revoke/extend)
- tollgate_firewall.c — client allowlist, NAT filter, packet filter
- tollgate_mining.c — hashprice, nbits→difficulty, shares-to-allotment, client stats
components/tollgate_esp/ — ESP-IDF component wrapper:
- tollgate_esp_platform.c — implements tollgate_platform_t using ESP-IDF APIs
- CMakeLists.txt — compiles tollgate_core + ESP wrapper, links to ESP-IDF
- idf_component.yml — for IDF Component Registry
Platform interface (tollgate_platform.h):
- Config access: price, step_size, mint_url, metric
- Time: get_time_ms (via FreeRTOS ticks on ESP)
- Logging: log_info/warn/error (via ESP_LOG on ESP)
- Wallet: receive/send/balance (via nucula on ESP)
- HTTP: http_post (via esp_http_client on ESP)
- WiFi/MAC: mac_for_ip, napt_enable
- Mining: stratum config, hashprice override
Build verification:
- tollgate_core compiles standalone: cmake + make (gcc, host)
- tollgate_esp + ESP-IDF full build: passes (idf.py build)
- Unit tests: 22/22 pass (make test-unit)
- No behavioral change to existing main/ code yet (Phase 5)
Diffstat (limited to 'components/tollgate_esp/src')
| -rw-r--r-- | components/tollgate_esp/src/tollgate_esp_platform.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/components/tollgate_esp/src/tollgate_esp_platform.c b/components/tollgate_esp/src/tollgate_esp_platform.c new file mode 100644 index 0000000..7d671f4 --- /dev/null +++ b/components/tollgate_esp/src/tollgate_esp_platform.c | |||
| @@ -0,0 +1,289 @@ | |||
| 1 | #include "tollgate_esp_platform.h" | ||
| 2 | #include "tollgate_core.h" | ||
| 3 | #include "config.h" | ||
| 4 | #include "nucula_wallet.h" | ||
| 5 | #include "esp_log.h" | ||
| 6 | #include "esp_http_client.h" | ||
| 7 | #include "esp_crt_bundle.h" | ||
| 8 | #include "freertos/FreeRTOS.h" | ||
| 9 | #include "freertos/task.h" | ||
| 10 | #include "esp_wifi.h" | ||
| 11 | #include "esp_wifi_ap_get_sta_list.h" | ||
| 12 | #include "lwip/etharp.h" | ||
| 13 | #include "lwip/netif.h" | ||
| 14 | #include "lwip/lwip_napt.h" | ||
| 15 | #include <string.h> | ||
| 16 | #include <stdarg.h> | ||
| 17 | |||
| 18 | static const char *TAG = "tg_esp"; | ||
| 19 | |||
| 20 | static void esp_log_info(const char *tag, const char *fmt, ...) | ||
| 21 | { | ||
| 22 | va_list args; | ||
| 23 | va_start(args, fmt); | ||
| 24 | esp_log_write(ESP_LOG_INFO, tag, fmt, args); | ||
| 25 | va_end(args); | ||
| 26 | } | ||
| 27 | |||
| 28 | static void esp_log_warn(const char *tag, const char *fmt, ...) | ||
| 29 | { | ||
| 30 | va_list args; | ||
| 31 | va_start(args, fmt); | ||
| 32 | esp_log_write(ESP_LOG_WARN, tag, fmt, args); | ||
| 33 | va_end(args); | ||
| 34 | } | ||
| 35 | |||
| 36 | static void esp_log_error(const char *tag, const char *fmt, ...) | ||
| 37 | { | ||
| 38 | va_list args; | ||
| 39 | va_start(args, fmt); | ||
| 40 | esp_log_write(ESP_LOG_ERROR, tag, fmt, args); | ||
| 41 | va_end(args); | ||
| 42 | } | ||
| 43 | |||
| 44 | static uint16_t esp_get_price_sats(void) | ||
| 45 | { | ||
| 46 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 47 | return cfg ? (uint16_t)cfg->price_per_step : 21; | ||
| 48 | } | ||
| 49 | |||
| 50 | static int32_t esp_get_step_ms(void) | ||
| 51 | { | ||
| 52 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 53 | return cfg ? (int32_t)cfg->step_size_ms : 60000; | ||
| 54 | } | ||
| 55 | |||
| 56 | static int64_t esp_get_step_bytes(void) | ||
| 57 | { | ||
| 58 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 59 | return cfg ? (int64_t)cfg->step_size_bytes : 22020096; | ||
| 60 | } | ||
| 61 | |||
| 62 | static const char *esp_get_mint_url(void) | ||
| 63 | { | ||
| 64 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 65 | return cfg ? cfg->mint_url : NULL; | ||
| 66 | } | ||
| 67 | |||
| 68 | static const char *esp_get_metric(void) | ||
| 69 | { | ||
| 70 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 71 | return cfg ? cfg->metric : "milliseconds"; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int64_t esp_get_time_ms(void) | ||
| 75 | { | ||
| 76 | return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS; | ||
| 77 | } | ||
| 78 | |||
| 79 | static bool esp_wallet_receive(const char *token) | ||
| 80 | { | ||
| 81 | return nucula_wallet_receive(token) == ESP_OK; | ||
| 82 | } | ||
| 83 | |||
| 84 | static bool esp_wallet_send(uint64_t amount, char *buf, size_t buf_len) | ||
| 85 | { | ||
| 86 | return nucula_wallet_send(amount, buf, buf_len) == ESP_OK; | ||
| 87 | } | ||
| 88 | |||
| 89 | static uint64_t esp_wallet_balance(void) | ||
| 90 | { | ||
| 91 | return nucula_wallet_balance(); | ||
| 92 | } | ||
| 93 | |||
| 94 | static int esp_http_post(const char *url, const char *headers, | ||
| 95 | const char *body, int body_len, | ||
| 96 | char *resp, int resp_len) | ||
| 97 | { | ||
| 98 | esp_http_client_config_t config = { | ||
| 99 | .url = url, | ||
| 100 | .method = HTTP_METHOD_POST, | ||
| 101 | .timeout_ms = 15000, | ||
| 102 | .crt_bundle_attach = esp_crt_bundle_attach, | ||
| 103 | }; | ||
| 104 | esp_http_client_handle_t client = esp_http_client_init(&config); | ||
| 105 | if (!client) return -1; | ||
| 106 | |||
| 107 | if (headers) { | ||
| 108 | const char *h = headers; | ||
| 109 | while (*h) { | ||
| 110 | const char *colon = strchr(h, ':'); | ||
| 111 | if (!colon) break; | ||
| 112 | char key[64] = {0}; | ||
| 113 | size_t klen = colon - h; | ||
| 114 | if (klen >= sizeof(key)) klen = sizeof(key) - 1; | ||
| 115 | memcpy(key, h, klen); | ||
| 116 | esp_http_client_set_header(client, key, colon + 1); | ||
| 117 | const char *next = strchr(colon, '\n'); | ||
| 118 | if (!next) break; | ||
| 119 | h = next + 1; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | if (headers && headers[0]) { | ||
| 123 | esp_http_client_set_header(client, "Content-Type", "application/json"); | ||
| 124 | } | ||
| 125 | |||
| 126 | esp_err_t err = esp_http_client_open(client, body_len); | ||
| 127 | if (err != ESP_OK) { | ||
| 128 | esp_http_client_cleanup(client); | ||
| 129 | return -1; | ||
| 130 | } | ||
| 131 | int written = esp_http_client_write(client, body, body_len); | ||
| 132 | (void)written; | ||
| 133 | |||
| 134 | esp_http_client_fetch_headers(client); | ||
| 135 | int status = esp_http_client_get_status_code(client); | ||
| 136 | int rd = esp_http_client_read(client, resp, resp_len); | ||
| 137 | esp_http_client_cleanup(client); | ||
| 138 | |||
| 139 | if (status != 200 || rd <= 0) return -1; | ||
| 140 | return rd; | ||
| 141 | } | ||
| 142 | |||
| 143 | static bool esp_create_task(void (*fn)(void*), void *arg, | ||
| 144 | const char *name, int stack_bytes, int priority) | ||
| 145 | { | ||
| 146 | return xTaskCreate(fn, name ? name : "tg_task", | ||
| 147 | stack_bytes / sizeof(StackType_t), | ||
| 148 | arg, priority, NULL) == pdPASS; | ||
| 149 | } | ||
| 150 | |||
| 151 | static bool esp_mac_for_ip(uint32_t ip, char *mac_out, int mac_out_size) | ||
| 152 | { | ||
| 153 | wifi_sta_list_t sta_list; | ||
| 154 | if (esp_wifi_ap_get_sta_list(&sta_list) == ESP_OK) { | ||
| 155 | wifi_sta_mac_ip_list_t ip_mac_list; | ||
| 156 | if (esp_wifi_ap_get_sta_list_with_ip(&sta_list, &ip_mac_list) == ESP_OK) { | ||
| 157 | for (int i = 0; i < ip_mac_list.num; i++) { | ||
| 158 | if (ip_mac_list.sta[i].ip.addr == ip) { | ||
| 159 | snprintf(mac_out, mac_out_size, "%02x:%02x:%02x:%02x:%02x:%02x", | ||
| 160 | ip_mac_list.sta[i].mac[0], ip_mac_list.sta[i].mac[1], | ||
| 161 | ip_mac_list.sta[i].mac[2], ip_mac_list.sta[i].mac[3], | ||
| 162 | ip_mac_list.sta[i].mac[4], ip_mac_list.sta[i].mac[5]); | ||
| 163 | return true; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | ip4_addr_t *entry_ip = NULL; | ||
| 170 | struct netif *entry_netif = NULL; | ||
| 171 | struct eth_addr *entry_eth = NULL; | ||
| 172 | ssize_t i = 0; | ||
| 173 | while (etharp_get_entry(i, &entry_ip, &entry_netif, &entry_eth) == ERR_OK) { | ||
| 174 | if (entry_ip && entry_ip->addr == ip && entry_eth) { | ||
| 175 | snprintf(mac_out, mac_out_size, "%02x:%02x:%02x:%02x:%02x:%02x", | ||
| 176 | entry_eth->addr[0], entry_eth->addr[1], entry_eth->addr[2], | ||
| 177 | entry_eth->addr[3], entry_eth->addr[4], entry_eth->addr[5]); | ||
| 178 | return true; | ||
| 179 | } | ||
| 180 | i++; | ||
| 181 | } | ||
| 182 | return false; | ||
| 183 | } | ||
| 184 | |||
| 185 | static void esp_napt_enable(uint32_t ip, bool enable) | ||
| 186 | { | ||
| 187 | ip_napt_enable(ip, enable ? 1 : 0); | ||
| 188 | } | ||
| 189 | |||
| 190 | static bool esp_mining_enabled(void) | ||
| 191 | { | ||
| 192 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 193 | return cfg ? cfg->mining_enabled : false; | ||
| 194 | } | ||
| 195 | |||
| 196 | static const char *esp_get_stratum_host(void) | ||
| 197 | { | ||
| 198 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 199 | return cfg ? cfg->stratum_host : NULL; | ||
| 200 | } | ||
| 201 | |||
| 202 | static uint16_t esp_get_stratum_port(void) | ||
| 203 | { | ||
| 204 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 205 | return cfg ? cfg->stratum_port : 3333; | ||
| 206 | } | ||
| 207 | |||
| 208 | static const char *esp_get_stratum_user(void) | ||
| 209 | { | ||
| 210 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 211 | return cfg ? cfg->stratum_user : NULL; | ||
| 212 | } | ||
| 213 | |||
| 214 | static const char *esp_get_stratum_pass(void) | ||
| 215 | { | ||
| 216 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 217 | return cfg ? cfg->stratum_pass : NULL; | ||
| 218 | } | ||
| 219 | |||
| 220 | static uint16_t esp_get_mining_port(void) | ||
| 221 | { | ||
| 222 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 223 | return cfg ? cfg->mining_port : 3334; | ||
| 224 | } | ||
| 225 | |||
| 226 | static uint64_t esp_get_hashprice_override(void) | ||
| 227 | { | ||
| 228 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 229 | return cfg ? cfg->hashprice_sats_per_ghs_day : 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | static void tg_esp_fill_random(void *buf, int len) | ||
| 233 | { | ||
| 234 | esp_fill_random(buf, (size_t)len); | ||
| 235 | } | ||
| 236 | |||
| 237 | static int esp_get_accepted_mint_count(void) | ||
| 238 | { | ||
| 239 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 240 | return cfg ? cfg->accepted_mint_count : 0; | ||
| 241 | } | ||
| 242 | |||
| 243 | static const char *esp_get_accepted_mint(int index) | ||
| 244 | { | ||
| 245 | const tollgate_config_t *cfg = tollgate_config_get(); | ||
| 246 | if (!cfg || index < 0 || index >= cfg->accepted_mint_count) return NULL; | ||
| 247 | return cfg->accepted_mints[index]; | ||
| 248 | } | ||
| 249 | |||
| 250 | static bool esp_is_mint_reachable(const char *mint_url) | ||
| 251 | { | ||
| 252 | (void)mint_url; | ||
| 253 | return true; | ||
| 254 | } | ||
| 255 | |||
| 256 | static const tollgate_platform_t s_platform = { | ||
| 257 | .get_price_sats = esp_get_price_sats, | ||
| 258 | .get_step_ms = esp_get_step_ms, | ||
| 259 | .get_step_bytes = esp_get_step_bytes, | ||
| 260 | .get_mint_url = esp_get_mint_url, | ||
| 261 | .get_metric = esp_get_metric, | ||
| 262 | .get_time_ms = esp_get_time_ms, | ||
| 263 | .log_info = esp_log_info, | ||
| 264 | .log_warn = esp_log_warn, | ||
| 265 | .log_error = esp_log_error, | ||
| 266 | .wallet_receive = esp_wallet_receive, | ||
| 267 | .wallet_send = esp_wallet_send, | ||
| 268 | .wallet_balance = esp_wallet_balance, | ||
| 269 | .http_post = esp_http_post, | ||
| 270 | .create_task = esp_create_task, | ||
| 271 | .mac_for_ip = esp_mac_for_ip, | ||
| 272 | .napt_enable = esp_napt_enable, | ||
| 273 | .mining_enabled = esp_mining_enabled, | ||
| 274 | .get_stratum_host = esp_get_stratum_host, | ||
| 275 | .get_stratum_port = esp_get_stratum_port, | ||
| 276 | .get_stratum_user = esp_get_stratum_user, | ||
| 277 | .get_stratum_pass = esp_get_stratum_pass, | ||
| 278 | .get_mining_port = esp_get_mining_port, | ||
| 279 | .get_hashprice_override = esp_get_hashprice_override, | ||
| 280 | .fill_random = tg_esp_fill_random, | ||
| 281 | .get_accepted_mint_count = esp_get_accepted_mint_count, | ||
| 282 | .get_accepted_mint = esp_get_accepted_mint, | ||
| 283 | .is_mint_reachable = esp_is_mint_reachable, | ||
| 284 | }; | ||
| 285 | |||
| 286 | const tollgate_platform_t *tollgate_esp_get_platform(void) | ||
| 287 | { | ||
| 288 | return &s_platform; | ||
| 289 | } | ||