diff options
| author | Your Name <you@example.com> | 2026-05-19 02:31:19 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-19 02:32:41 +0530 |
| commit | 81f2dc52dc42d01c89dff45a5407ec40b8863052 (patch) | |
| tree | 15018c2438639ca89dc6d33a5144c10d0b1c2af0 /main/config.c | |
| parent | 75688d55b3c8d13c8c9a50da9668ec408f684cb3 (diff) | |
feat: local Nostr relay with relay selection, sync, and integration tests
Local Nostr relay (NIP-01) on port 4869 with LittleFS 4MB storage.
All events published locally first, then synced to public relays via REQ-diff.
Relay selection via NIP-11 HTTP probing with NIP-77 scoring and auto-failover.
Components:
- wisp_relay: 16-file local relay (ws_server, storage_engine, sub_manager,
broadcaster, relay_validator, router, handlers, rate_limiter, nip11,
deletion, flash_monitor, relay_types)
- esp_littlefs: LittleFS VFS integration (git submodule)
- negentropy: for future NIP-77 binary sync (git submodule)
New source files:
- local_relay.c/h: thin wrapper for relay init/start/publish
- relay_selector.c/h: NIP-11 probe + scoring + auto-failover
- sync_manager.c/h: REQ-diff sync (primary 30min, fallback 6h)
Bug fixes:
- config.c: use-after-free (cJSON_Delete before seed_relays/sync parsing)
- local_relay: moved init to app_main for boot-time start (not gated on STA IP)
Flash layout: 4MB LittleFS partition at 0x500000 for relay_store
Test results (Board B, live hardware):
- Smoke: ping + HTTP 4869 + NIP-11: PASS
- NIP-11 info document: 10/11 PASS
- WS pub/sub (connect, REQ/EOSE, EVENT/OK, CLOSE, concurrent): 6/6 PASS
- Unit tests (relay_validator + relay_selector): 13/13 PASS
Hardware test make targets in physical-router-test-automation/:
- make relay-build, relay-flash-b, relay-test-smoke/nip11/pubsub/sync/full
Diffstat (limited to 'main/config.c')
| -rw-r--r-- | main/config.c | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/main/config.c b/main/config.c index 9dd2a1d..b991991 100644 --- a/main/config.c +++ b/main/config.c | |||
| @@ -35,6 +35,8 @@ esp_err_t tollgate_config_init(void) | |||
| 35 | g_config.payout.mint_count = 0; | 35 | g_config.payout.mint_count = 0; |
| 36 | g_config.cvm_enabled = true; | 36 | g_config.cvm_enabled = true; |
| 37 | strncpy(g_config.cvm_relays, "wss://relay.primal.net", sizeof(g_config.cvm_relays) - 1); | 37 | strncpy(g_config.cvm_relays, "wss://relay.primal.net", sizeof(g_config.cvm_relays) - 1); |
| 38 | g_config.nostr_sync_interval_s = 1800; | ||
| 39 | g_config.nostr_fallback_sync_interval_s = 21600; | ||
| 38 | 40 | ||
| 39 | esp_vfs_spiffs_conf_t conf = { | 41 | esp_vfs_spiffs_conf_t conf = { |
| 40 | .base_path = "/spiffs", | 42 | .base_path = "/spiffs", |
| @@ -257,6 +259,28 @@ esp_err_t tollgate_config_init(void) | |||
| 257 | g_config.payout.mint_count = 1; | 259 | g_config.payout.mint_count = 1; |
| 258 | } | 260 | } |
| 259 | 261 | ||
| 262 | cJSON *seed_relays = cJSON_GetObjectItem(root, "nostr_seed_relays"); | ||
| 263 | if (seed_relays && cJSON_IsArray(seed_relays)) { | ||
| 264 | int srcount = cJSON_GetArraySize(seed_relays); | ||
| 265 | if (srcount > TOLLGATE_MAX_SEED_RELAYS) srcount = TOLLGATE_MAX_SEED_RELAYS; | ||
| 266 | for (int i = 0; i < srcount; i++) { | ||
| 267 | cJSON *r = cJSON_GetArrayItem(seed_relays, i); | ||
| 268 | if (r && cJSON_IsString(r)) { | ||
| 269 | strncpy(g_config.nostr_seed_relays[i], r->valuestring, | ||
| 270 | sizeof(g_config.nostr_seed_relays[i]) - 1); | ||
| 271 | g_config.nostr_seed_relay_count++; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | cJSON *sync_interval = cJSON_GetObjectItem(root, "nostr_sync_interval_s"); | ||
| 277 | if (sync_interval) g_config.nostr_sync_interval_s = sync_interval->valueint; | ||
| 278 | |||
| 279 | cJSON *fallback_interval = cJSON_GetObjectItem(root, "nostr_fallback_sync_interval_s"); | ||
| 280 | if (fallback_interval) g_config.nostr_fallback_sync_interval_s = fallback_interval->valueint; | ||
| 281 | |||
| 282 | cJSON_Delete(root); | ||
| 283 | |||
| 260 | if (g_config.payout.recipient_count == 0) { | 284 | if (g_config.payout.recipient_count == 0) { |
| 261 | strncpy(g_config.payout.recipients[0].lightning_address, "TollGate@coinos.io", | 285 | strncpy(g_config.payout.recipients[0].lightning_address, "TollGate@coinos.io", |
| 262 | sizeof(g_config.payout.recipients[0].lightning_address) - 1); | 286 | sizeof(g_config.payout.recipients[0].lightning_address) - 1); |
| @@ -264,14 +288,24 @@ esp_err_t tollgate_config_init(void) | |||
| 264 | g_config.payout.recipient_count = 1; | 288 | g_config.payout.recipient_count = 1; |
| 265 | } | 289 | } |
| 266 | 290 | ||
| 267 | cJSON_Delete(root); | ||
| 268 | |||
| 269 | if (g_config.nostr_relay_count == 0) { | 291 | if (g_config.nostr_relay_count == 0) { |
| 270 | strncpy(g_config.nostr_relays[0], "wss://relay.damus.io", sizeof(g_config.nostr_relays[0]) - 1); | 292 | strncpy(g_config.nostr_relays[0], "wss://relay.damus.io", sizeof(g_config.nostr_relays[0]) - 1); |
| 271 | strncpy(g_config.nostr_relays[1], "wss://nos.lol", sizeof(g_config.nostr_relays[1]) - 1); | 293 | strncpy(g_config.nostr_relays[1], "wss://nos.lol", sizeof(g_config.nostr_relays[1]) - 1); |
| 272 | g_config.nostr_relay_count = 2; | 294 | g_config.nostr_relay_count = 2; |
| 273 | } | 295 | } |
| 274 | 296 | ||
| 297 | if (g_config.nostr_seed_relay_count == 0) { | ||
| 298 | strncpy(g_config.nostr_seed_relays[0], "wss://relay.orangesync.tech", | ||
| 299 | sizeof(g_config.nostr_seed_relays[0]) - 1); | ||
| 300 | strncpy(g_config.nostr_seed_relays[1], "wss://relay.damus.io", | ||
| 301 | sizeof(g_config.nostr_seed_relays[1]) - 1); | ||
| 302 | strncpy(g_config.nostr_seed_relays[2], "wss://nos.lol", | ||
| 303 | sizeof(g_config.nostr_seed_relays[2]) - 1); | ||
| 304 | strncpy(g_config.nostr_seed_relays[3], "wss://relay.nostr.band", | ||
| 305 | sizeof(g_config.nostr_seed_relays[3]) - 1); | ||
| 306 | g_config.nostr_seed_relay_count = 4; | ||
| 307 | } | ||
| 308 | |||
| 275 | ESP_LOGI(TAG, "Config loaded: nsec=%s...%s, %d WiFi networks, price=%d sats/%dms", | 309 | ESP_LOGI(TAG, "Config loaded: nsec=%s...%s, %d WiFi networks, price=%d sats/%dms", |
| 276 | g_config.nsec, g_config.nsec + 60, g_config.network_count, | 310 | g_config.nsec, g_config.nsec + 60, g_config.network_count, |
| 277 | g_config.price_per_step, g_config.step_size_ms); | 311 | g_config.price_per_step, g_config.step_size_ms); |