From 0642900ae44d84cfe24abe6911dbed4cd31ab8ad Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 23 May 2026 06:24:33 +0530 Subject: Phase 8a-8b: extract beacon_price and market into component - tollgate_core_beacon.c/h: pure logic (hash_mint, hash_npub, build_ie) with platform-agnostic tollgate_beacon_config_t input struct - tollgate_core_market.c/h: market table logic (parse_ie, find_cheapest, update_ssid) with platform callbacks for time - main/beacon_price.c: thin shim delegates to component, WiFi IE start/stop - main/market.h: typedef aliases to component types - All 21 unit tests pass, ESP-IDF build passes - Hardware smoke test: 5/6 pass (AP, portal, grant/revoke, firewall) - Market scanner discovers other TollGates via vendor IEs on hardware --- .../tollgate_core/src/tollgate_core_beacon.c | 48 ++++++++ .../tollgate_core/src/tollgate_core_beacon.h | 53 +++++++++ .../tollgate_core/src/tollgate_core_market.c | 121 +++++++++++++++++++++ .../tollgate_core/src/tollgate_core_market.h | 49 +++++++++ 4 files changed, 271 insertions(+) create mode 100644 components/tollgate_core/src/tollgate_core_beacon.c create mode 100644 components/tollgate_core/src/tollgate_core_beacon.h create mode 100644 components/tollgate_core/src/tollgate_core_market.c create mode 100644 components/tollgate_core/src/tollgate_core_market.h (limited to 'components/tollgate_core/src') diff --git a/components/tollgate_core/src/tollgate_core_beacon.c b/components/tollgate_core/src/tollgate_core_beacon.c new file mode 100644 index 0000000..927aa03 --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_beacon.c @@ -0,0 +1,48 @@ +#include "tollgate_core_beacon.h" +#include "mbedtls/sha256.h" +#include + +void tollgate_core_beacon_hash_mint(const char *mint_url, uint8_t hash_out[4]) +{ + uint8_t full_hash[32]; + mbedtls_sha256((const unsigned char *)mint_url, strlen(mint_url), full_hash, 0); + memcpy(hash_out, full_hash, 4); +} + +void tollgate_core_beacon_hash_npub(const char *npub_hex, uint8_t hash_out[4]) +{ + uint8_t full_hash[32]; + mbedtls_sha256((const unsigned char *)npub_hex, strlen(npub_hex), full_hash, 0); + memcpy(hash_out, full_hash, 4); +} + +void tollgate_core_beacon_build_ie(const tollgate_beacon_config_t *cfg, tollgate_price_ie_t *ie) +{ + memset(ie, 0, sizeof(*ie)); + ie->element_id = TOLLGATE_IE_ELEMENT_ID; + ie->length = 4 + TOLLGATE_IE_PAYLOAD_SIZE; + ie->vendor_oui[0] = TOLLGATE_OUI_0; + ie->vendor_oui[1] = TOLLGATE_OUI_1; + ie->vendor_oui[2] = TOLLGATE_OUI_2; + ie->vendor_oui_type = TOLLGATE_IE_TYPE; + + tollgate_price_payload_t *p = &ie->payload; + p->version = TOLLGATE_IE_VERSION; + p->metric = (strcmp(cfg->metric, "bytes") == 0) ? 1 : 0; + p->price_per_step = (uint16_t)cfg->price_per_step; + + bool is_bytes = (strcmp(cfg->metric, "bytes") == 0); + p->step_size = is_bytes ? (uint32_t)cfg->step_size_bytes : (uint32_t)cfg->step_size_ms; + + tollgate_core_beacon_hash_mint(cfg->mint_url, p->mint_hash); + + p->geohash_len = (uint8_t)strnlen(cfg->geohash, TOLLGATE_IE_GEOHASH_MAX); + memcpy(p->geohash, cfg->geohash, p->geohash_len); + if (p->geohash_len < TOLLGATE_IE_GEOHASH_MAX) { + memset(p->geohash + p->geohash_len, 0, TOLLGATE_IE_GEOHASH_MAX - p->geohash_len); + } + + if (cfg->identity_initialized && cfg->npub_hex) { + tollgate_core_beacon_hash_npub(cfg->npub_hex, p->npub_hash); + } +} diff --git a/components/tollgate_core/src/tollgate_core_beacon.h b/components/tollgate_core/src/tollgate_core_beacon.h new file mode 100644 index 0000000..27ad3fe --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_beacon.h @@ -0,0 +1,53 @@ +#ifndef TOLLGATE_CORE_BEACON_H +#define TOLLGATE_CORE_BEACON_H + +#include +#include + +#define TOLLGATE_OUI_0 0xC0 +#define TOLLGATE_OUI_1 0xFF +#define TOLLGATE_OUI_2 0xEE +#define TOLLGATE_IE_TYPE 0x01 +#define TOLLGATE_IE_VERSION 1 +#define TOLLGATE_IE_ELEMENT_ID 0xDD + +#define TOLLGATE_IE_GEOHASH_MAX 9 + +typedef struct __attribute__((packed)) { + uint8_t version; + uint8_t metric; + uint16_t price_per_step; + uint32_t step_size; + uint8_t mint_hash[4]; + uint8_t geohash_len; + char geohash[TOLLGATE_IE_GEOHASH_MAX]; + uint8_t npub_hash[4]; +} tollgate_price_payload_t; + +#define TOLLGATE_IE_PAYLOAD_SIZE sizeof(tollgate_price_payload_t) +#define TOLLGATE_IE_TOTAL_SIZE (6 + TOLLGATE_IE_PAYLOAD_SIZE) + +typedef struct __attribute__((packed)) { + uint8_t element_id; + uint8_t length; + uint8_t vendor_oui[3]; + uint8_t vendor_oui_type; + tollgate_price_payload_t payload; +} tollgate_price_ie_t; + +typedef struct { + const char *mint_url; + const char *metric; + int price_per_step; + int step_size_ms; + int step_size_bytes; + const char *geohash; + const char *npub_hex; + bool identity_initialized; +} tollgate_beacon_config_t; + +void tollgate_core_beacon_hash_mint(const char *mint_url, uint8_t hash_out[4]); +void tollgate_core_beacon_hash_npub(const char *npub_hex, uint8_t hash_out[4]); +void tollgate_core_beacon_build_ie(const tollgate_beacon_config_t *cfg, tollgate_price_ie_t *ie); + +#endif diff --git a/components/tollgate_core/src/tollgate_core_market.c b/components/tollgate_core/src/tollgate_core_market.c new file mode 100644 index 0000000..8530212 --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_market.c @@ -0,0 +1,121 @@ +#include "tollgate_core_market.h" +#include + +static tollgate_market_t s_market; +static tollgate_market_get_time_ms_fn s_get_time_ms; + +static int find_entry_by_bssid(const uint8_t bssid[6]) +{ + for (int i = 0; i < TG_MARKET_MAX_ENTRIES; i++) { + if (s_market.entries[i].valid && memcmp(s_market.entries[i].bssid, bssid, 6) == 0) { + return i; + } + } + return -1; +} + +static int find_free_slot(void) +{ + for (int i = 0; i < TG_MARKET_MAX_ENTRIES; i++) { + if (!s_market.entries[i].valid) return i; + } + int oldest = 0; + int64_t oldest_time = s_market.entries[0].discovered_ms; + for (int i = 1; i < TG_MARKET_MAX_ENTRIES; i++) { + if (s_market.entries[i].discovered_ms < oldest_time) { + oldest_time = s_market.entries[i].discovered_ms; + oldest = i; + } + } + return oldest; +} + +void tollgate_core_market_init(tollgate_market_get_time_ms_fn get_time_ms) +{ + memset(&s_market, 0, sizeof(s_market)); + s_get_time_ms = get_time_ms; +} + +void tollgate_core_market_parse_ie(const uint8_t sa[6], const tollgate_vendor_ie_t *ie, int rssi, + const uint8_t *self_npub_hash) +{ + if (!ie || ie->payload_len < TOLLGATE_IE_PAYLOAD_SIZE) return; + + static const uint8_t oui[3] = { TOLLGATE_OUI_0, TOLLGATE_OUI_1, TOLLGATE_OUI_2 }; + if (memcmp(ie->vendor_oui, oui, 3) != 0) return; + if (ie->vendor_oui_type != TOLLGATE_IE_TYPE) return; + + const tollgate_price_payload_t *payload = (const tollgate_price_payload_t *)ie->payload; + if (payload->version != TOLLGATE_IE_VERSION) return; + + if (self_npub_hash && memcmp(payload->npub_hash, self_npub_hash, 4) == 0) return; + + int idx = find_entry_by_bssid(sa); + if (idx < 0) { + idx = find_free_slot(); + if (s_market.count < TG_MARKET_MAX_ENTRIES) s_market.count++; + } + + tollgate_market_entry_t *entry = &s_market.entries[idx]; + memcpy(entry->bssid, sa, 6); + entry->rssi = (int8_t)rssi; + entry->price_per_step = payload->price_per_step; + entry->step_size = payload->step_size; + entry->metric = payload->metric; + memcpy(entry->mint_hash, payload->mint_hash, 4); + memcpy(entry->npub_hash, payload->npub_hash, 4); + + uint8_t gh_len = payload->geohash_len; + if (gh_len > TOLLGATE_IE_GEOHASH_MAX) gh_len = TOLLGATE_IE_GEOHASH_MAX; + memcpy(entry->geohash, payload->geohash, gh_len); + entry->geohash[gh_len] = '\0'; + + entry->discovered_ms = s_get_time_ms ? s_get_time_ms() : 0; + entry->valid = true; + entry->ssid[0] = '\0'; +} + +int tollgate_core_market_find_cheapest(const tollgate_market_t *market) +{ + int cheapest = -1; + uint32_t best_eff_price = UINT32_MAX; + + for (int i = 0; i < TG_MARKET_MAX_ENTRIES; i++) { + if (!market->entries[i].valid) continue; + if (market->entries[i].ssid[0] == '\0') continue; + + uint32_t step = market->entries[i].step_size; + if (step == 0) continue; + + uint32_t eff; + if (market->entries[i].metric == 0) { + eff = (uint32_t)market->entries[i].price_per_step * 60000 / step; + } else { + eff = (uint32_t)market->entries[i].price_per_step * 1048576 / step; + } + + if (eff < best_eff_price) { + best_eff_price = eff; + cheapest = i; + } + } + return cheapest; +} + +const tollgate_market_t *tollgate_core_market_get(void) +{ + return &s_market; +} + +void tollgate_core_market_update_ssid(const uint8_t bssid[6], const char *ssid, int8_t rssi) +{ + for (int i = 0; i < TG_MARKET_MAX_ENTRIES; i++) { + if (!s_market.entries[i].valid) continue; + if (memcmp(s_market.entries[i].bssid, bssid, 6) == 0) { + memcpy(s_market.entries[i].ssid, ssid, 32); + s_market.entries[i].ssid[32] = '\0'; + s_market.entries[i].rssi = rssi; + break; + } + } +} diff --git a/components/tollgate_core/src/tollgate_core_market.h b/components/tollgate_core/src/tollgate_core_market.h new file mode 100644 index 0000000..a89a1db --- /dev/null +++ b/components/tollgate_core/src/tollgate_core_market.h @@ -0,0 +1,49 @@ +#ifndef TOLLGATE_CORE_MARKET_H +#define TOLLGATE_CORE_MARKET_H + +#include "tollgate_core_beacon.h" +#include +#include +#include + +#define TG_MARKET_MAX_ENTRIES 10 + +typedef struct { + uint8_t bssid[6]; + char ssid[33]; + int8_t rssi; + uint16_t price_per_step; + uint32_t step_size; + uint8_t metric; + uint8_t mint_hash[4]; + uint8_t npub_hash[4]; + char geohash[TOLLGATE_IE_GEOHASH_MAX + 1]; + int64_t discovered_ms; + bool valid; +} tollgate_market_entry_t; + +typedef struct { + tollgate_market_entry_t entries[TG_MARKET_MAX_ENTRIES]; + int count; + int64_t last_scan_ms; + bool scanning; + int consecutive_failures; +} tollgate_market_t; + +typedef struct { + const uint8_t *vendor_oui; + uint8_t vendor_oui_type; + const uint8_t *payload; + size_t payload_len; +} tollgate_vendor_ie_t; + +typedef int64_t (*tollgate_market_get_time_ms_fn)(void); + +void tollgate_core_market_init(tollgate_market_get_time_ms_fn get_time_ms); +void tollgate_core_market_parse_ie(const uint8_t sa[6], const tollgate_vendor_ie_t *ie, int rssi, + const uint8_t *self_npub_hash); +int tollgate_core_market_find_cheapest(const tollgate_market_t *market); +const tollgate_market_t *tollgate_core_market_get(void); +void tollgate_core_market_update_ssid(const uint8_t bssid[6], const char *ssid, int8_t rssi); + +#endif -- cgit v1.2.3