1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
#include "tollgate_core.h"
#include "tollgate_core_cashu.h"
#include "tollgate_core_dns.h"
#include "tollgate_core_firewall.h"
#include "tollgate_core_session.h"
#include "esp_log.h"
#include "cJSON.h"
#include <string.h>
static const char *TAG = "tg_core";
static const tollgate_platform_t *s_platform;
static esp_ip4_addr_t s_ap_ip;
static uint32_t s_owner_ip;
static uint8_t s_owner_mac[6];
static bool s_owner_connected;
esp_err_t tollgate_core_init(const tollgate_platform_t *platform, esp_ip4_addr_t ap_ip)
{
if (!platform) {
ESP_LOGE(TAG, "Platform callbacks required");
return ESP_FAIL;
}
s_platform = platform;
s_ap_ip = ap_ip;
s_owner_connected = false;
memset(s_owner_mac, 0, sizeof(s_owner_mac));
if (!platform->spend_proofs) {
ESP_LOGW(TAG, "spend_proofs is NULL — double-spend window open until wallet integrated");
}
tollgate_core_session_set_platform(platform);
tollgate_core_session_init();
tollgate_core_fw_init(ap_ip);
ESP_LOGI(TAG, "TollGate core initialized, AP IP=" IPSTR, IP2STR(&ap_ip));
return ESP_OK;
}
esp_err_t tollgate_core_dns_start(esp_ip4_addr_t upstream_dns)
{
return tollgate_core_dns_start_internal(s_ap_ip, upstream_dns);
}
esp_err_t tollgate_core_process_payment(uint32_t client_ip, const char *token_str)
{
if (!s_platform || !token_str) return ESP_FAIL;
const char *accepted_mint = s_platform->get_mint_url ? s_platform->get_mint_url() : NULL;
if (!accepted_mint || accepted_mint[0] == '\0') {
ESP_LOGE(TAG, "No mint URL configured");
return ESP_FAIL;
}
tg_cashu_token_t token;
esp_err_t ret = tollgate_core_cashu_decode_token(token_str, &token);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Token decode failed");
return ESP_FAIL;
}
if (!tollgate_core_cashu_is_mint_accepted(token.mint_url, accepted_mint)) {
ESP_LOGE(TAG, "Token mint '%s' not accepted (expected '%s')", token.mint_url, accepted_mint);
return ESP_FAIL;
}
tg_cashu_proof_state_t states[TG_CASHU_MAX_PROOFS];
int state_count = 0;
ret = tollgate_core_cashu_check_proof_states(token.mint_url, &token, states, &state_count);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Proof state check failed");
return ESP_FAIL;
}
for (int i = 0; i < state_count; i++) {
if (states[i].spent) {
ESP_LOGE(TAG, "Proof %d is SPENT — rejecting", i);
return ESP_FAIL;
}
}
if (s_platform->spend_proofs) {
if (!s_platform->spend_proofs(token_str)) {
ESP_LOGE(TAG, "spend_proofs rejected the token");
return ESP_FAIL;
}
}
const char *metric = s_platform->get_metric ? s_platform->get_metric() : "milliseconds";
uint64_t price = s_platform->get_price_sats ? s_platform->get_price_sats() : 21;
uint64_t step_size;
if (strcmp(metric, "bytes") == 0) {
step_size = s_platform->get_step_bytes ? (uint64_t)s_platform->get_step_bytes() : 22020096;
} else {
step_size = s_platform->get_step_ms ? (uint64_t)s_platform->get_step_ms() : 60000;
}
uint64_t allotment = tollgate_core_cashu_calculate_allotment(token.total_amount, price, step_size);
if (allotment == 0) {
ESP_LOGE(TAG, "Token amount %llu too small for price %llu",
(unsigned long long)token.total_amount, (unsigned long long)price);
return ESP_FAIL;
}
if (strcmp(metric, "bytes") == 0) {
if (!tollgate_core_session_create_bytes(client_ip, allotment)) {
return ESP_FAIL;
}
} else {
if (!tollgate_core_session_create(client_ip, allotment)) {
return ESP_FAIL;
}
}
ESP_LOGI(TAG, "Payment processed: %llu sats → %llu %s allotment for client",
(unsigned long long)token.total_amount, (unsigned long long)allotment, metric);
return ESP_OK;
}
void tollgate_core_client_connected(const uint8_t *mac, uint32_t client_ip)
{
if (!s_owner_connected) {
s_owner_connected = true;
s_owner_ip = client_ip;
if (mac) memcpy(s_owner_mac, mac, 6);
esp_ip4_addr_t ip = { .addr = client_ip };
ESP_LOGI(TAG, "First client = owner: " IPSTR, IP2STR(&ip));
return;
}
ESP_LOGI(TAG, "Client connected (non-owner): " IPSTR, IP2STR(&(esp_ip4_addr_t){.addr=client_ip}));
}
void tollgate_core_client_disconnected(const uint8_t *mac)
{
if (!s_owner_connected) return;
if (mac && memcmp(s_owner_mac, mac, 6) == 0) {
ESP_LOGI(TAG, "Owner disconnected — reassigning");
s_owner_connected = false;
memset(s_owner_mac, 0, sizeof(s_owner_mac));
int fw_count = tollgate_core_fw_client_count();
if (fw_count > 0) {
tg_session_t *sessions = tollgate_core_session_get_array();
for (int i = 0; i < tollgate_core_session_get_array_size(); i++) {
if (sessions[i].active && sessions[i].mac[0] != '\0') {
if (memcmp(sessions[i].mac, mac, 6) != 0) {
s_owner_connected = true;
s_owner_ip = sessions[i].client_ip;
ESP_LOGI(TAG, "New owner: " IPSTR, IP2STR(&(esp_ip4_addr_t){.addr=s_owner_ip}));
break;
}
}
}
}
return;
}
ESP_LOGI(TAG, "Client disconnected");
}
void tollgate_core_tick(void)
{
tollgate_core_session_tick();
}
bool tollgate_core_is_client_allowed(uint32_t client_ip)
{
return tollgate_core_fw_is_allowed(client_ip);
}
bool tollgate_core_is_dns_running(void)
{
return tollgate_core_dns_is_running();
}
char *tollgate_core_get_status_json(void)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddBoolToObject(root, "ownerConnected", s_owner_connected);
cJSON_AddNumberToObject(root, "activeSessions", tollgate_core_session_active_count());
cJSON_AddNumberToObject(root, "allowedClients", tollgate_core_fw_client_count());
cJSON_AddBoolToObject(root, "dnsRunning", tollgate_core_dns_is_running());
char *json = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return json;
}
char *tollgate_core_get_config_json(void)
{
cJSON *root = cJSON_CreateObject();
if (s_platform) {
if (s_platform->get_price_sats)
cJSON_AddNumberToObject(root, "priceSats", s_platform->get_price_sats());
if (s_platform->get_step_ms)
cJSON_AddNumberToObject(root, "stepMs", s_platform->get_step_ms());
if (s_platform->get_mint_url)
cJSON_AddStringToObject(root, "mintUrl", s_platform->get_mint_url());
if (s_platform->get_metric)
cJSON_AddStringToObject(root, "metric", s_platform->get_metric());
if (s_platform->get_step_bytes)
cJSON_AddNumberToObject(root, "stepBytes", s_platform->get_step_bytes());
}
char *json = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return json;
}
int tollgate_core_active_session_count(void)
{
return tollgate_core_session_active_count();
}
int tollgate_core_allowed_client_count(void)
{
return tollgate_core_fw_client_count();
}
bool tollgate_core_is_owner(uint32_t client_ip)
{
return s_owner_connected && s_owner_ip == client_ip;
}
bool tollgate_core_is_owner_connected(void)
{
return s_owner_connected;
}
|