19 bool begin(
unsigned long)
override {
return true; }
20 void print(
const __FlashStringHelper*)
override {}
21 void print(
const char*)
override {}
23 void print(uint8_t,
int)
override {}
24 void print(uint16_t,
int)
override {}
25 void print(uint32_t,
int)
override {}
26 void print(
int,
int)
override {}
28 void println(
const __FlashStringHelper*)
override {}
38#define PN532_I2C_PORT 0
43#define PN532_I2C_HZ 100000U
47#define LED_R GPIO_NUM_4
48#define LED_G GPIO_NUM_16
49#define LED_B GPIO_NUM_17
54#define WIFI_SAVED_ATTEMPTS 3U
55#define TIME_SYNC_ATTEMPTS 3U
59 "Could not join that network - check the password";
61 "No network time - this Wi-Fi has no usable internet";
99 if (xQueueReceive(
s_ui_queue, &msg, portMAX_DELAY) != pdTRUE) {
continue; }
100 got = (msg.
event == want);
115 char ssid[33] = { 0 };
116 char pass[65] = { 0 };
124 ESP_LOGI(
TAG,
"Wi-Fi '%s': attempt %" PRIu32
"/%u",
128 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(pass),
sizeof(pass));
130 ESP_LOGW(
TAG,
"saved network '%s' failed %u times",
159 if (xQueueReceive(
s_ui_queue, &msg, portMAX_DELAY) != pdTRUE) {
continue; }
162 char w_ssid[33] = { 0 };
163 char w_pass[65] = { 0 };
166 w_pass,
sizeof(w_pass)) > 0U) {
177 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(w_pass),
sizeof(w_pass));
178 if (
ok) {
return true; }
212#define BOOT_FAULT_RESTART_S 30U
226 ESP_LOGE(
TAG,
"startup fault - restarting in %u s",
236 ESP_LOGE(
TAG,
"stored Ethereum payout address rejected");
243 ESP_LOGE(
TAG,
"Bad ADDR_TO in config");
269bool run_wizard(CryptnoxWallet &wallet, Pn532NfcTransport &transport,
270 CW_CryptoProvider &crypto,
bool wifi_only)
273 ESP_LOGE(
TAG,
"setup portal unavailable - falling back to the panel");
303 if (xQueueReceive(
s_ui_queue, &msg, portMAX_DELAY) != pdTRUE) {
continue; }
364 char pin[16] = { 0 };
366 char err[48] = { 0 };
369 card_eth,
sizeof(card_eth),
370 card_tron,
sizeof(card_tron),
372 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(pin),
sizeof(pin));
380 prov_set_note(
"Accept each address on the terminal screen.");
383 if (card_eth[0] !=
'\0') {
386 }
else if (card_tron[0] !=
'\0') {
401 if (card_tron[0] !=
'\0') {
410 char w_ssid[33] = { 0 };
411 char w_pass[65] = { 0 };
414 w_pass,
sizeof(w_pass)) > 0U) {
438 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(w_pass),
464 ESP_LOGW(
TAG,
"setup finished - restarting to apply it");
466 vTaskDelay(pdMS_TO_TICKS(600));
498 ESP_LOGI(
TAG,
"===== cryptnox-pos boot =====");
502 (void)gpio_set_direction(p, GPIO_MODE_OUTPUT);
503 (void)gpio_set_level(p, 1);
505#ifdef CRYPTNOX_POS_DEV_BUILD
507 ESP_LOGI(
TAG,
"Build: %s %s", __DATE__, __TIME__);
512 esp_log_level_set(
"pn532", ESP_LOG_INFO);
513 esp_log_level_set(
"pn532_adapter", ESP_LOG_WARN);
514 esp_log_level_set(
"Pn532NfcTransport", ESP_LOG_WARN);
519 esp_log_level_set(
"i2c.master", ESP_LOG_NONE);
523 esp_err_t nvs_ret = nvs_flash_init();
524 if ((nvs_ret == ESP_ERR_NVS_NO_FREE_PAGES) ||
525 (nvs_ret == ESP_ERR_NVS_NEW_VERSION_FOUND)) {
526 ESP_ERROR_CHECK(nvs_flash_erase());
527 nvs_ret = nvs_flash_init();
529 ESP_ERROR_CHECK(nvs_ret);
549 ESP_LOGI(
TAG,
"clock: UTC%+d:%02d, DST rule %u",
555 ESP_LOGI(
TAG,
"networks: %s",
561 bool addr_checksummed =
false;
562 for (
const char *pc =
s_payout_eth + 2; *pc !=
'\0'; ++pc) {
563 if ((*pc >=
'A') && (*pc <=
'F')) { addr_checksummed =
true;
break; }
565 if (!addr_checksummed) {
566 ESP_LOGW(
TAG,
"recipient is all-lowercase: no EIP-55 checksum verified");
572 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(&nfc),
sizeof(nfc));
574 pn532_config_t nfc_cfg;
575 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(&nfc_cfg),
sizeof(nfc_cfg));
576 nfc_cfg.transport = PN532_TRANSPORT_I2C;
586 esp_err_t nfc_ret = pn532_init(&nfc, &nfc_cfg);
587 if (nfc_ret != ESP_OK) {
588 ESP_LOGE(
TAG,
"PN532 bus init failed: %s", esp_err_to_name(nfc_ret));
595 uint32_t nfc_fw = pn532_get_firmware_version(&nfc);
597 ESP_LOGE(
TAG,
"PN532 did not answer GetFirmwareVersion - reader absent?");
600 ESP_LOGI(
TAG,
"PN532 firmware: IC 0x%02X, version %u.%u",
601 (
unsigned)((nfc_fw >> 24) & 0xFFU),
602 (
unsigned)((nfc_fw >> 16) & 0xFFU),
603 (
unsigned)((nfc_fw >> 8) & 0xFFU));
608 (void)logger.
begin(115200UL);
609 static ESP32CryptoProvider cryptoProvider;
614 uint8_t tron_to21[CW_TRON_ADDRESS_BYTES];
615 uint8_t tron_to21_echo[CW_TRON_ADDRESS_BYTES];
620 !CW_Tron::decodeAddress(
s_payout_tron, cryptoProvider, tron_to21)) {
621 ESP_LOGE(
TAG,
"stored Tron payout address rejected - sales refused");
625 if (!CW_Tron::decodeAddress(
s_payout_tron, cryptoProvider, tron_to21) ||
626 !CW_Tron::decodeAddress(
s_payout_tron, cryptoProvider, tron_to21_echo)) {
627 ESP_LOGE(
TAG,
"Bad TRON_ADDR_TO in config");
655 ESP_LOGW(
TAG,
"selected chain has no payout address - switching to %d",
656 static_cast<int>(to));
663 ESP_LOGW(
TAG,
"no payout address configured - running setup");
666 static Pn532NfcTransport nfcTransport(&nfc, logger);
667 static ESP32Platform platform;
668 static CryptnoxWallet wallet(nfcTransport, logger, cryptoProvider, platform);
670 if (!wallet.begin()) {
671 ESP_LOGE(
TAG,
"Wallet begin failed");
690 char greeting[96] =
"";
691 if (fresh_update || wiped) {
693 (void)snprintf(greeting,
sizeof(greeting),
"Updated to %s.%s",
696 wiped ?
" Settings are cleared - set the terminal up again."
705#ifdef TRON_CA_CERT_PEM
713#ifndef RPC_CA_CERT_PEM
714 ESP_LOGW(
TAG,
"TLS: Ethereum RPC not pinned (RPC_CA_CERT_PEM) - full CA bundle");
716#ifndef POLY_CA_CERT_PEM
717 ESP_LOGW(
TAG,
"TLS: Polygon RPC not pinned (POLY_CA_CERT_PEM) - full CA bundle");
719#ifndef TRON_CA_CERT_PEM
720 ESP_LOGW(
TAG,
"TLS: Tron RPC not pinned (TRON_CA_CERT_PEM) - full CA bundle");
743 const bool setup_run = no_code || no_payout;
745 ESP_LOGI(
TAG,
"no admin code - first-run setup");
754 }
else if (no_payout) {
755 ESP_LOGW(
TAG,
"admin code stored but no payout address - setup resumes at "
759 (void)
run_wizard(wallet, nfcTransport, cryptoProvider,
false);
771 bool try_saved =
true;
773 bool offer_setup = !setup_run;
774 const char *net_note = NULL;
784 if (!joined && try_saved && unattended) {
785 ESP_LOGW(
TAG,
"saved network down - coming up offline, re-joining "
786 "in the background");
801 (void)
run_wizard(wallet, nfcTransport, cryptoProvider, configured);
803 ESP_LOGW(
TAG,
"no setup portal - panel Wi-Fi picker");
808 net_note =
"Could not join the saved network";
820 ESP_LOGE(
TAG,
"SNTP time sync failed on this network");
821 if (joined && unattended) {
826 ESP_LOGW(
TAG,
"no network time yet - retrying in the background");
848 for (
int attempt = 0; synced && (attempt < 3) && !rpc_ok; attempt++) {
849 uint64_t boot_nonce = 0U;
852 if (synced && !rpc_ok) {
853 ESP_LOGE(
TAG,
"RPC unreachable or clock rejected at boot - carrying on, "
854 "each sale re-checks");
857 ESP_LOGI(
TAG,
"Ready%s", synced ?
"" :
" (offline - re-joining in the background)");
860 ESP_LOGI(
TAG,
"heap at ready: %u free, %u largest block",
861 (
unsigned)esp_get_free_heap_size(),
862 (
unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT));
865 if (greeting[0] !=
'\0') {
871 return pos_hw_t{ wallet, nfcTransport, cryptoProvider };
bool run_wizard(CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, bool wifi_only)
Run the browser wizard until the operator presses Finish.
#define BOOT_FAULT_RESTART_S
static bool resolve_evm_payout(bool *rejected)
Resolve the EVM payout address into its dual store, at boot.
pos_hw_t pos_boot(void)
Everything before the main loop. Returns the card stack, which lives for the life of the program.
static char s_join_ssid[33]
const char *const NOTE_NO_TIME
const char *const NOTE_JOIN_FAILED
void wifi_keep_or_drop(bool keep)
Persist or discard the pending picker credentials, then scrub them.
void boot_fault(ui_boot_err_t kind, const char *detail)
Show a startup fault, then restart. Does not return.
void wait_for_ui_event(ui_event_t want)
Block until the UI reports want, discarding anything else.
#define TIME_SYNC_ATTEMPTS
static char s_join_pass[65]
#define WIFI_SAVED_ATTEMPTS
bool sync_time(void)
Block on an SNTP sync so TLS certificate validity-period checks run against real time instead of the ...
bool wifi_picker(const char *note)
Run the panel network picker (scan → list → keyboard → connect) until connected.
bool wifi_try_saved(void)
Try the saved credentials, staying on the splash while it happens.
bool card_read_payouts(CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, const char *pin, size_t pin_chars, char *eth_out, size_t eth_n, char *tron_out, size_t tron_n, char *err, size_t err_n)
Read the card's payout addresses and put them through the panel.
bool begin(unsigned long) override
void println(char) override
void print(uint32_t, int) override
void println(uint32_t, int) override
void println(const char *) override
void println(uint8_t, int) override
void println(uint16_t, int) override
void print(char) override
void print(uint16_t, int) override
void print(uint8_t, int) override
void print(int, int) override
void println(int, int) override
void print(const char *) override
void print(const __FlashStringHelper *) override
void println(const __FlashStringHelper *) override
bool eth_addr_parse(const char *hex, uint8_t out[ETH_ADDR_LEN])
Parse a 20-byte Ethereum address from a hex string.
#define ETH_ADDR_LEN
Length of a raw (binary) Ethereum address, in bytes.
bool eth_rpc_get_nonce(uint64_t *nonce_out)
Fetch the confirmed transaction count (nonce) for from_addr.
static const char *const TAG
std::atomic< bool > s_user_cancelled
void ui_event_dispatch(ui_event_t event, uint64_t payload)
UI-task callback: forward a touch event to the main task queue.
void net_wifi_disconnect(void)
Drop the station association, without the retry loop pulling it back.
void net_time_background(void)
Subscribe SNTP without waiting, so the clock is set whenever the network comes back.
void net_wifi_keep_trying(void)
Hold on to the network last configured, even though it is down now.
bool net_time_sync(uint32_t timeout_ms)
Block until the system clock has been set via SNTP and sanity-checked.
bool net_wifi_connect(const char *ssid, const char *password)
Connect to a WiFi network and block until an IP is obtained (up to 30 s). May be called repeatedly to...
uint16_t net_wifi_scan(net_wifi_ap_t *out, uint16_t max)
Scan for nearby access points (blocking).
void net_wifi_init(void)
Bring up the WiFi driver in station mode (idempotent).
const char * ota_running_version(void)
The running firmware's version, from the image header.
bool ota_mark_valid(void)
Confirm the running image, cancelling the rollback armed by the bootloader.
#define OTA_VERSION_SHOWN_MAX
Bytes needed by ota_version_display: the version, a 'v', a NUL.
static const char * ota_version_display(const char *v, char *buf, size_t n)
The display form of a version: 1.0.0 reads as v1.0.0 on screen.
const token_cfg_t TOKEN_CFG[]
void token_load(const token_cfg_t *cfg, CW_CryptoProvider &crypto)
Load one token at boot: the operator's contract if one is set and parses, config.h otherwise.
char s_payout_tron[SETTINGS_PAYOUT_MAX]
const size_t TOKEN_CFG_COUNT
void ui_refresh_addresses(void)
Implemented by main: repoint those two rows at the selected chain.
char s_payout_eth[SETTINGS_PAYOUT_MAX]
void eth_rpc_select(void)
Private to the application files (main, boot, card_io, pay, pay_evm, pay_tron): the state they share ...
static bool chain_is_tron(void)
true when the operator has switched the terminal to Tron.
void prov_set_step(prov_step_t step)
Tell the portal which wizard step is current.
prov_step_t prov_step(void)
The current step.
void prov_set_note(const char *note)
Put a one-line message on the page.
void prov_set_wifi_only(void)
Cut the wizard down to the Wi-Fi step, with no admin code.
bool prov_propose(prov_ask_t kind, const char *addr)
Propose a value on behalf of the panel itself.
static esp_err_t ok(httpd_req_t *req, const char *msg)
200 with a plain-text message.
bool prov_authed(void)
Whether the browser session is authorised to change anything.
prov_mode_t prov_mode(void)
Which mode is running, or PROV_MODE_OFF.
void prov_stop(void)
Stop the portal, drop the AP, and withdraw anything unaccepted.
void prov_set_scan(const net_wifi_ap_t *aps, uint16_t n)
Hand the portal a Wi-Fi scan for the browser to choose from.
bool prov_start(prov_mode_t mode, ui_event_cb_t cb)
Raise the portal.
prov_step_t
Where the wizard has got to.
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_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.
int16_t settings_get_tz_offset_min(void)
The panel clock's standard (winter) offset from UTC, in minutes east.
uint8_t settings_get_tz_dst(void)
The clock's DST rule, a civil_dst_t; CIVIL_DST_NONE when unset.
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.
void settings_set_chain(pos_chain_t chain)
Persist the selected chain.
bool settings_has_admin_code(void)
true once an admin code exists.
bool settings_get_mainnet(void)
true when the terminal is on the production networks.
bool settings_has_payout(bool tron)
Whether an operator has actually set the payout address for a network.
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.
A scanned access point (subset of fields the UI needs).
The card stack pos_boot() brought up; lives for the program.
void tron_rpc_set_ca_cert(const char *ca_pem)
Optional: pin the Tron endpoint's TLS certificate.
void tron_rpc_init(const char *base_url)
Set the Tron HTTP API base URL (no trailing slash).
void ui_set_prov_note(const char *msg)
Put a failure line on the setup screen, in red under the step title.
void ui_show_prov(int step)
Show the setup screen: QR code, AP name and passphrase.
void ui_show_prov_auth(void)
Demand the admin code so a browser can be authorised.
void ui_show_welcome(const char *sub)
Greet the operator at the start of first-run setup, or after an update.
void ui_set_boot_status(const char *step)
Set the one-line progress note on the splash screen.
void ui_show_wifi_list(const net_wifi_ap_t *aps, uint16_t n, const char *note)
Show the scanned Wi-Fi networks for the user to pick from.
void ui_show_card_pin(void)
Ask for the card PIN before reading an address off a Cryptnox card.
void ui_init(ui_event_cb_t cb)
Initialise display + touch and start the UI task.
size_t ui_take_wifi_creds(char *ssid, size_t ssid_n, char *pass, size_t pass_n)
Fetch the selected SSID + entered password and wipe the UI's copy.
void ui_show_prov_confirm(void)
Raise the modal that asks the operator to accept a value a browser proposed, reading the pending prop...
void ui_show_admin_set(void)
Run the first-run admin-code creation (enter, then confirm).
void ui_show_splash(void)
Switch to the splash screen.
void ui_show_wifi_connecting(const char *ssid)
Show a "Connecting to <ssid>…" screen while main associates.
void ui_show_boot_error(ui_boot_err_t kind, const char *detail)
Show a startup fault, naming the cause and what to do about it.
size_t ui_take_pin(char *out, size_t n)
Copy the most recently entered PIN out and wipe the UI's copy.
ui_event_t
Events emitted by the UI task towards the main task.
@ UI_EVENT_PROV_VALUE_SET
ui_boot_err_t
Startup faults shown on UI_SCREEN_BOOT_ERROR.