21#include "esp_random.h"
34#define TRON_ADDR_USDT ""
37#ifndef TRON_ADDR_USDT_MAIN
38#define TRON_ADDR_USDT_MAIN ""
41#define ADDR_USDC_MAIN ""
44static const char *
const TAG =
"settings";
46#define NS_SETTINGS "settings"
47#define K_BRIGHTNESS "bright"
48#define K_WIFI_SSID "wifi_ssid"
49#define K_WIFI_PASS "wifi_pass"
50#define K_MAX_FEE "max_fee_gw"
51#define K_PRIO_FEE "prio_fee_gw"
52#define K_ADMIN_SALT "adm_salt"
53#define K_ADMIN_HASH "adm_hash"
54#define K_ADMIN_FAILS "adm_fails"
55#define K_CHAIN "chain"
56#define K_MAINNET "mainnet"
58#define K_TZ_OFFSET "tz_off"
59#define K_TZ_DST "tz_dst"
61#define K_TOUCH_X "touch_x"
62#define K_TOUCH_Y "touch_y"
64#define K_BUILD_ID "build_id"
66#error "BUILD_ID is set from git by the project CMakeLists.txt"
69#define K_INFLIGHT "inflight"
71#define K_PAY_ETH "pay_eth"
72#define K_PAY_ETH2 "pay_eth_e"
73#define K_PAY_TRX "pay_trx"
74#define K_PAY_TRX2 "pay_trx_e"
77#define K_CT_ETH "ct_eth"
78#define K_CT_ETH2 "ct_eth_e"
79#define K_CT_TRX "ct_trx"
80#define K_CT_TRX2 "ct_trx_e"
81#define K_CT_ETH_M "ct_eth_m"
82#define K_CT_ETH_M2 "ct_eth_me"
83#define K_CT_TRX_M "ct_trx_m"
84#define K_CT_TRX_M2 "ct_trx_me"
88#define DEFAULT_BRIGHTNESS 80U
91#define WEI_PER_GWEI 1000000000ULL
92#define DEFAULT_MAX_FEE_GWEI (uint32_t)(MAX_FEE / WEI_PER_GWEI)
93#define DEFAULT_PRIORITY_FEE_GWEI (uint32_t)(MAX_PRIORITY_FEE / WEI_PER_GWEI)
105 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
106 (void)nvs_get_u8(h, key, &val);
115 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) == ESP_OK) {
116 (void)nvs_set_u8(h, key, val);
120 ESP_LOGW(
TAG,
"%s: nvs_open failed", key);
128 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
129 (void)nvs_get_u32(h, key, &val);
138 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) == ESP_OK) {
139 (void)nvs_set_u32(h, key, val);
143 ESP_LOGW(
TAG,
"%s: nvs_open failed", key);
192 if (cached >= 0) {
return (cached != 0); }
207 ESP_LOGW(
TAG,
"network set to %s", mainnet ?
"mainnet" :
"testnet");
222 if (pct > 100U) { pct = 100U; }
257#define TOUCH_CAL_DEF_MIN 200U
258#define TOUCH_CAL_DEF_MAX 3800U
261 uint16_t *y_min, uint16_t *y_max)
266 *x_min = (uint16_t)(x >> 16); *x_max = (uint16_t)(x & 0xFFFFU);
267 *y_min = (uint16_t)(y >> 16); *y_max = (uint16_t)(y & 0xFFFFU);
271 uint16_t y_min, uint16_t y_max)
275 const uint16_t MIN_SPAN = 500U;
276 if ((x_max < x_min + MIN_SPAN) || (y_max < y_min + MIN_SPAN)) {
277 ESP_LOGW(
TAG,
"touch cal rejected: span too small");
286 bool present =
false;
288 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
290 size_t len =
sizeof(ssid);
291 present = (nvs_get_str(h,
K_WIFI_SSID, ssid, &len) == ESP_OK) &&
300 if ((ssid == NULL) || (pass == NULL) || (ssid_n == 0U) || (pass_n == 0U)) {
308 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
311 if ((nvs_get_str(h,
K_WIFI_SSID, ssid, &ls) == ESP_OK) &&
312 (nvs_get_str(h,
K_WIFI_PASS, pass, &lp) == ESP_OK) &&
326 if ((ssid == NULL) || (pass == NULL)) {
return; }
328 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) == ESP_OK) {
334 ESP_LOGW(
TAG,
"wifi: nvs_open failed");
341static uint32_t
fee_get(
const char *key, uint32_t dflt)
374 bool present =
false;
376 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
378 present = (nvs_get_blob(h,
K_ADMIN_HASH, NULL, &len) == ESP_OK) &&
387 if (code == NULL) {
return false; }
390 esp_fill_random(salt,
sizeof(salt));
400 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) == ESP_OK) {
401 ok = (nvs_set_blob(h,
K_ADMIN_SALT, salt,
sizeof(salt)) == ESP_OK) &&
402 (nvs_set_blob(h,
K_ADMIN_HASH, hash,
sizeof(hash)) == ESP_OK) &&
404 (nvs_commit(h) == ESP_OK);
406 ESP_LOGI(
TAG,
"admin code set: %s",
ok ?
"ok" :
"FAILED");
408 ESP_LOGW(
TAG,
"admin code: nvs_open failed");
410 CW_Utils::secure_wipe(hash,
sizeof(hash));
416 if (code == NULL) {
return false; }
423 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
424 size_t ls =
sizeof(salt);
425 size_t lh =
sizeof(stored);
426 have = (nvs_get_blob(h,
K_ADMIN_SALT, salt, &ls) == ESP_OK) &&
427 (nvs_get_blob(h,
K_ADMIN_HASH, stored, &lh) == ESP_OK) &&
431 if (!have) {
return false; }
436 CW_Utils::secure_wipe(calc,
sizeof(calc));
443 ESP_LOGW(
TAG,
"admin unlock failed (%u consecutive)", (
unsigned)(n + 1U));
466static bool dual_get(
const char *k_val,
const char *k_echo,
const char *def,
467 const char *what,
char *out,
size_t n)
469 if ((out == NULL) || (n == 0U)) {
return false; }
477 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
478 size_t lv =
sizeof(val);
479 size_t le =
sizeof(echo);
480 stored = (nvs_get_str(h, k_val, val, &lv) == ESP_OK) &&
481 (nvs_get_str(h, k_echo, echo, &le) == ESP_OK);
487 if (stored && !CW_Utils::secure_compare(
reinterpret_cast<const uint8_t *
>(val),
488 reinterpret_cast<const uint8_t *
>(echo),
490 ESP_LOGE(
TAG,
"%s: stored copies disagree - using config.h", what);
494 (void)snprintf(out, n,
"%s", stored ? val : def);
495 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(val),
sizeof(val));
496 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(echo),
sizeof(echo));
501static bool dual_set(
const char *k_val,
const char *k_echo,
bool tron,
502 const char *what,
const char *addr)
511 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) == ESP_OK) {
512 ok = (nvs_set_str(h, k_val, norm) == ESP_OK) &&
513 (nvs_set_str(h, k_echo, norm) == ESP_OK) &&
514 (nvs_commit(h) == ESP_OK);
518 ESP_LOGW(
TAG,
"%s set to %s", what, norm);
520 ESP_LOGE(
TAG,
"%s: NVS write failed", what);
538 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(scratch),
sizeof(scratch));
554 if ((out != NULL) && (n > 0U)) { out[0] =
'\0'; }
560 "contract(tron)", out, n)
563 "contract(eth)", out, n);
573 true,
"contract(tron)", addr)
575 false,
"contract(eth)", addr);
581 uint32_t stored = 0U;
582 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) == ESP_OK) {
590 if (stored == BUILD_ID) {
591 ESP_LOGI(
TAG,
"build %u - settings written by this build, kept",
596 ESP_LOGW(
TAG,
"stamped %u, running build %u - erasing NVS",
597 (
unsigned)stored, (
unsigned)BUILD_ID);
598 esp_err_t err = nvs_flash_erase();
599 if (err == ESP_OK) { err = nvs_flash_init(); }
603 ESP_LOGE(
TAG,
"NVS erase failed (%s) - settings kept", esp_err_to_name(err));
608 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) == ESP_OK) {
609 (void)nvs_set_u32(h,
K_BUILD_ID, (uint32_t)BUILD_ID);
622 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) == ESP_OK) {
625 (void)nvs_erase_all(h);
628 (void)nvs_set_u32(h,
K_BUILD_ID, (uint32_t)BUILD_ID);
631 ESP_LOGW(
TAG,
"settings: factory reset");
637 if (nvs_open(
"prov", NVS_READWRITE, &h) == ESP_OK) {
638 (void)nvs_erase_all(h);
641 ESP_LOGW(
TAG,
"settings: portal namespace cleared");
647 if ((rec == NULL) || (n == 0U)) {
return false; }
649 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) != ESP_OK) {
650 ESP_LOGE(
TAG,
"inflight: nvs_open failed - sale not persisted");
653 const bool ok = (nvs_set_blob(h,
K_INFLIGHT, rec, n) == ESP_OK) &&
654 (nvs_commit(h) == ESP_OK);
656 if (!
ok) { ESP_LOGE(
TAG,
"inflight: write failed - sale not persisted"); }
662 if ((rec == NULL) || (n == 0U)) {
return false; }
664 if (nvs_open(
NS_SETTINGS, NVS_READONLY, &h) != ESP_OK) {
return false; }
666 const esp_err_t err = nvs_get_blob(h,
K_INFLIGHT, rec, &len);
670 return (err == ESP_OK) && (len == n);
676 if (nvs_open(
NS_SETTINGS, NVS_READWRITE, &h) != ESP_OK) {
return; }
678 if (nvs_erase_key(h,
K_INFLIGHT) == ESP_OK) { (void)nvs_commit(h); }
TZ-independent calendar arithmetic and date-string parsing.
#define TRON_ADDR_USDT_MAIN
static const char *const TAG
Original Keccak-256 digest as used by Ethereum.
static esp_err_t ok(httpd_req_t *req, const char *msg)
200 with a plain-text message.
static uint32_t nvs_u32_get(const char *key, uint32_t def)
bool settings_check_admin_code(const char *code)
Check a candidate code, maintaining the failure counter.
static void cache_invalidate(void)
Forget both, so the next read goes back to flash. For the erase paths.
bool settings_has_wifi(void)
true if a Wi-Fi SSID has been stored.
bool settings_get_wifi(char *ssid, size_t ssid_n, char *pass, size_t pass_n)
Read the stored Wi-Fi credentials.
bool settings_set_tz_offset_min(int16_t minutes)
Store the panel clock's UTC offset.
static uint32_t fee_get(const char *key, uint32_t dflt)
bool settings_wipe_if_new_build(void)
Erase NVS unless this exact build is the one that wrote it.
const char * settings_net_str(const char *testnet, const char *mainnet)
Pick the string belonging to the network the terminal is on.
pos_chain_t settings_get_chain(void)
Selected chain, or POS_CHAIN_ETH_USDC if never set.
bool settings_get_contract(pos_chain_t chain, char *out, size_t n)
Read the token-contract address for a network.
static bool dual_set(const char *k_val, const char *k_echo, bool tron, const char *what, const char *addr)
Write a dual-stored address, normalising Ethereum to "0x"-prefixed.
static uint8_t nvs_u8_get(const char *key, uint8_t def)
int16_t settings_get_tz_offset_min(void)
The panel clock's standard (winter) offset from UTC, in minutes east.
static std::atomic< int16_t > s_chain_cache
uint8_t settings_get_tz_dst(void)
The clock's DST rule, a civil_dst_t; CIVIL_DST_NONE when unset.
uint32_t settings_get_priority_fee_gwei(void)
EIP-1559 max priority fee (tip) per gas, in Gwei.
bool settings_set_fees_gwei(uint32_t max_gwei, uint32_t prio_gwei)
Persist the max fee and the tip per gas (Gwei), as a pair.
void settings_set_wifi(const char *ssid, const char *pass)
Persist Wi-Fi credentials (plaintext — see README threat model).
bool settings_get_payout(bool tron, char *out, size_t n)
Read the payout address for a network.
bool settings_set_admin_code(const char *code)
Store a new admin code, with a fresh random salt.
uint8_t settings_get_brightness(void)
Backlight level in percent, or 80 if never set.
static std::atomic< int8_t > s_mainnet_cache
static void nvs_u8_set(const char *key, uint8_t val)
#define DEFAULT_MAX_FEE_GWEI
void settings_set_chain(pos_chain_t chain)
Persist the selected chain.
#define TOUCH_CAL_DEF_MIN
bool settings_inflight_load(void *rec, size_t n)
Read the persisted sale back. false if none, or not n bytes.
#define DEFAULT_PRIORITY_FEE_GWEI
void settings_set_mainnet(bool mainnet)
Persist the production/test network choice.
bool settings_set_contract(pos_chain_t chain, const char *addr)
Persist a token-contract address, value and echo copy.
void settings_get_touch_cal(uint16_t *x_min, uint16_t *x_max, uint16_t *y_min, uint16_t *y_max)
Raw XPT2046 range that maps to the panel's four edges.
bool settings_set_payout(bool tron, const char *addr)
Persist a payout address, writing both the value and its echo copy.
void settings_factory_reset(void)
Erase all stored settings (brightness, auto, Wi-Fi creds, fees).
uint8_t settings_admin_fail_count(void)
Consecutive failed unlock attempts, persisted.
bool settings_set_tz_dst(uint8_t rule)
Store the DST rule.
void settings_set_brightness(uint8_t pct)
Persist the backlight level (0..100).
#define DEFAULT_BRIGHTNESS
bool settings_has_admin_code(void)
true once an admin code exists.
void settings_set_touch_cal(uint16_t x_min, uint16_t x_max, uint16_t y_min, uint16_t y_max)
Persist a calibration. Rejected (and ignored) if an axis is inverted or collapsed — a bad store here ...
bool settings_get_mainnet(void)
true when the terminal is on the production networks.
void settings_inflight_clear(void)
Drop the persisted sale. Writes nothing when there is none.
bool settings_has_payout(bool tron)
Whether an operator has actually set the payout address for a network.
uint32_t settings_get_max_fee_gwei(void)
EIP-1559 max fee per gas, in Gwei.
static void admin_set_fails(uint8_t n)
#define TOUCH_CAL_DEF_MAX
static bool dual_get(const char *k_val, const char *k_echo, const char *def, const char *what, char *out, size_t n)
Read a dual-stored address, falling back to def on any doubt.
bool settings_inflight_save(const void *rec, size_t n)
Persist the sale between broadcast and verdict (opaque record).
static void nvs_u32_set(const char *key, uint32_t val)
Persistent device settings stored in NVS (backlight, Wi-Fi creds).
pos_chain_t
Which chain (and therefore which asset) the terminal charges in.
#define SETTINGS_PAYOUT_MAX
Longest payout address plus NUL — "0x" + 40 hex, or 34 base58 Tron.
What a setting may be, apart from where it is stored.
static void admin_derive(const char *code, const uint8_t *salt, uint8_t out[ADMIN_HASH_LEN])
Derive the stored digest: a single keccak256 over salt || code.
static bool settings_addr_normalise(bool tron, const char *addr, char norm[SETTINGS_PAYOUT_MAX])
The form a payout address or contract is stored in: Tron as given, Ethereum "0x"-prefixed whichever w...
static uint32_t tz_offset_encode(int16_t minutes)
A valid offset in its stored, biased form.
static bool tz_dst_valid(long rule)
true for a DST rule the clock knows.
static uint8_t tz_dst_decode(uint8_t r)
A stored DST rule, or no DST if it is not one the clock knows.
static int16_t tz_offset_decode(uint32_t raw)
The stored form back to minutes; nonsense in NVS is UTC, not a wild clock.
static bool tz_offset_valid(long minutes)
true for an offset the clock accepts (TZ_OFFSET_MIN .. _MAX).
static fee_pair_t fee_pair_check(unsigned long max_gwei, unsigned long prio_gwei)
Check a (max fee, tip) pair in Gwei; the range is checked first.