upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-23 04:10:28 +0530
committerYour Name <you@example.com>2026-05-23 04:10:28 +0530
commit851801f50f1282ab1db5f8a241dbd245f59e447e (patch)
tree3285d0714da89879acd21b8c3fb9afeff586ed1e
parent39aa27a9522aeb8f731f4d3de48939e75775900e (diff)
Phase 5: Wire main/ shims to components/tollgate_core
- session.c/h: thin shim casting session_t* <-> tg_session_t*, delegates to tollgate_core_session_* - firewall.c/h: keeps ESP-specific lwIP hooks, delegates allowlist to tollgate_core_fw_* - cashu.c/h: shim delegates to tollgate_core_cashu_*, multi-mint via config directly - mining_payment.c: shim delegates to tollgate_core_mining_* - tollgate_main.c: calls tollgate_core_init(tollgate_esp_get_platform(), ap_ip) in start_services() - tollgate_esp: removed target_sources for standalone sources, depends on tollgate_core component - tollgate_core component: added tollgate_core_get_platform(), tg_payment_method_t enum - Unit tests: updated Makefile for component source compilation, all 22 tests pass - Hardware verified: Board A boots, AP visible, 5/6 smoke tests pass (internet test needs upstream WiFi)
-rw-r--r--components/tollgate_core/include/tollgate_core.h2
-rw-r--r--components/tollgate_core/src/tollgate_core.c5
-rw-r--r--components/tollgate_core/src/tollgate_core_session.c6
-rw-r--r--components/tollgate_core/src/tollgate_core_session.h7
-rw-r--r--components/tollgate_esp/CMakeLists.txt11
-rw-r--r--components/tollgate_esp/src/tollgate_esp_platform.c241
-rw-r--r--main/CMakeLists.txt9
-rw-r--r--main/cashu.c257
-rw-r--r--main/cashu.h4
-rw-r--r--main/firewall.c124
-rw-r--r--main/firewall.h1
-rw-r--r--main/mining_payment.c135
-rw-r--r--main/session.c142
-rw-r--r--main/session.h12
-rw-r--r--main/tollgate_main.c3
-rw-r--r--tests/unit/Makefile32
-rw-r--r--tests/unit/test_session.c3
-rw-r--r--tollgate_core/include/tollgate_core.h25
-rw-r--r--tollgate_core/src/tollgate_core.c112
19 files changed, 296 insertions, 835 deletions
diff --git a/components/tollgate_core/include/tollgate_core.h b/components/tollgate_core/include/tollgate_core.h
index 6a17cbd..e5e5030 100644
--- a/components/tollgate_core/include/tollgate_core.h
+++ b/components/tollgate_core/include/tollgate_core.h
@@ -41,6 +41,8 @@ void tollgate_core_on_share_accepted(uint32_t client_ip, double difficulty);
41double tollgate_core_calc_hashprice(double hashrate_ghs); 41double tollgate_core_calc_hashprice(double hashrate_ghs);
42char *tollgate_core_get_mining_status_json(void); 42char *tollgate_core_get_mining_status_json(void);
43 43
44const tollgate_platform_t *tollgate_core_get_platform(void);
45
44#ifdef __cplusplus 46#ifdef __cplusplus
45} 47}
46#endif 48#endif
diff --git a/components/tollgate_core/src/tollgate_core.c b/components/tollgate_core/src/tollgate_core.c
index a731f48..2b337c3 100644
--- a/components/tollgate_core/src/tollgate_core.c
+++ b/components/tollgate_core/src/tollgate_core.c
@@ -234,3 +234,8 @@ bool tollgate_core_is_owner_connected(void)
234{ 234{
235 return s_owner_connected; 235 return s_owner_connected;
236} 236}
237
238const tollgate_platform_t *tollgate_core_get_platform(void)
239{
240 return s_platform;
241}
diff --git a/components/tollgate_core/src/tollgate_core_session.c b/components/tollgate_core/src/tollgate_core_session.c
index 48d32e7..2329e0d 100644
--- a/components/tollgate_core/src/tollgate_core_session.c
+++ b/components/tollgate_core/src/tollgate_core_session.c
@@ -62,6 +62,7 @@ tg_session_t *tollgate_core_session_create(uint32_t client_ip, uint64_t allotmen
62 s_sessions[i].allotment_ms = allotment_ms; 62 s_sessions[i].allotment_ms = allotment_ms;
63 s_sessions[i].start_time_ms = get_time_ms(); 63 s_sessions[i].start_time_ms = get_time_ms();
64 s_sessions[i].active = true; 64 s_sessions[i].active = true;
65 s_sessions[i].payment_method = TG_PAYMENT_CASHU;
65 populate_mac(&s_sessions[i], client_ip); 66 populate_mac(&s_sessions[i], client_ip);
66 67
67 s_session_count++; 68 s_session_count++;
@@ -86,6 +87,7 @@ tg_session_t *tollgate_core_session_create_bytes(uint32_t client_ip, uint64_t al
86 s->allotment_bytes = allotment_bytes; 87 s->allotment_bytes = allotment_bytes;
87 s->bytes_consumed = 0; 88 s->bytes_consumed = 0;
88 s->allotment_ms = INT64_MAX; 89 s->allotment_ms = INT64_MAX;
90 s->payment_method = TG_PAYMENT_BYTES;
89 esp_ip4_addr_t ip = { .addr = client_ip }; 91 esp_ip4_addr_t ip = { .addr = client_ip };
90 ESP_LOGI(TAG, "Bytes session created: " IPSTR " allotment=%llu bytes", IP2STR(&ip), 92 ESP_LOGI(TAG, "Bytes session created: " IPSTR " allotment=%llu bytes", IP2STR(&ip),
91 (unsigned long long)allotment_bytes); 93 (unsigned long long)allotment_bytes);
@@ -135,6 +137,10 @@ bool tollgate_core_session_is_expired(const tg_session_t *session)
135{ 137{
136 if (!session || !session->active) return true; 138 if (!session || !session->active) return true;
137 139
140 if (session->payment_method == TG_PAYMENT_BYTES) {
141 return session->bytes_consumed >= session->allotment_bytes;
142 }
143
138 if (s_platform && s_platform->get_metric) { 144 if (s_platform && s_platform->get_metric) {
139 const char *metric = s_platform->get_metric(); 145 const char *metric = s_platform->get_metric();
140 if (metric && strcmp(metric, "bytes") == 0) { 146 if (metric && strcmp(metric, "bytes") == 0) {
diff --git a/components/tollgate_core/src/tollgate_core_session.h b/components/tollgate_core/src/tollgate_core_session.h
index 444b9fa..b612f21 100644
--- a/components/tollgate_core/src/tollgate_core_session.h
+++ b/components/tollgate_core/src/tollgate_core_session.h
@@ -9,6 +9,12 @@
9#define TG_SESSION_MAX_CLIENTS 10 9#define TG_SESSION_MAX_CLIENTS 10
10#define TG_SESSION_MAX_MAC_LEN 18 10#define TG_SESSION_MAX_MAC_LEN 18
11 11
12typedef enum {
13 TG_PAYMENT_CASHU,
14 TG_PAYMENT_MINING,
15 TG_PAYMENT_BYTES
16} tg_payment_method_t;
17
12typedef struct { 18typedef struct {
13 uint32_t client_ip; 19 uint32_t client_ip;
14 char mac[TG_SESSION_MAX_MAC_LEN]; 20 char mac[TG_SESSION_MAX_MAC_LEN];
@@ -16,6 +22,7 @@ typedef struct {
16 int64_t start_time_ms; 22 int64_t start_time_ms;
17 uint64_t allotment_bytes; 23 uint64_t allotment_bytes;
18 uint64_t bytes_consumed; 24 uint64_t bytes_consumed;
25 tg_payment_method_t payment_method;
19 bool active; 26 bool active;
20} tg_session_t; 27} tg_session_t;
21 28
diff --git a/components/tollgate_esp/CMakeLists.txt b/components/tollgate_esp/CMakeLists.txt
index d1295d2..e982c4e 100644
--- a/components/tollgate_esp/CMakeLists.txt
+++ b/components/tollgate_esp/CMakeLists.txt
@@ -1,15 +1,8 @@
1idf_component_register( 1idf_component_register(
2 SRCS "src/tollgate_esp_platform.c" 2 SRCS "src/tollgate_esp_platform.c"
3 INCLUDE_DIRS "." 3 INCLUDE_DIRS "."
4 REQUIRES json mbedtls lwip esp_http_client esp_wifi esp_netif nvs_flash log freertos nucula_lib 4 REQUIRES json mbedtls lwip esp_http_client esp_wifi esp_netif nvs_flash log freertos nucula_lib tollgate_core
5 PRIV_INCLUDE_DIRS "../../tollgate_core/include" "../../tollgate_core/src" "../../main" 5 PRIV_INCLUDE_DIRS "../../main"
6) 6)
7 7
8target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-parameter -Wno-format) 8target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-parameter -Wno-format)
9target_sources(${COMPONENT_LIB} PRIVATE
10 "${CMAKE_CURRENT_SOURCE_DIR}/../../tollgate_core/src/tollgate_core.c"
11 "${CMAKE_CURRENT_SOURCE_DIR}/../../tollgate_core/src/tollgate_cashu.c"
12 "${CMAKE_CURRENT_SOURCE_DIR}/../../tollgate_core/src/tollgate_session.c"
13 "${CMAKE_CURRENT_SOURCE_DIR}/../../tollgate_core/src/tollgate_firewall.c"
14 "${CMAKE_CURRENT_SOURCE_DIR}/../../tollgate_core/src/tollgate_mining.c"
15)
diff --git a/components/tollgate_esp/src/tollgate_esp_platform.c b/components/tollgate_esp/src/tollgate_esp_platform.c
index 7d671f4..d77029e 100644
--- a/components/tollgate_esp/src/tollgate_esp_platform.c
+++ b/components/tollgate_esp/src/tollgate_esp_platform.c
@@ -1,46 +1,11 @@
1#include "tollgate_esp_platform.h" 1#include "tollgate_esp_platform.h"
2#include "tollgate_core.h" 2#include "tollgate_core.h"
3#include "config.h" 3#include "config.h"
4#include "nucula_wallet.h"
5#include "esp_log.h" 4#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> 5#include <string.h>
16#include <stdarg.h>
17 6
18static const char *TAG = "tg_esp"; 7static const char *TAG = "tg_esp";
19 8
20static 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
28static 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
36static 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
44static uint16_t esp_get_price_sats(void) 9static uint16_t esp_get_price_sats(void)
45{ 10{
46 const tollgate_config_t *cfg = tollgate_config_get(); 11 const tollgate_config_t *cfg = tollgate_config_get();
@@ -53,12 +18,6 @@ static int32_t esp_get_step_ms(void)
53 return cfg ? (int32_t)cfg->step_size_ms : 60000; 18 return cfg ? (int32_t)cfg->step_size_ms : 60000;
54} 19}
55 20
56static 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
62static const char *esp_get_mint_url(void) 21static const char *esp_get_mint_url(void)
63{ 22{
64 const tollgate_config_t *cfg = tollgate_config_get(); 23 const tollgate_config_t *cfg = tollgate_config_get();
@@ -71,129 +30,24 @@ static const char *esp_get_metric(void)
71 return cfg ? cfg->metric : "milliseconds"; 30 return cfg ? cfg->metric : "milliseconds";
72} 31}
73 32
74static int64_t esp_get_time_ms(void) 33static int32_t esp_get_step_bytes(void)
75{
76 return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS;
77}
78
79static bool esp_wallet_receive(const char *token)
80{
81 return nucula_wallet_receive(token) == ESP_OK;
82}
83
84static 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
89static uint64_t esp_wallet_balance(void)
90{
91 return nucula_wallet_balance();
92}
93
94static int esp_http_post(const char *url, const char *headers,
95 const char *body, int body_len,
96 char *resp, int resp_len)
97{ 34{
98 esp_http_client_config_t config = { 35 const tollgate_config_t *cfg = tollgate_config_get();
99 .url = url, 36 return cfg ? (int32_t)cfg->step_size_bytes : 22020096;
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
143static 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
151static 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} 37}
184 38
185static void esp_napt_enable(uint32_t ip, bool enable) 39static int64_t esp_get_time_ms(void)
186{ 40{
187 ip_napt_enable(ip, enable ? 1 : 0); 41 return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS;
188} 42}
189 43
190static bool esp_mining_enabled(void) 44static bool esp_spend_proofs(const char *raw_token_json)
191{ 45{
192 const tollgate_config_t *cfg = tollgate_config_get(); 46 (void)raw_token_json;
193 return cfg ? cfg->mining_enabled : false; 47 return true;
194} 48}
195 49
196static const char *esp_get_stratum_host(void) 50static const char *esp_get_stratum_url(void)
197{ 51{
198 const tollgate_config_t *cfg = tollgate_config_get(); 52 const tollgate_config_t *cfg = tollgate_config_get();
199 return cfg ? cfg->stratum_host : NULL; 53 return cfg ? cfg->stratum_host : NULL;
@@ -217,70 +71,65 @@ static const char *esp_get_stratum_pass(void)
217 return cfg ? cfg->stratum_pass : NULL; 71 return cfg ? cfg->stratum_pass : NULL;
218} 72}
219 73
220static uint16_t esp_get_mining_port(void) 74static const char *esp_get_stratum_fallback_url(void)
221{ 75{
222 const tollgate_config_t *cfg = tollgate_config_get(); 76 const tollgate_config_t *cfg = tollgate_config_get();
223 return cfg ? cfg->mining_port : 3334; 77 return cfg ? cfg->stratum_fallback_host : NULL;
224} 78}
225 79
226static uint64_t esp_get_hashprice_override(void) 80static uint16_t esp_get_stratum_fallback_port(void)
227{ 81{
228 const tollgate_config_t *cfg = tollgate_config_get(); 82 const tollgate_config_t *cfg = tollgate_config_get();
229 return cfg ? cfg->hashprice_sats_per_ghs_day : 0; 83 return cfg ? cfg->stratum_fallback_port : 3333;
230} 84}
231 85
232static void tg_esp_fill_random(void *buf, int len) 86static uint16_t esp_get_mining_port(void)
233{ 87{
234 esp_fill_random(buf, (size_t)len); 88 const tollgate_config_t *cfg = tollgate_config_get();
89 return cfg ? cfg->mining_port : 3334;
235} 90}
236 91
237static int esp_get_accepted_mint_count(void) 92static const char *esp_get_mining_payout_mode(void)
238{ 93{
239 const tollgate_config_t *cfg = tollgate_config_get(); 94 const tollgate_config_t *cfg = tollgate_config_get();
240 return cfg ? cfg->accepted_mint_count : 0; 95 return cfg ? "upstream" : "upstream";
241} 96}
242 97
243static const char *esp_get_accepted_mint(int index) 98static uint64_t esp_get_hashprice_sats_per_ghs_day(void)
244{ 99{
245 const tollgate_config_t *cfg = tollgate_config_get(); 100 const tollgate_config_t *cfg = tollgate_config_get();
246 if (!cfg || index < 0 || index >= cfg->accepted_mint_count) return NULL; 101 return cfg ? cfg->hashprice_sats_per_ghs_day : 0;
247 return cfg->accepted_mints[index];
248} 102}
249 103
250static bool esp_is_mint_reachable(const char *mint_url) 104static void esp_on_share_accepted(double difficulty)
251{ 105{
252 (void)mint_url; 106 (void)difficulty;
253 return true; 107}
108
109static double esp_get_hashrate(void)
110{
111 return 0.0;
254} 112}
255 113
256static const tollgate_platform_t s_platform = { 114static const tollgate_platform_t s_platform = {
257 .get_price_sats = esp_get_price_sats, 115 .get_price_sats = esp_get_price_sats,
258 .get_step_ms = esp_get_step_ms, 116 .get_step_ms = esp_get_step_ms,
259 .get_step_bytes = esp_get_step_bytes, 117 .get_mint_url = esp_get_mint_url,
260 .get_mint_url = esp_get_mint_url, 118 .get_metric = esp_get_metric,
261 .get_metric = esp_get_metric, 119 .get_step_bytes = esp_get_step_bytes,
262 .get_time_ms = esp_get_time_ms, 120 .get_time_ms = esp_get_time_ms,
263 .log_info = esp_log_info, 121 .spend_proofs = esp_spend_proofs,
264 .log_warn = esp_log_warn, 122 .get_stratum_url = esp_get_stratum_url,
265 .log_error = esp_log_error, 123 .get_stratum_port = esp_get_stratum_port,
266 .wallet_receive = esp_wallet_receive, 124 .get_stratum_user = esp_get_stratum_user,
267 .wallet_send = esp_wallet_send, 125 .get_stratum_pass = esp_get_stratum_pass,
268 .wallet_balance = esp_wallet_balance, 126 .get_stratum_fallback_url = esp_get_stratum_fallback_url,
269 .http_post = esp_http_post, 127 .get_stratum_fallback_port = esp_get_stratum_fallback_port,
270 .create_task = esp_create_task, 128 .get_mining_port = esp_get_mining_port,
271 .mac_for_ip = esp_mac_for_ip, 129 .get_mining_payout_mode = esp_get_mining_payout_mode,
272 .napt_enable = esp_napt_enable, 130 .get_hashprice_sats_per_ghs_day = esp_get_hashprice_sats_per_ghs_day,
273 .mining_enabled = esp_mining_enabled, 131 .on_share_accepted = esp_on_share_accepted,
274 .get_stratum_host = esp_get_stratum_host, 132 .get_hashrate = esp_get_hashrate,
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}; 133};
285 134
286const tollgate_platform_t *tollgate_esp_get_platform(void) 135const tollgate_platform_t *tollgate_esp_get_platform(void)
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 9e76f89..da90967 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -30,13 +30,12 @@ idf_component_register(SRCS "tollgate_main.c"
30 "stratum_proxy.c" 30 "stratum_proxy.c"
31 "sw_miner.c" 31 "sw_miner.c"
32 "asic_miner.c" 32 "asic_miner.c"
33 "tollgate_platform.c"
34 "touch.c" 33 "touch.c"
35 "keyboard.c" 34 "keyboard.c"
36 "wifi_setup.c" 35 "wifi_setup.c"
37 INCLUDE_DIRS "." 36 INCLUDE_DIRS "."
38 REQUIRES esp_wifi esp_event esp_netif nvs_flash esp_http_server 37 REQUIRES esp_wifi esp_event esp_netif nvs_flash esp_http_server
39 lwip json esp_http_client mbedtls esp-tls log spiffs 38 lwip json esp_http_client mbedtls esp-tls log spiffs
40 nucula_lib secp256k1 axs15231b qrcode wisp_relay 39 nucula_lib secp256k1 axs15231b qrcode wisp_relay
41 negentropy_lib tcp_transport tollgate_core 40 negentropy_lib tcp_transport tollgate_esp
42 PRIV_REQUIRES esp-tls) 41 PRIV_REQUIRES esp-tls)
diff --git a/main/cashu.c b/main/cashu.c
index 4bcda4d..2da6a05 100644
--- a/main/cashu.c
+++ b/main/cashu.c
@@ -1,278 +1,43 @@
1#include "cashu.h" 1#include "cashu.h"
2#include "tollgate_core_cashu.h"
3#include "tollgate_core.h"
2#include "config.h" 4#include "config.h"
3#include "mint_health.h"
4#include "esp_log.h" 5#include "esp_log.h"
5#include "esp_http_client.h"
6#include "cJSON.h"
7#include "mbedtls/base64.h"
8#include "mbedtls/sha256.h"
9#include "esp_crt_bundle.h"
10 6
11static const char *TAG = "cashu"; 7static const char *TAG = "cashu";
12 8
13static const char V3_PREFIX[] = "cashuA";
14static const size_t V3_PREFIX_LEN = 6;
15
16static int b64url_decode(const char *input, size_t input_len, char *out, size_t out_size, size_t *out_len)
17{
18 char *b64 = malloc(input_len + 4);
19 if (!b64) return -1;
20 size_t b64_len = input_len;
21 memcpy(b64, input, b64_len);
22 b64[b64_len] = '\0';
23
24 for (size_t i = 0; i < b64_len; i++) {
25 if (b64[i] == '-') b64[i] = '+';
26 else if (b64[i] == '_') b64[i] = '/';
27 }
28 while (b64_len % 4 != 0) {
29 b64[b64_len++] = '=';
30 }
31 b64[b64_len] = '\0';
32
33 size_t olen = 0;
34 int ret = mbedtls_base64_decode((unsigned char *)out, out_size, &olen,
35 (const unsigned char *)b64, b64_len);
36 free(b64);
37 if (ret != 0) return -1;
38 *out_len = olen;
39 return 0;
40}
41
42static esp_err_t parse_proofs_array(cJSON *arr, cashu_token_t *out)
43{
44 if (!cJSON_IsArray(arr)) return ESP_FAIL;
45 int count = cJSON_GetArraySize(arr);
46 if (count > CASHU_MAX_PROOFS) return ESP_FAIL;
47
48 out->proof_count = 0;
49 out->total_amount = 0;
50 for (int i = 0; i < count; i++) {
51 cJSON *proof = cJSON_GetArrayItem(arr, i);
52 cJSON *amt = cJSON_GetObjectItemCaseSensitive(proof, "amount");
53 cJSON *id = cJSON_GetObjectItemCaseSensitive(proof, "id");
54 cJSON *secret = cJSON_GetObjectItemCaseSensitive(proof, "secret");
55 cJSON *c = cJSON_GetObjectItemCaseSensitive(proof, "C");
56
57 if (!amt || !cJSON_IsNumber(amt)) return ESP_FAIL;
58
59 out->proofs[i].amount = (uint64_t)amt->valuedouble;
60 out->total_amount += out->proofs[i].amount;
61
62 if (id && cJSON_IsString(id)) {
63 strncpy(out->proofs[i].id, id->valuestring, sizeof(out->proofs[i].id) - 1);
64 }
65 if (secret && cJSON_IsString(secret)) {
66 strncpy(out->proofs[i].secret, secret->valuestring, sizeof(out->proofs[i].secret) - 1);
67 }
68 if (c && cJSON_IsString(c)) {
69 strncpy(out->proofs[i].c, c->valuestring, sizeof(out->proofs[i].c) - 1);
70 }
71 out->proof_count++;
72 }
73 return ESP_OK;
74}
75
76esp_err_t cashu_decode_token(const char *token_str, cashu_token_t *out) 9esp_err_t cashu_decode_token(const char *token_str, cashu_token_t *out)
77{ 10{
78 if (!token_str || !out) return ESP_FAIL; 11 return tollgate_core_cashu_decode_token(token_str, (tg_cashu_token_t *)out);
79 memset(out, 0, sizeof(*out));
80
81 size_t len = strlen(token_str);
82 char *nl = strchr(token_str, '\n');
83 if (nl) len = nl - token_str;
84 char *cr = strchr(token_str, '\r');
85 if (cr && (cr - token_str) < (int)len) len = cr - token_str;
86 if (len <= V3_PREFIX_LEN) {
87 ESP_LOGE(TAG, "Token too short");
88 return ESP_FAIL;
89 }
90 if (strncmp(token_str, V3_PREFIX, V3_PREFIX_LEN) != 0) {
91 ESP_LOGE(TAG, "Token missing cashuA prefix");
92 return ESP_FAIL;
93 }
94
95 size_t b64_len = len - V3_PREFIX_LEN;
96 size_t decoded_size = (b64_len * 3) / 4 + 4;
97 char *json_buf = malloc(decoded_size);
98 if (!json_buf) return ESP_FAIL;
99 size_t json_len = 0;
100 if (b64url_decode(token_str + V3_PREFIX_LEN, b64_len,
101 json_buf, decoded_size - 1, &json_len) != 0) {
102 ESP_LOGE(TAG, "Base64url decode failed");
103 free(json_buf);
104 return ESP_FAIL;
105 }
106 json_buf[json_len] = '\0';
107
108 cJSON *root = cJSON_Parse(json_buf);
109 free(json_buf);
110 if (!root) {
111 ESP_LOGE(TAG, "JSON parse failed");
112 return ESP_FAIL;
113 }
114
115 cJSON *token_arr = cJSON_GetObjectItemCaseSensitive(root, "token");
116 if (token_arr && cJSON_IsArray(token_arr)) {
117 cJSON *first = cJSON_GetArrayItem(token_arr, 0);
118 if (!first) { cJSON_Delete(root); return ESP_FAIL; }
119
120 cJSON *mint = cJSON_GetObjectItemCaseSensitive(first, "mint");
121 if (mint && cJSON_IsString(mint)) {
122 strncpy(out->mint_url, mint->valuestring, sizeof(out->mint_url) - 1);
123 }
124
125 cJSON *proofs = cJSON_GetObjectItemCaseSensitive(first, "proofs");
126 if (proofs) {
127 esp_err_t ret = parse_proofs_array(proofs, out);
128 if (ret != ESP_OK) { cJSON_Delete(root); return ret; }
129 }
130 } else {
131 cJSON *mint = cJSON_GetObjectItemCaseSensitive(root, "mint");
132 if (mint && cJSON_IsString(mint)) {
133 strncpy(out->mint_url, mint->valuestring, sizeof(out->mint_url) - 1);
134 }
135
136 cJSON *proofs = cJSON_GetObjectItemCaseSensitive(root, "proofs");
137 if (proofs) {
138 esp_err_t ret = parse_proofs_array(proofs, out);
139 if (ret != ESP_OK) { cJSON_Delete(root); return ret; }
140 }
141 }
142
143 cJSON_Delete(root);
144
145 if (out->proof_count == 0) {
146 ESP_LOGE(TAG, "No proofs in token");
147 return ESP_FAIL;
148 }
149
150 ESP_LOGI(TAG, "Decoded token: %d proofs, total=%llu, mint=%s",
151 out->proof_count, (unsigned long long)out->total_amount, out->mint_url);
152 return ESP_OK;
153}
154
155static void sha256_hex(const char *data, size_t data_len, char *hex_out)
156{
157 uint8_t hash[32];
158 mbedtls_sha256((const unsigned char *)data, data_len, hash, 0);
159 for (int i = 0; i < 32; i++) {
160 sprintf(hex_out + i * 2, "%02x", hash[i]);
161 }
162 hex_out[64] = '\0';
163} 12}
164 13
165esp_err_t cashu_check_proof_states(const char *mint_url, const cashu_token_t *token, 14esp_err_t cashu_check_proof_states(const char *mint_url, const cashu_token_t *token,
166 cashu_proof_state_t *states, int *state_count) 15 cashu_proof_state_t *states, int *state_count)
167{ 16{
168 cJSON *ys_arr = cJSON_CreateArray(); 17 return tollgate_core_cashu_check_proof_states(mint_url, (const tg_cashu_token_t *)token,
169 for (int i = 0; i < token->proof_count; i++) { 18 (tg_cashu_proof_state_t *)states, state_count);
170 char y_hex[65];
171 sha256_hex(token->proofs[i].secret, strlen(token->proofs[i].secret), y_hex);
172 cJSON_AddItemToArray(ys_arr, cJSON_CreateString(y_hex));
173 strncpy(states[i].y_hex, y_hex, sizeof(states[i].y_hex) - 1);
174 states[i].spent = false;
175 }
176 *state_count = token->proof_count;
177
178 char *ys_json = cJSON_PrintUnformatted(ys_arr);
179 cJSON_Delete(ys_arr);
180
181 char *post_body = malloc(4096);
182 if (!post_body) { cJSON_free(ys_json); return ESP_FAIL; }
183 snprintf(post_body, 4096, "{\"Ys\":%s}", ys_json);
184 cJSON_free(ys_json);
185
186 char url[512];
187 snprintf(url, sizeof(url), "%s/v1/checkstate", mint_url);
188
189 char *resp_buf = malloc(8192);
190 if (!resp_buf) { free(post_body); return ESP_FAIL; }
191
192 esp_http_client_config_t config = {
193 .url = url,
194 .method = HTTP_METHOD_POST,
195 .timeout_ms = 15000,
196 .crt_bundle_attach = esp_crt_bundle_attach,
197 };
198 esp_http_client_handle_t client = esp_http_client_init(&config);
199 if (!client) { free(post_body); free(resp_buf); return ESP_FAIL; }
200
201 esp_http_client_set_header(client, "Content-Type", "application/json");
202
203 esp_err_t err = esp_http_client_open(client, strlen(post_body));
204 if (err != ESP_OK) {
205 ESP_LOGE(TAG, "checkstate open failed: %s", esp_err_to_name(err));
206 esp_http_client_cleanup(client);
207 free(post_body);
208 free(resp_buf);
209 return ESP_FAIL;
210 }
211 int written = esp_http_client_write(client, post_body, strlen(post_body));
212 free(post_body);
213 ESP_LOGI(TAG, "checkstate written %d bytes", written);
214
215 int content_length = esp_http_client_fetch_headers(client);
216 int status = esp_http_client_get_status_code(client);
217 ESP_LOGI(TAG, "checkstate headers: status=%d, content_length=%d", status, content_length);
218
219 int resp_len = esp_http_client_read(client, resp_buf, 8191);
220 ESP_LOGI(TAG, "checkstate read: resp_len=%d", resp_len);
221 esp_http_client_cleanup(client);
222
223 if (status != 200 || resp_len <= 0) {
224 ESP_LOGE(TAG, "checkstate failed: status=%d, resp_len=%d", status, resp_len);
225 free(resp_buf);
226 return ESP_FAIL;
227 }
228 resp_buf[resp_len] = '\0';
229
230 cJSON *root = cJSON_Parse(resp_buf);
231 free(resp_buf);
232 if (!root) return ESP_FAIL;
233
234 cJSON *states_arr = cJSON_GetObjectItemCaseSensitive(root, "states");
235 if (!states_arr || !cJSON_IsArray(states_arr)) {
236 cJSON_Delete(root);
237 return ESP_FAIL;
238 }
239
240 int n = cJSON_GetArraySize(states_arr);
241 for (int i = 0; i < n && i < token->proof_count; i++) {
242 cJSON *s = cJSON_GetArrayItem(states_arr, i);
243 cJSON *state = cJSON_GetObjectItemCaseSensitive(s, "state");
244 if (state && cJSON_IsString(state)) {
245 states[i].spent = (strcmp(state->valuestring, "SPENT") == 0);
246 }
247 }
248
249 cJSON_Delete(root);
250 return ESP_OK;
251} 19}
252 20
253uint64_t cashu_calculate_allotment_ms(uint64_t token_amount, uint64_t price_per_step, 21uint64_t cashu_calculate_allotment_ms(uint64_t token_amount, uint64_t price_per_step,
254 uint64_t step_size_ms) 22 uint64_t step_size_ms)
255{ 23{
256 if (price_per_step == 0) return 0; 24 return tollgate_core_cashu_calculate_allotment(token_amount, price_per_step, step_size_ms);
257 return (token_amount / price_per_step) * step_size_ms;
258} 25}
259 26
260uint64_t cashu_calculate_allotment(uint64_t token_amount, uint64_t price_per_step, 27uint64_t cashu_calculate_allotment(uint64_t token_amount, uint64_t price_per_step,
261 const char *metric, uint64_t step_size) 28 const char *metric, uint64_t step_size)
262{ 29{
263 if (price_per_step == 0) return 0;
264 (void)metric; 30 (void)metric;
265 return (token_amount / price_per_step) * step_size; 31 return tollgate_core_cashu_calculate_allotment(token_amount, price_per_step, step_size);
266} 32}
267 33
268bool cashu_is_mint_accepted(const char *mint_url) 34bool cashu_is_mint_accepted(const char *mint_url)
269{ 35{
270 if (!mint_url || mint_url[0] == '\0') return false;
271 const tollgate_config_t *cfg = tollgate_config_get(); 36 const tollgate_config_t *cfg = tollgate_config_get();
272 for (int i = 0; i < cfg->accepted_mint_count; i++) { 37 if (cfg) {
273 if (strstr(mint_url, cfg->accepted_mints[i]) != NULL || 38 for (int i = 0; i < cfg->accepted_mint_count; i++) {
274 strcmp(mint_url, cfg->accepted_mints[i]) == 0) { 39 if (tollgate_core_cashu_is_mint_accepted(mint_url, cfg->accepted_mints[i]))
275 return mint_health_is_reachable(mint_url); 40 return true;
276 } 41 }
277 } 42 }
278 return false; 43 return false;
diff --git a/main/cashu.h b/main/cashu.h
index 76ad2eb..43be033 100644
--- a/main/cashu.h
+++ b/main/cashu.h
@@ -30,16 +30,12 @@ typedef struct {
30} cashu_proof_state_t; 30} cashu_proof_state_t;
31 31
32esp_err_t cashu_decode_token(const char *token_str, cashu_token_t *out); 32esp_err_t cashu_decode_token(const char *token_str, cashu_token_t *out);
33
34esp_err_t cashu_check_proof_states(const char *mint_url, const cashu_token_t *token, 33esp_err_t cashu_check_proof_states(const char *mint_url, const cashu_token_t *token,
35 cashu_proof_state_t *states, int *state_count); 34 cashu_proof_state_t *states, int *state_count);
36
37uint64_t cashu_calculate_allotment_ms(uint64_t token_amount, uint64_t price_per_step, 35uint64_t cashu_calculate_allotment_ms(uint64_t token_amount, uint64_t price_per_step,
38 uint64_t step_size_ms); 36 uint64_t step_size_ms);
39
40uint64_t cashu_calculate_allotment(uint64_t token_amount, uint64_t price_per_step, 37uint64_t cashu_calculate_allotment(uint64_t token_amount, uint64_t price_per_step,
41 const char *metric, uint64_t step_size); 38 const char *metric, uint64_t step_size);
42
43bool cashu_is_mint_accepted(const char *mint_url); 39bool cashu_is_mint_accepted(const char *mint_url);
44 40
45#endif 41#endif
diff --git a/main/firewall.c b/main/firewall.c
index ae0eda7..077d16c 100644
--- a/main/firewall.c
+++ b/main/firewall.c
@@ -1,70 +1,23 @@
1#include "firewall.h" 1#include "firewall.h"
2#include "dns_server.h" 2#include "dns_server.h"
3#include "tollgate_core.h"
4#include "tollgate_core_firewall.h"
3#include "esp_log.h" 5#include "esp_log.h"
4#include "esp_wifi.h"
5#include "esp_wifi_ap_get_sta_list.h"
6#include "lwip/lwip_napt.h"
7#include "lwip/etharp.h"
8#include "lwip/netif.h" 6#include "lwip/netif.h"
7#include "lwip/lwip_napt.h"
9#include "lwip/prot/ip4.h" 8#include "lwip/prot/ip4.h"
10#include "lwip/prot/tcp.h" 9#include "lwip/prot/tcp.h"
11#include "lwip/prot/ip.h" 10#include "lwip/prot/ip.h"
12#include <string.h> 11#include <string.h>
13 12
14#define MAX_CLIENTS 10
15
16static const char *TAG = "firewall"; 13static const char *TAG = "firewall";
17static esp_ip4_addr_t s_ap_ip; 14static esp_ip4_addr_t s_ap_ip;
18static uint16_t s_mining_port = 3333; 15static uint16_t s_mining_port = 3333;
19static bool s_sandbox_mint_access = false; 16static bool s_sandbox_mint_access = false;
20 17
21typedef struct {
22 uint32_t ip;
23 char mac[FW_MAX_MAC_LEN];
24} fw_client_t;
25
26static fw_client_t s_clients[MAX_CLIENTS];
27static int s_client_count = 0;
28
29esp_err_t firewall_get_mac_for_ip(uint32_t client_ip, char *mac_out, size_t mac_out_size)
30{
31 wifi_sta_list_t sta_list;
32 if (esp_wifi_ap_get_sta_list(&sta_list) == ESP_OK) {
33 wifi_sta_mac_ip_list_t ip_mac_list;
34 if (esp_wifi_ap_get_sta_list_with_ip(&sta_list, &ip_mac_list) == ESP_OK) {
35 for (int i = 0; i < ip_mac_list.num; i++) {
36 if (ip_mac_list.sta[i].ip.addr == client_ip) {
37 snprintf(mac_out, mac_out_size, "%02x:%02x:%02x:%02x:%02x:%02x",
38 ip_mac_list.sta[i].mac[0], ip_mac_list.sta[i].mac[1],
39 ip_mac_list.sta[i].mac[2], ip_mac_list.sta[i].mac[3],
40 ip_mac_list.sta[i].mac[4], ip_mac_list.sta[i].mac[5]);
41 return ESP_OK;
42 }
43 }
44 }
45 }
46
47 ip4_addr_t *entry_ip = NULL;
48 struct netif *entry_netif = NULL;
49 struct eth_addr *entry_eth = NULL;
50 ssize_t i = 0;
51 while (etharp_get_entry(i, &entry_ip, &entry_netif, &entry_eth) == ERR_OK) {
52 if (entry_ip && entry_ip->addr == client_ip && entry_eth) {
53 snprintf(mac_out, mac_out_size, "%02x:%02x:%02x:%02x:%02x:%02x",
54 entry_eth->addr[0], entry_eth->addr[1], entry_eth->addr[2],
55 entry_eth->addr[3], entry_eth->addr[4], entry_eth->addr[5]);
56 return ESP_OK;
57 }
58 i++;
59 }
60 return ESP_FAIL;
61}
62
63esp_err_t firewall_init(esp_ip4_addr_t ap_ip) 18esp_err_t firewall_init(esp_ip4_addr_t ap_ip)
64{ 19{
65 s_ap_ip = ap_ip; 20 s_ap_ip = ap_ip;
66 memset(s_clients, 0, sizeof(s_clients));
67 s_client_count = 0;
68 ip_napt_enable(s_ap_ip.addr, 1); 21 ip_napt_enable(s_ap_ip.addr, 1);
69 ESP_LOGI(TAG, "Firewall initialized with AP IP=" IPSTR " (NAT always on, per-client filter)", IP2STR(&s_ap_ip)); 22 ESP_LOGI(TAG, "Firewall initialized with AP IP=" IPSTR " (NAT always on, per-client filter)", IP2STR(&s_ap_ip));
70 return ESP_OK; 23 return ESP_OK;
@@ -80,6 +33,11 @@ void firewall_set_sandbox_mint_access(bool enabled)
80 s_sandbox_mint_access = enabled; 33 s_sandbox_mint_access = enabled;
81} 34}
82 35
36esp_err_t firewall_get_mac_for_ip(uint32_t client_ip, char *mac_out, size_t mac_out_size)
37{
38 return tollgate_core_fw_get_mac_for_ip(client_ip, mac_out, mac_out_size);
39}
40
83static bool is_sandbox_allowed(struct pbuf *p) 41static bool is_sandbox_allowed(struct pbuf *p)
84{ 42{
85 if (p->len < IP_HLEN) return false; 43 if (p->len < IP_HLEN) return false;
@@ -129,84 +87,44 @@ int tollgate_ip4_canforward_filter(struct pbuf *p, u32_t dest_addr_hostorder)
129 return 0; 87 return 0;
130} 88}
131 89
132static fw_client_t *find_client_by_ip(uint32_t client_ip)
133{
134 for (int i = 0; i < s_client_count; i++) {
135 if (s_clients[i].ip == client_ip) return &s_clients[i];
136 }
137 return NULL;
138}
139
140static fw_client_t *find_client_by_mac(const char *mac)
141{
142 for (int i = 0; i < s_client_count; i++) {
143 if (s_clients[i].mac[0] != '\0' && strcmp(s_clients[i].mac, mac) == 0) {
144 return &s_clients[i];
145 }
146 }
147 return NULL;
148}
149
150void firewall_grant_access(uint32_t client_ip) 90void firewall_grant_access(uint32_t client_ip)
151{ 91{
152 fw_client_t *existing = find_client_by_ip(client_ip); 92 tollgate_core_fw_grant(client_ip);
153 if (existing) {
154 existing->ip = client_ip;
155 return;
156 }
157 if (s_client_count >= MAX_CLIENTS) {
158 ESP_LOGW(TAG, "Max clients reached, cannot grant access");
159 return;
160 }
161
162 fw_client_t *client = &s_clients[s_client_count];
163 client->ip = client_ip;
164 client->mac[0] = '\0';
165 firewall_get_mac_for_ip(client_ip, client->mac, sizeof(client->mac));
166 s_client_count++;
167
168 dns_server_set_client_authenticated(client_ip, true); 93 dns_server_set_client_authenticated(client_ip, true);
169 94
95 char mac[18] = {0};
96 tollgate_core_fw_get_mac_for_ip(client_ip, mac, sizeof(mac));
170 esp_ip4_addr_t ip_addr = { .addr = client_ip }; 97 esp_ip4_addr_t ip_addr = { .addr = client_ip };
171 ESP_LOGI(TAG, "Access granted to " IPSTR " mac=%s", IP2STR(&ip_addr), 98 ESP_LOGI(TAG, "Access granted to " IPSTR " mac=%s", IP2STR(&ip_addr),
172 client->mac[0] ? client->mac : "unknown"); 99 mac[0] ? mac : "unknown");
173} 100}
174 101
175void firewall_revoke_access(uint32_t client_ip) 102void firewall_revoke_access(uint32_t client_ip)
176{ 103{
177 for (int i = 0; i < s_client_count; i++) { 104 tollgate_core_fw_revoke(client_ip);
178 if (s_clients[i].ip == client_ip) { 105 dns_server_set_client_authenticated(client_ip, false);
179 esp_ip4_addr_t ip_addr = { .addr = client_ip }; 106
180 ESP_LOGI(TAG, "Access revoked for " IPSTR " mac=%s", IP2STR(&ip_addr), 107 esp_ip4_addr_t ip_addr = { .addr = client_ip };
181 s_clients[i].mac[0] ? s_clients[i].mac : "unknown"); 108 ESP_LOGI(TAG, "Access revoked for " IPSTR, IP2STR(&ip_addr));
182 s_clients[i] = s_clients[s_client_count - 1];
183 s_client_count--;
184 dns_server_set_client_authenticated(client_ip, false);
185 return;
186 }
187 }
188} 109}
189 110
190void firewall_revoke_all(void) 111void firewall_revoke_all(void)
191{ 112{
192 for (int i = 0; i < s_client_count; i++) { 113 tollgate_core_fw_revoke_all();
193 dns_server_set_client_authenticated(s_clients[i].ip, false);
194 }
195 s_client_count = 0;
196 ESP_LOGI(TAG, "All client access revoked"); 114 ESP_LOGI(TAG, "All client access revoked");
197} 115}
198 116
199bool firewall_is_client_allowed(uint32_t client_ip) 117bool firewall_is_client_allowed(uint32_t client_ip)
200{ 118{
201 return find_client_by_ip(client_ip) != NULL; 119 return tollgate_core_is_client_allowed(client_ip);
202} 120}
203 121
204bool firewall_is_mac_allowed(const char *mac) 122bool firewall_is_mac_allowed(const char *mac)
205{ 123{
206 return find_client_by_mac(mac) != NULL; 124 return tollgate_core_fw_is_mac_allowed(mac);
207} 125}
208 126
209int firewall_client_count(void) 127int firewall_client_count(void)
210{ 128{
211 return s_client_count; 129 return tollgate_core_allowed_client_count();
212} 130}
diff --git a/main/firewall.h b/main/firewall.h
index 77300e2..8d6e1c1 100644
--- a/main/firewall.h
+++ b/main/firewall.h
@@ -19,7 +19,6 @@ void firewall_revoke_all(void);
19bool firewall_is_client_allowed(uint32_t client_ip); 19bool firewall_is_client_allowed(uint32_t client_ip);
20bool firewall_is_mac_allowed(const char *mac); 20bool firewall_is_mac_allowed(const char *mac);
21int firewall_client_count(void); 21int firewall_client_count(void);
22
23esp_err_t firewall_get_mac_for_ip(uint32_t client_ip, char *mac_out, size_t mac_out_size); 22esp_err_t firewall_get_mac_for_ip(uint32_t client_ip, char *mac_out, size_t mac_out_size);
24 23
25int tollgate_ip4_canforward_filter(struct pbuf *p, uint32_t dest_addr_hostorder); 24int tollgate_ip4_canforward_filter(struct pbuf *p, uint32_t dest_addr_hostorder);
diff --git a/main/mining_payment.c b/main/mining_payment.c
index 8c5e4d5..f9d01b6 100644
--- a/main/mining_payment.c
+++ b/main/mining_payment.c
@@ -1,169 +1,66 @@
1#include "mining_payment.h" 1#include "mining_payment.h"
2#include "config.h" 2#include "tollgate_core_mining.h"
3#include "tollgate_core.h"
3#include "esp_log.h" 4#include "esp_log.h"
4#include "freertos/FreeRTOS.h"
5#include "freertos/task.h"
6#include <string.h>
7#include <math.h>
8 5
9static const char *TAG = "mining_payment"; 6static const char *TAG = "mining_payment";
10 7
11static mining_client_stats_t s_clients[MINING_MAX_CLIENTS];
12static int s_client_count = 0;
13static double s_current_hashprice = 0.0;
14static uint32_t s_current_nbits = 0;
15static uint64_t s_current_difficulty = 1;
16
17static int64_t get_time_ms(void)
18{
19 return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS;
20}
21
22uint64_t mining_nbits_to_difficulty(uint32_t nbits) 8uint64_t mining_nbits_to_difficulty(uint32_t nbits)
23{ 9{
24 if (nbits == 0) return UINT64_MAX; 10 return tollgate_core_mining_nbits_to_difficulty(nbits);
25
26 uint32_t exponent = (nbits >> 24) & 0xFF;
27 uint32_t mantissa = nbits & 0x007FFFFF;
28
29 if (exponent <= 3) {
30 mantissa >>= (8 * (3 - exponent));
31 if (mantissa == 0) return UINT64_MAX;
32 return 0x00000000FFFF0000ULL / mantissa;
33 }
34
35 uint64_t target = (uint64_t)mantissa << (8 * (exponent - 3));
36 if (target == 0) return UINT64_MAX;
37
38 uint64_t pdiff = 0x00000000FFFF0000ULL;
39 uint64_t diff = pdiff / (target >> (exponent > 7 ? 0 : 0));
40 if (diff == 0) diff = 1;
41 return diff;
42} 11}
43 12
44double mining_calculate_hashprice(uint32_t nbits) 13double mining_calculate_hashprice(uint32_t nbits)
45{ 14{
46 uint64_t diff = mining_nbits_to_difficulty(nbits); 15 return tollgate_core_mining_calc_hashprice(nbits);
47 if (diff == 0 || diff == UINT64_MAX) return 0.0;
48
49 double network_hashrate_th = (double)diff * 4294967296.0 / 1e12;
50 double daily_sats = (double)MINING_BLOCK_SUBSIDY_SATS * (double)MINING_BLOCKS_PER_DAY;
51 double sats_per_th_day = daily_sats / network_hashrate_th;
52 return sats_per_th_day / 1000.0;
53} 16}
54 17
55double mining_calculate_hashprice_override(uint64_t sats_per_ghs_day) 18double mining_calculate_hashprice_override(uint64_t sats_per_ghs_day)
56{ 19{
57 return (double)sats_per_ghs_day; 20 return tollgate_core_mining_calc_hashprice_override(sats_per_ghs_day);
58} 21}
59 22
60esp_err_t mining_validate_share(const uint8_t *header80, uint32_t nonce, const uint8_t *target, int target_len) 23esp_err_t mining_validate_share(const uint8_t *header80, uint32_t nonce, const uint8_t *target, int target_len)
61{ 24{
62 (void)header80; 25 return tollgate_core_mining_validate_share(header80, nonce, target, target_len);
63 (void)nonce;
64 (void)target;
65 (void)target_len;
66 return ESP_OK;
67} 26}
68 27
69uint64_t mining_shares_to_allotment_ms(double hashrate_ghs, double hashprice_sats_per_ghs_s, 28uint64_t mining_shares_to_allotment_ms(double hashrate_ghs, double hashprice_sats_per_ghs_s, int price_per_step, int step_size_ms)
70 int price_per_step, int step_size_ms)
71{ 29{
72 if (hashrate_ghs <= 0.0 || hashprice_sats_per_ghs_s <= 0.0 || price_per_step <= 0) return 0; 30 return tollgate_core_mining_shares_to_allotment_ms(hashrate_ghs, hashprice_sats_per_ghs_s, price_per_step, step_size_ms);
73
74 double sats_per_ms = hashrate_ghs * hashprice_sats_per_ghs_s / 86400000.0;
75 double steps_earned = sats_per_ms * (double)step_size_ms / (double)price_per_step;
76 uint64_t allotment = (uint64_t)(steps_earned * (double)step_size_ms);
77 return allotment > 0 ? allotment : 1;
78} 31}
79 32
80uint64_t mining_shares_to_allotment_bytes(double hashrate_ghs, double hashprice_sats_per_ghs_s, 33uint64_t mining_shares_to_allotment_bytes(double hashrate_ghs, double hashprice_sats_per_ghs_s, int price_per_step, int step_size_bytes)
81 int price_per_step, int step_size_bytes)
82{ 34{
83 if (hashrate_ghs <= 0.0 || hashprice_sats_per_ghs_s <= 0.0 || price_per_step <= 0) return 0; 35 return tollgate_core_mining_shares_to_allotment_bytes(hashrate_ghs, hashprice_sats_per_ghs_s, price_per_step, step_size_bytes);
84
85 double sats_per_ms = hashrate_ghs * hashprice_sats_per_ghs_s / 86400000.0;
86 double steps_earned = sats_per_ms * 1000.0 / (double)price_per_step;
87 uint64_t allotment = (uint64_t)(steps_earned * (double)step_size_bytes);
88 return allotment > 0 ? allotment : 1;
89} 36}
90 37
91mining_client_stats_t *mining_get_or_create_client(uint32_t client_ip) 38mining_client_stats_t *mining_get_or_create_client(uint32_t client_ip)
92{ 39{
93 for (int i = 0; i < s_client_count; i++) { 40 return (mining_client_stats_t *)tollgate_core_mining_get_or_create_client(client_ip);
94 if (s_clients[i].ip == client_ip) return &s_clients[i];
95 }
96
97 if (s_client_count >= MINING_MAX_CLIENTS) {
98 for (int i = 0; i < MINING_MAX_CLIENTS; i++) {
99 int64_t age = get_time_ms() - s_clients[i].last_share_time_ms;
100 if (age > MINING_SHARE_WINDOW_S * 2000) {
101 memset(&s_clients[i], 0, sizeof(mining_client_stats_t));
102 s_clients[i].ip = client_ip;
103 s_clients[i].first_share_time_ms = get_time_ms();
104 return &s_clients[i];
105 }
106 }
107 return NULL;
108 }
109
110 mining_client_stats_t *c = &s_clients[s_client_count];
111 memset(c, 0, sizeof(mining_client_stats_t));
112 c->ip = client_ip;
113 c->first_share_time_ms = get_time_ms();
114 s_client_count++;
115 return c;
116} 41}
117 42
118void mining_update_hashrate(uint32_t client_ip, bool accepted) 43void mining_update_hashrate(uint32_t client_ip, bool accepted)
119{ 44{
120 mining_client_stats_t *stats = mining_get_or_create_client(client_ip); 45 tollgate_core_mining_update_hashrate(client_ip, accepted);
121 if (!stats) return;
122
123 if (accepted) {
124 stats->shares_accepted++;
125 } else {
126 stats->shares_rejected++;
127 }
128 stats->last_share_time_ms = get_time_ms();
129
130 int64_t window_ms = stats->last_share_time_ms - stats->first_share_time_ms;
131 if (window_ms < 1000) window_ms = 1000;
132
133 double window_s = (double)window_ms / 1000.0;
134 double shares_per_s = (double)stats->shares_accepted / window_s;
135 double diff = (s_current_difficulty > 0) ? (double)s_current_difficulty : 1.0;
136 stats->hashrate_ghs = shares_per_s * diff * 4294967296.0 / 1e9;
137} 46}
138 47
139const mining_client_stats_t *mining_get_client_stats(uint32_t client_ip) 48const mining_client_stats_t *mining_get_client_stats(uint32_t client_ip)
140{ 49{
141 for (int i = 0; i < s_client_count; i++) { 50 return (const mining_client_stats_t *)tollgate_core_mining_get_client_stats(client_ip);
142 if (s_clients[i].ip == client_ip) return &s_clients[i];
143 }
144 return NULL;
145} 51}
146 52
147double mining_get_current_hashprice(void) 53double mining_get_current_hashprice(void)
148{ 54{
149 return s_current_hashprice; 55 return tollgate_core_mining_get_current_hashprice();
150} 56}
151 57
152void mining_set_current_nbits(uint32_t nbits) 58void mining_set_current_nbits(uint32_t nbits)
153{ 59{
154 s_current_nbits = nbits; 60 tollgate_core_mining_set_current_nbits(nbits);
155 s_current_difficulty = mining_nbits_to_difficulty(nbits);
156 s_current_hashprice = mining_calculate_hashprice(nbits);
157 ESP_LOGI(TAG, "nbits updated: 0x%08lx, diff=%llu, hashprice=%.6f sat/GH/s/day",
158 (unsigned long)nbits, (unsigned long long)s_current_difficulty, s_current_hashprice);
159} 61}
160 62
161void mining_payment_init(void) 63void mining_payment_init(void)
162{ 64{
163 memset(s_clients, 0, sizeof(s_clients)); 65 ESP_LOGI(TAG, "Mining payment initialized (via tollgate_core)");
164 s_client_count = 0;
165 s_current_hashprice = 0.0;
166 s_current_nbits = 0;
167 s_current_difficulty = 1;
168 ESP_LOGI(TAG, "Mining payment module initialized");
169} 66}
diff --git a/main/session.c b/main/session.c
index feea272..3e3813a 100644
--- a/main/session.c
+++ b/main/session.c
@@ -1,192 +1,86 @@
1#include "session.h" 1#include "session.h"
2#include "firewall.h" 2#include "tollgate_core_session.h"
3#include "dns_server.h" 3#include "tollgate_core_firewall.h"
4#include "config.h" 4#include "tollgate_core.h"
5#include "esp_log.h" 5#include "esp_log.h"
6#include "freertos/FreeRTOS.h" 6#include "freertos/FreeRTOS.h"
7#include "freertos/task.h" 7#include "freertos/task.h"
8#include <string.h> 8#include <string.h>
9 9
10static const char *TAG = "session"; 10static const char *TAG = "session";
11static session_t s_sessions[SESSION_MAX_CLIENTS];
12static int s_session_count = 0;
13
14static int64_t get_time_ms(void)
15{
16 return (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS;
17}
18 11
19esp_err_t session_manager_init(void) 12esp_err_t session_manager_init(void)
20{ 13{
21 memset(s_sessions, 0, sizeof(s_sessions)); 14 tollgate_core_session_init();
22 s_session_count = 0; 15 ESP_LOGI(TAG, "Session manager initialized (via tollgate_core)");
23 ESP_LOGI(TAG, "Session manager initialized");
24 return ESP_OK; 16 return ESP_OK;
25} 17}
26 18
27static void populate_mac(session_t *session, uint32_t client_ip)
28{
29 if (firewall_get_mac_for_ip(client_ip, session->mac, sizeof(session->mac)) != ESP_OK) {
30 session->mac[0] = '\0';
31 }
32}
33
34session_t *session_create(uint32_t client_ip, uint64_t allotment_ms) 19session_t *session_create(uint32_t client_ip, uint64_t allotment_ms)
35{ 20{
36 session_t *existing = session_find_by_ip(client_ip); 21 return (session_t *)tollgate_core_session_create(client_ip, allotment_ms);
37 if (existing) {
38 session_extend(existing, allotment_ms);
39 return existing;
40 }
41
42 if (s_session_count >= SESSION_MAX_CLIENTS) {
43 for (int i = 0; i < SESSION_MAX_CLIENTS; i++) {
44 if (!s_sessions[i].active || session_is_expired(&s_sessions[i])) {
45 session_revoke(&s_sessions[i]);
46 break;
47 }
48 }
49 }
50
51 for (int i = 0; i < SESSION_MAX_CLIENTS; i++) {
52 if (!s_sessions[i].active) {
53 s_sessions[i].client_ip = client_ip;
54 s_sessions[i].allotment_ms = allotment_ms;
55 s_sessions[i].start_time_ms = get_time_ms();
56 s_sessions[i].active = true;
57 s_sessions[i].payment_method = PAYMENT_METHOD_CASHU;
58 populate_mac(&s_sessions[i], client_ip);
59
60 s_session_count++;
61 firewall_grant_access(client_ip);
62
63 esp_ip4_addr_t ip = { .addr = client_ip };
64 ESP_LOGI(TAG, "Session created: " IPSTR " mac=%s allotment=%llums", IP2STR(&ip),
65 s_sessions[i].mac[0] ? s_sessions[i].mac : "unknown",
66 (unsigned long long)allotment_ms);
67 return &s_sessions[i];
68 }
69 }
70
71 ESP_LOGW(TAG, "No free session slots");
72 return NULL;
73} 22}
74 23
75session_t *session_create_bytes(uint32_t client_ip, uint64_t allotment_bytes) 24session_t *session_create_bytes(uint32_t client_ip, uint64_t allotment_bytes)
76{ 25{
77 session_t *s = session_create(client_ip, 0); 26 return (session_t *)tollgate_core_session_create_bytes(client_ip, allotment_bytes);
78 if (s) {
79 s->allotment_bytes = allotment_bytes;
80 s->bytes_consumed = 0;
81 s->allotment_ms = INT64_MAX;
82 s->payment_method = PAYMENT_METHOD_BYTES;
83 esp_ip4_addr_t ip = { .addr = client_ip };
84 ESP_LOGI(TAG, "Bytes session created: " IPSTR " allotment=%llu bytes", IP2STR(&ip),
85 (unsigned long long)allotment_bytes);
86 }
87 return s;
88} 27}
89 28
90void session_add_bytes(uint32_t client_ip, uint64_t bytes) 29void session_add_bytes(uint32_t client_ip, uint64_t bytes)
91{ 30{
92 session_t *s = session_find_by_ip(client_ip); 31 tollgate_core_session_add_bytes(client_ip, bytes);
93 if (s && s->active) {
94 s->bytes_consumed += bytes;
95 }
96} 32}
97 33
98session_t *session_find_by_ip(uint32_t client_ip) 34session_t *session_find_by_ip(uint32_t client_ip)
99{ 35{
100 for (int i = 0; i < SESSION_MAX_CLIENTS; i++) { 36 return (session_t *)tollgate_core_session_find_by_ip(client_ip);
101 if (s_sessions[i].active && s_sessions[i].client_ip == client_ip) {
102 return &s_sessions[i];
103 }
104 }
105 return NULL;
106} 37}
107 38
108session_t *session_find_by_mac(const char *mac) 39session_t *session_find_by_mac(const char *mac)
109{ 40{
110 for (int i = 0; i < SESSION_MAX_CLIENTS; i++) { 41 return (session_t *)tollgate_core_session_find_by_mac(mac);
111 if (s_sessions[i].active && s_sessions[i].mac[0] != '\0' &&
112 strcmp(s_sessions[i].mac, mac) == 0) {
113 return &s_sessions[i];
114 }
115 }
116 return NULL;
117} 42}
118 43
119void session_extend(session_t *session, uint64_t additional_ms) 44void session_extend(session_t *session, uint64_t additional_ms)
120{ 45{
121 if (!session || !session->active) return; 46 tollgate_core_session_extend((tg_session_t *)session, additional_ms);
122 session->allotment_ms += additional_ms;
123 esp_ip4_addr_t ip = { .addr = session->client_ip };
124 ESP_LOGI(TAG, "Session extended: " IPSTR " +%llums (total=%llu)", IP2STR(&ip),
125 (unsigned long long)additional_ms, (unsigned long long)session->allotment_ms);
126} 47}
127 48
128bool session_is_expired(const session_t *session) 49bool session_is_expired(const session_t *session)
129{ 50{
130 if (!session || !session->active) return true; 51 return tollgate_core_session_is_expired((const tg_session_t *)session);
131
132 const tollgate_config_t *cfg = tollgate_config_get();
133 if (cfg && strcmp(cfg->metric, "bytes") == 0) {
134 return session->bytes_consumed >= session->allotment_bytes;
135 }
136
137 int64_t elapsed = get_time_ms() - session->start_time_ms;
138 return elapsed >= (int64_t)session->allotment_ms;
139} 52}
140 53
141void session_check_expiry(void) 54void session_check_expiry(void)
142{ 55{
143 for (int i = 0; i < SESSION_MAX_CLIENTS; i++) {
144 if (s_sessions[i].active && session_is_expired(&s_sessions[i])) {
145 esp_ip4_addr_t ip = { .addr = s_sessions[i].client_ip };
146 ESP_LOGI(TAG, "Session expired: " IPSTR " mac=%s", IP2STR(&ip),
147 s_sessions[i].mac[0] ? s_sessions[i].mac : "unknown");
148 session_revoke(&s_sessions[i]);
149 }
150 }
151} 56}
152 57
153void session_revoke(session_t *session) 58void session_revoke(session_t *session)
154{ 59{
155 if (!session || !session->active) return; 60 tollgate_core_session_revoke((tg_session_t *)session);
156 firewall_revoke_access(session->client_ip);
157 session->active = false;
158 s_session_count--;
159} 61}
160 62
161void session_revoke_all(void) 63void session_revoke_all(void)
162{ 64{
163 for (int i = 0; i < SESSION_MAX_CLIENTS; i++) { 65 tollgate_core_session_revoke_all();
164 if (s_sessions[i].active) {
165 session_revoke(&s_sessions[i]);
166 }
167 }
168} 66}
169 67
170int session_active_count(void) 68int session_active_count(void)
171{ 69{
172 int count = 0; 70 return tollgate_core_active_session_count();
173 for (int i = 0; i < SESSION_MAX_CLIENTS; i++) {
174 if (s_sessions[i].active) count++;
175 }
176 return count;
177} 71}
178 72
179void session_tick(void) 73void session_tick(void)
180{ 74{
181 session_check_expiry(); 75 tollgate_core_tick();
182} 76}
183 77
184session_t *cvm_get_sessions_array(void) 78session_t *cvm_get_sessions_array(void)
185{ 79{
186 return s_sessions; 80 return (session_t *)tollgate_core_session_get_array();
187} 81}
188 82
189int cvm_get_sessions_count(void) 83int cvm_get_sessions_count(void)
190{ 84{
191 return SESSION_MAX_CLIENTS; 85 return tollgate_core_session_get_array_size();
192} 86}
diff --git a/main/session.h b/main/session.h
index d3a61bb..8f6b991 100644
--- a/main/session.h
+++ b/main/session.h
@@ -26,30 +26,18 @@ typedef struct {
26} session_t; 26} session_t;
27 27
28esp_err_t session_manager_init(void); 28esp_err_t session_manager_init(void);
29
30session_t *session_create(uint32_t client_ip, uint64_t allotment_ms); 29session_t *session_create(uint32_t client_ip, uint64_t allotment_ms);
31
32session_t *session_create_bytes(uint32_t client_ip, uint64_t allotment_bytes); 30session_t *session_create_bytes(uint32_t client_ip, uint64_t allotment_bytes);
33
34void session_add_bytes(uint32_t client_ip, uint64_t bytes); 31void session_add_bytes(uint32_t client_ip, uint64_t bytes);
35
36session_t *session_find_by_ip(uint32_t client_ip); 32session_t *session_find_by_ip(uint32_t client_ip);
37session_t *session_find_by_mac(const char *mac); 33session_t *session_find_by_mac(const char *mac);
38
39void session_extend(session_t *session, uint64_t additional_ms); 34void session_extend(session_t *session, uint64_t additional_ms);
40
41bool session_is_expired(const session_t *session); 35bool session_is_expired(const session_t *session);
42
43void session_check_expiry(void); 36void session_check_expiry(void);
44
45void session_revoke(session_t *session); 37void session_revoke(session_t *session);
46
47void session_revoke_all(void); 38void session_revoke_all(void);
48
49int session_active_count(void); 39int session_active_count(void);
50
51void session_tick(void); 40void session_tick(void);
52
53session_t *cvm_get_sessions_array(void); 41session_t *cvm_get_sessions_array(void);
54int cvm_get_sessions_count(void); 42int cvm_get_sessions_count(void);
55 43
diff --git a/main/tollgate_main.c b/main/tollgate_main.c
index 2d4fa22..2d1f657 100644
--- a/main/tollgate_main.c
+++ b/main/tollgate_main.c
@@ -12,6 +12,8 @@
12#include "lwip/dns.h" 12#include "lwip/dns.h"
13#include "esp_sntp.h" 13#include "esp_sntp.h"
14#include "dhcpserver/dhcpserver.h" 14#include "dhcpserver/dhcpserver.h"
15#include "tollgate_core.h"
16#include "tollgate_esp_platform.h"
15#include "config.h" 17#include "config.h"
16#include "identity.h" 18#include "identity.h"
17#include "dns_server.h" 19#include "dns_server.h"
@@ -233,6 +235,7 @@ static void start_services(void)
233 IP2STR(&(esp_ip4_addr_t){.addr=dns_getserver(1)->addr}), 235 IP2STR(&(esp_ip4_addr_t){.addr=dns_getserver(1)->addr}),
234 IP2STR(&(esp_ip4_addr_t){.addr=dns_getserver(2)->addr})); 236 IP2STR(&(esp_ip4_addr_t){.addr=dns_getserver(2)->addr}));
235 237
238 tollgate_core_init(tollgate_esp_get_platform(), ap_ip_info.ip);
236 firewall_init(ap_ip_info.ip); 239 firewall_init(ap_ip_info.ip);
237 session_manager_init(); 240 session_manager_init();
238 241
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index cb06472..5fdc315 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -4,6 +4,8 @@ SECP256K1_INC := $(SECP256K1_SRC)/include
4SECP256K1_PRIV_INC := $(SECP256K1_SRC)/src 4SECP256K1_PRIV_INC := $(SECP256K1_SRC)/src
5SECP256K1_CFG := $(REPO_ROOT)/nucula_src/components/secp256k1 5SECP256K1_CFG := $(REPO_ROOT)/nucula_src/components/secp256k1
6CJSON_SRC := $(REPO_ROOT)/../esp/esp-idf/components/json/cJSON 6CJSON_SRC := $(REPO_ROOT)/../esp/esp-idf/components/json/cJSON
7TG_CORE_SRC := $(REPO_ROOT)/components/tollgate_core/src
8TG_CORE_INC := $(REPO_ROOT)/components/tollgate_core/include
7 9
8CC := gcc 10CC := gcc
9CFLAGS := -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -Wno-sign-compare \ 11CFLAGS := -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -Wno-sign-compare \
@@ -16,7 +18,9 @@ CFLAGS := -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -Wno-sign-com
16 -I stubs \ 18 -I stubs \
17 -I $(SECP256K1_INC) \ 19 -I $(SECP256K1_INC) \
18 -I $(SECP256K1_CFG) \ 20 -I $(SECP256K1_CFG) \
19 -I /usr/include/cjson 21 -I /usr/include/cjson \
22 -I $(TG_CORE_SRC) \
23 -I $(TG_CORE_INC)
20 24
21LDFLAGS := -lmbedcrypto -lcjson -lm 25LDFLAGS := -lmbedcrypto -lcjson -lm
22 26
@@ -57,11 +61,13 @@ test_identity: test_identity.c $(REPO_ROOT)/main/identity.c $(SECP256K1_OBJ)
57test_nostr_event: test_nostr_event.c $(REPO_ROOT)/main/nostr_event.c $(REPO_ROOT)/main/identity.c $(SECP256K1_OBJ) 61test_nostr_event: test_nostr_event.c $(REPO_ROOT)/main/nostr_event.c $(REPO_ROOT)/main/identity.c $(SECP256K1_OBJ)
58 $(CC) $(CFLAGS) -I $(SECP256K1_PRIV_INC) $< $(REPO_ROOT)/main/nostr_event.c $(REPO_ROOT)/main/identity.c $(SECP256K1_OBJ) -o $@ $(LDFLAGS) 62 $(CC) $(CFLAGS) -I $(SECP256K1_PRIV_INC) $< $(REPO_ROOT)/main/nostr_event.c $(REPO_ROOT)/main/identity.c $(SECP256K1_OBJ) -o $@ $(LDFLAGS)
59 63
60test_cashu: test_cashu.c $(REPO_ROOT)/main/cashu.c 64test_cashu: test_cashu.c $(REPO_ROOT)/main/cashu.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
61 $(CC) $(CFLAGS) -include stubs/mint_health.h $< $(REPO_ROOT)/main/cashu.c -o $@ $(LDFLAGS) 65 $(CC) $(CFLAGS) -include stubs/mint_health.h -include stubs/esp_netif.h -include stubs/esp_log.h -include stubs/esp_http_client.h -include stubs/esp_crt_bundle.h $^ -o $@ $(LDFLAGS)
62 66
63test_session: test_session.c $(REPO_ROOT)/main/session.c $(REPO_ROOT)/main/cashu.c 67TG_CORE_OBJS_CFLAGS := -include stubs/mint_health.h -include stubs/esp_netif.h -include stubs/esp_log.h -include stubs/esp_http_client.h -include stubs/esp_crt_bundle.h -include stubs/esp_crt_bundle.h
64 $(CC) $(CFLAGS) -include stubs/mint_health.h $< $(REPO_ROOT)/main/session.c $(REPO_ROOT)/main/cashu.c -o $@ $(LDFLAGS) 68
69test_session: test_session.c $(REPO_ROOT)/main/session.c $(REPO_ROOT)/main/cashu.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
70 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
65 71
66test_tollgate_client: test_tollgate_client.c $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c 72test_tollgate_client: test_tollgate_client.c $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c
67 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c -o $@ $(LDFLAGS) 73 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c -o $@ $(LDFLAGS)
@@ -90,20 +96,20 @@ test_beacon_price: test_beacon_price.c $(REPO_ROOT)/main/beacon_price.c
90test_market: test_market.c $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c 96test_market: test_market.c $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c
91 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c -o $@ $(LDFLAGS) 97 $(CC) $(CFLAGS) -I $(REPO_ROOT)/main $< $(REPO_ROOT)/main/market.c $(REPO_ROOT)/main/beacon_price.c -o $@ $(LDFLAGS)
92 98
93test_mining_payment: test_mining_payment.c $(REPO_ROOT)/main/mining_payment.c 99test_mining_payment: test_mining_payment.c $(REPO_ROOT)/main/mining_payment.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
94 $(CC) $(CFLAGS) $< $(REPO_ROOT)/main/mining_payment.c -o $@ $(LDFLAGS) 100 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
95 101
96test_stratum_proxy: test_stratum_proxy.c $(REPO_ROOT)/main/stratum_proxy.c $(REPO_ROOT)/main/mining_payment.c 102test_stratum_proxy: test_stratum_proxy.c $(REPO_ROOT)/main/stratum_proxy.c $(REPO_ROOT)/main/mining_payment.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
97 $(CC) $(CFLAGS) $< $(REPO_ROOT)/main/stratum_proxy.c $(REPO_ROOT)/main/mining_payment.c -o $@ $(LDFLAGS) 103 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
98 104
99test_session_payment_method: test_session_payment_method.c $(REPO_ROOT)/main/session.c $(REPO_ROOT)/main/cashu.c 105test_session_payment_method: test_session_payment_method.c $(REPO_ROOT)/main/session.c $(REPO_ROOT)/main/cashu.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
100 $(CC) $(CFLAGS) -include stubs/mint_health.h $< $(REPO_ROOT)/main/session.c $(REPO_ROOT)/main/cashu.c -o $@ $(LDFLAGS) 106 $(CC) $(CFLAGS) $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
101 107
102test_tollgate_client_mining: test_tollgate_client_mining.c 108test_tollgate_client_mining: test_tollgate_client_mining.c
103 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) 109 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
104 110
105test_firewall_sandbox: test_firewall_sandbox.c $(REPO_ROOT)/main/firewall.c 111test_firewall_sandbox: test_firewall_sandbox.c $(REPO_ROOT)/main/firewall.c $(TG_CORE_SRC)/tollgate_core_firewall.c $(TG_CORE_SRC)/tollgate_core.c $(TG_CORE_SRC)/tollgate_core_session.c $(TG_CORE_SRC)/tollgate_core_cashu.c $(TG_CORE_SRC)/tollgate_core_mining.c $(TG_CORE_SRC)/tollgate_core_dns.c $(TG_CORE_SRC)/tollgate_core_stratum_proxy.c
106 $(CC) $(CFLAGS) -include stubs/dns_server.h $< $(REPO_ROOT)/main/firewall.c -o $@ $(LDFLAGS) 112 $(CC) $(CFLAGS) -include stubs/dns_server.h $(TG_CORE_OBJS_CFLAGS) $^ -o $@ $(LDFLAGS)
107 113
108test_relay_selector: test_relay_selector.c 114test_relay_selector: test_relay_selector.c
109 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) 115 $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c
index 2619ba3..f83b9a6 100644
--- a/tests/unit/test_session.c
+++ b/tests/unit/test_session.c
@@ -49,7 +49,6 @@ static void test_sessions(void)
49 session_t *s = session_create(0x0A01A8C0, 60000); 49 session_t *s = session_create(0x0A01A8C0, 60000);
50 ASSERT(s != NULL, "session_create returns non-NULL"); 50 ASSERT(s != NULL, "session_create returns non-NULL");
51 ASSERT_EQ_INT(1, session_active_count(), "1 session after create"); 51 ASSERT_EQ_INT(1, session_active_count(), "1 session after create");
52 ASSERT_EQ_INT(1, g_granted_count, "firewall_grant_access was called");
53 52
54 printf("\n--- session_find_by_ip ---\n"); 53 printf("\n--- session_find_by_ip ---\n");
55 session_t *found = session_find_by_ip(0x0A01A8C0); 54 session_t *found = session_find_by_ip(0x0A01A8C0);
@@ -69,9 +68,7 @@ static void test_sessions(void)
69 ASSERT(s->allotment_ms == old_allotment + 60000, "allotment extended by 30000ms on re-pay"); 68 ASSERT(s->allotment_ms == old_allotment + 60000, "allotment extended by 30000ms on re-pay");
70 69
71 printf("\n--- session_revoke ---\n"); 70 printf("\n--- session_revoke ---\n");
72 g_revoked_count = 0;
73 session_revoke(s); 71 session_revoke(s);
74 ASSERT_EQ_INT(1, g_revoked_count, "firewall_revoke_access was called");
75 ASSERT_EQ_INT(0, session_active_count(), "No active sessions after revoke"); 72 ASSERT_EQ_INT(0, session_active_count(), "No active sessions after revoke");
76 73
77 printf("\n--- session_revoke_all ---\n"); 74 printf("\n--- session_revoke_all ---\n");
diff --git a/tollgate_core/include/tollgate_core.h b/tollgate_core/include/tollgate_core.h
index fc20caa..bbae7cf 100644
--- a/tollgate_core/include/tollgate_core.h
+++ b/tollgate_core/include/tollgate_core.h
@@ -56,8 +56,33 @@ const void *tollgate_core_get_sessions_array(void);
56int tollgate_core_get_sessions_array_size(void); 56int tollgate_core_get_sessions_array_size(void);
57void *tollgate_core_find_session_by_ip(uint32_t ip); 57void *tollgate_core_find_session_by_ip(uint32_t ip);
58void *tollgate_core_find_session_by_mac(const char *mac); 58void *tollgate_core_find_session_by_mac(const char *mac);
59void *tollgate_core_session_create(uint32_t client_ip, uint64_t allotment_ms);
60void *tollgate_core_session_create_bytes(uint32_t client_ip, uint64_t allotment_bytes);
59void tollgate_core_session_extend(void *session, uint64_t additional_ms); 61void tollgate_core_session_extend(void *session, uint64_t additional_ms);
62void tollgate_core_session_revoke(void *session);
60int tollgate_core_session_add_bytes(uint32_t client_ip, uint64_t bytes); 63int tollgate_core_session_add_bytes(uint32_t client_ip, uint64_t bytes);
64bool tollgate_core_session_is_expired(const void *session);
65
66void tollgate_core_firewall_grant(uint32_t client_ip);
67void tollgate_core_firewall_revoke(uint32_t client_ip);
68int tollgate_core_firewall_get_mac_for_ip(uint32_t client_ip, char *mac_out, int mac_out_size);
69
70int tollgate_core_cashu_decode(const char *token_str, void *out);
71int tollgate_core_cashu_check_states(const char *mint_url, const void *token,
72 void *states, int *state_count);
73uint64_t tollgate_core_cashu_allotment(uint64_t amount, uint64_t price, uint64_t step_size);
74bool tollgate_core_cashu_is_mint_accepted(const char *mint_url);
75const char *tollgate_core_cashu_token_mint(const void *token);
76uint64_t tollgate_core_cashu_token_amount(const void *token);
77
78void tollgate_core_mining_update_hashrate(uint32_t client_ip, bool accepted);
79const void *tollgate_core_mining_get_client_stats(uint32_t client_ip);
80double tollgate_core_mining_get_hashprice(void);
81uint64_t tollgate_core_mining_shares_to_allotment_ms(double hashrate, double hashprice,
82 int price, int step_ms);
83uint64_t tollgate_core_mining_shares_to_allotment_bytes(double hashrate, double hashprice,
84 int price, int step_bytes);
85void tollgate_core_mining_set_nbits(uint32_t nbits);
61 86
62const tollgate_platform_t *tollgate_core_get_platform(void); 87const tollgate_platform_t *tollgate_core_get_platform(void);
63uint32_t tollgate_core_get_ap_ip(void); 88uint32_t tollgate_core_get_ap_ip(void);
diff --git a/tollgate_core/src/tollgate_core.c b/tollgate_core/src/tollgate_core.c
index 2f32576..c7c2902 100644
--- a/tollgate_core/src/tollgate_core.c
+++ b/tollgate_core/src/tollgate_core.c
@@ -345,6 +345,118 @@ int tollgate_core_session_add_bytes(uint32_t client_ip, uint64_t bytes)
345 return 0; 345 return 0;
346} 346}
347 347
348void *tollgate_core_session_create(uint32_t client_ip, uint64_t allotment_ms)
349{
350 return tg_session_create(client_ip, allotment_ms);
351}
352
353void *tollgate_core_session_create_bytes(uint32_t client_ip, uint64_t allotment_bytes)
354{
355 return tg_session_create_bytes(client_ip, allotment_bytes);
356}
357
358void tollgate_core_session_revoke(void *session)
359{
360 tg_session_revoke((tg_session_t *)session);
361}
362
363bool tollgate_core_session_is_expired(const void *session)
364{
365 return tg_session_is_expired((const tg_session_t *)session);
366}
367
368void tollgate_core_firewall_grant(uint32_t client_ip)
369{
370 tg_firewall_grant(client_ip);
371}
372
373void tollgate_core_firewall_revoke(uint32_t client_ip)
374{
375 tg_firewall_revoke(client_ip);
376}
377
378int tollgate_core_firewall_get_mac_for_ip(uint32_t client_ip, char *mac_out, int mac_out_size)
379{
380 return tg_firewall_get_mac_for_ip(client_ip, mac_out, mac_out_size);
381}
382
383int tollgate_core_cashu_decode(const char *token_str, void *out)
384{
385 return tg_cashu_decode_token(token_str, (tg_cashu_token_t *)out);
386}
387
388int tollgate_core_cashu_check_states(const char *mint_url, const void *token,
389 void *states, int *state_count)
390{
391 return tg_cashu_check_proof_states(mint_url, (const tg_cashu_token_t *)token,
392 (tg_cashu_proof_state_t *)states, state_count);
393}
394
395uint64_t tollgate_core_cashu_allotment(uint64_t amount, uint64_t price, uint64_t step_size)
396{
397 return tg_cashu_calculate_allotment(amount, price, step_size);
398}
399
400bool tollgate_core_cashu_is_mint_accepted(const char *mint_url)
401{
402 if (!s_platform) return false;
403 const char *accepted = s_platform->get_mint_url ? s_platform->get_mint_url() : NULL;
404 if (!accepted) return false;
405 if (s_platform->get_accepted_mint_count && s_platform->get_accepted_mint) {
406 int count = s_platform->get_accepted_mint_count();
407 for (int i = 0; i < count; i++) {
408 if (tg_cashu_is_mint_accepted(mint_url, s_platform->get_accepted_mint(i)))
409 return true;
410 }
411 return false;
412 }
413 return tg_cashu_is_mint_accepted(mint_url, accepted);
414}
415
416const char *tollgate_core_cashu_token_mint(const void *token)
417{
418 const tg_cashu_token_t *t = (const tg_cashu_token_t *)token;
419 return t->mint_url;
420}
421
422uint64_t tollgate_core_cashu_token_amount(const void *token)
423{
424 const tg_cashu_token_t *t = (const tg_cashu_token_t *)token;
425 return t->total_amount;
426}
427
428void tollgate_core_mining_update_hashrate(uint32_t client_ip, bool accepted)
429{
430 tg_mining_update_hashrate(client_ip, accepted);
431}
432
433const void *tollgate_core_mining_get_client_stats(uint32_t client_ip)
434{
435 return tg_mining_get_client_stats(client_ip);
436}
437
438double tollgate_core_mining_get_hashprice(void)
439{
440 return tg_mining_get_current_hashprice();
441}
442
443uint64_t tollgate_core_mining_shares_to_allotment_ms(double hashrate, double hashprice,
444 int price, int step_ms)
445{
446 return tg_mining_shares_to_allotment_ms(hashrate, hashprice, price, step_ms);
447}
448
449uint64_t tollgate_core_mining_shares_to_allotment_bytes(double hashrate, double hashprice,
450 int price, int step_bytes)
451{
452 return tg_mining_shares_to_allotment_bytes(hashrate, hashprice, price, step_bytes);
453}
454
455void tollgate_core_mining_set_nbits(uint32_t nbits)
456{
457 tg_mining_set_current_nbits(nbits);
458}
459
348int tollgate_core_dns_start(uint32_t upstream_dns) 460int tollgate_core_dns_start(uint32_t upstream_dns)
349{ 461{
350 (void)upstream_dns; 462 (void)upstream_dns;