cryptnox-pos 1.0.0
Standalone USDC payment terminal firmware (ESP32 + Cryptnox smart card)
Loading...
Searching...
No Matches
pos_app.h
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: LGPL-3.0-or-later
3 * Copyright (c) 2026 Cryptnox SA
4 */
5
12
13#ifndef POS_APP_H
14#define POS_APP_H
15
16#include <stdio.h>
17#include <string.h>
18#include <sys/time.h> /* gettimeofday */
19#include <strings.h> /* strcasecmp */
20#include <stdlib.h>
21#include <inttypes.h>
22#include <atomic>
23
24#include "freertos/FreeRTOS.h"
25#include "freertos/task.h"
26#include "freertos/queue.h"
27#include "driver/spi_master.h"
28#include "driver/gpio.h"
29#include "esp_err.h"
30#include "esp_heap_caps.h"
31#include "esp_log.h"
32#include "esp_system.h"
33#include "esp_timer.h"
34#include "nvs_flash.h"
35
36#include "CryptnoxWallet.h"
37#include "CW_Utils.h"
38#include "Pn532NfcTransport.h"
39#include "ESP32Logger.h"
40#include "ESP32Platform.h"
41#include "esp32_crypto_provider.h"
42#include "CW_Tron.h"
43#include "settings.h"
44#include "assets.h"
45#include "provision.h"
46#include "ota.h"
47#include "ota_version.h"
48#include "wdt.h"
49
50extern "C" {
51#include "pn532.h"
52#include "keccak256.h"
53#include "eth_addr.h"
54#include "eth_sig.h"
55#include "card_status.h"
56#include "hardening.h"
57#include "eth_rlp.h"
58#include "eth_rpc.h"
59#include "rpc_error.h"
60#include "tron_rpc.h"
61#include "tron_tx.h"
62#include "net.h"
63#include "ui.h"
64}
65
66#include "money.h"
67#include "config_defaults.h"
68
69static const char *const TAG = "cryptnox_pos";
70
71/* ── Selection ───────────────────────────────────────────────── */
72
74static inline bool chain_is_tron(void) {
76}
77
79static inline bool chain_is_polygon(void) {
81}
82
84static inline bool chain_is_native_evm(void) {
86}
87
88/* ── What a sale is paid with and to (pay.cpp) ───────────────── */
89
98typedef struct {
101 bool ok;
102 bool checked;
103} token_t;
104
105/* Where each token's contract comes from in config.h, both deployments. EVM
106 * values without the "0x", Tron in base58. USDC on Ethereum and USDT on Tron can
107 * also be set from the config page (settings_get_contract), which wins. */
108typedef struct {
110 const char *test;
111 const char *main;
112 const char *name;
114
115extern const uint8_t ETH_DERIVE_PATH[20];
119extern const token_cfg_t TOKEN_CFG[];
120extern const size_t TOKEN_CFG_COUNT;
121extern pos_addr_t s_dest;
123extern bool s_payout_bad[2];
124
126void token_load(const token_cfg_t *cfg, CW_CryptoProvider &crypto);
127bool token_decimals_ok(pos_chain_t chain, char *err, size_t err_max);
128const pos_addr_t *active_dest(void);
129
130/* The fees one sale offers, read ONCE when its confirm screen is built and used
131 * for the cap shown, the balance check and the signed transaction. Read
132 * separately, a fee changed from the config page in between would make them
133 * disagree, and the check could pass a sale the signed tx cannot pay for. */
134typedef struct {
135 uint64_t max_fee; /* wei per gas */
136 uint64_t prio_fee; /* wei per gas */
137} sale_fee_t;
138
140void sale_fee_text(char *out, size_t n);
141
142/* ── The sale between broadcast and verdict (pay.cpp) ────────── */
143
144/* What a broadcast attempt ended as. UNKNOWN: the node never answered, so the
145 * transaction may be on its way. Only the chain can settle it, and until it does
146 * the sale is Unconfirmed — never declined, because a declined sale is one the
147 * merchant takes again. */
153
154/* Everything the receipt poll needs, kept here so "Check again" on the
155 * Unconfirmed screen resumes the same sale. Persisted as-is to NVS
156 * (inflight_persist), so its layout is the record's. */
157typedef struct {
158 bool active;
159 bool tron;
160 char hash[72]; /* EVM: "0x"+64, hashed HERE from the signed
161 bytes; Tron: the txID of verified raw_data */
162 uint8_t to[ETH_ADDR_LEN]; /* EVM: the tx's `to` (contract or payee) */
163 uint8_t payee[ETH_ADDR_LEN]; /* EVM token: the Transfer recipient */
164 bool token; /* EVM: an ERC-20 call, not a native send */
165 uint64_t amount; /* EVM token: the Transfer value */
166 uint64_t expiration_ms; /* Tron: after this it can never land */
167 bool broadcast_known; /* false: the broadcast answer was lost */
168 bool polygon; /* EVM: which endpoint the receipt is on */
169 pos_amount_t decided; /* for the final decision gate */
170} inflight_t;
171
173void inflight_persist(const inflight_t *fl);
174uint64_t wall_ms(void);
175void settle_inflight(void);
176
181bcast_t pay_sign_and_broadcast(CryptnoxWallet &wallet, Pn532NfcTransport &transport,
182 CW_CryptoProvider &crypto, const pos_amount_t *amount,
183 const char *pin, size_t pin_chars, inflight_t *fl,
184 char *err_out, size_t err_max);
185
186/* ── EVM (pay_evm.cpp) ───────────────────────────────────────── */
187
188void eth_rpc_select_for(bool polygon);
189void eth_rpc_select(void);
190void evm_fees_wei(bool polygon, uint64_t *max_fee, uint64_t *prio_fee);
191bool evm_balance_ok(const pos_amount_t *amount, char *err, size_t err_max);
192bcast_t sign_and_broadcast(CryptnoxWallet &wallet, Pn532NfcTransport &transport,
193 const pos_amount_t *amount, const pos_addr_t *to,
194 const char *pin, size_t pin_chars, inflight_t *fl,
195 char *err_out, size_t err_max);
196
197/* ── Tron (pay_tron.cpp) ─────────────────────────────────────── */
198
199void tron_addr_to_hex(const uint8_t *addr21, char *out, size_t n);
200bcast_t sign_and_broadcast_tron(CryptnoxWallet &wallet, Pn532NfcTransport &transport,
201 CW_CryptoProvider &crypto, const pos_amount_t *amount,
202 const pos_addr_t *to, const token_t *token,
203 const char *pin, size_t pin_chars, inflight_t *fl,
204 char *err_out, size_t err_max);
206
207/* ── The card (card_io.cpp) ──────────────────────────────────── */
208
215struct WipeGuard {
216 uint8_t *buf;
217 size_t len;
218 explicit WipeGuard(uint8_t *b, size_t n) : buf(b), len(n) {}
219 ~WipeGuard() { CW_Utils::secure_wipe(buf, len); }
220 WipeGuard(const WipeGuard &) = delete;
221 WipeGuard &operator=(const WipeGuard &) = delete;
222};
223
224extern const char *s_card_fault;
225const char *pin_fail_text(Pn532NfcTransport &transport, const char *wrong);
226bool card_connect(CryptnoxWallet &wallet, Pn532NfcTransport &transport,
227 CW_SecureSession &session, bool setup = false);
228bool card_sign(CryptnoxWallet &wallet, CW_SecureSession &session,
229 const uint8_t *hash, uint8_t hash_len,
230 const uint8_t *path, uint8_t path_len,
231 const char *pin, size_t pin_chars,
232 uint8_t rs_out[64], char *err_out, size_t err_max);
233bool card_read_payouts(CryptnoxWallet &wallet, Pn532NfcTransport &transport,
234 CW_CryptoProvider &crypto, const char *pin, size_t pin_chars,
235 char *eth_out, size_t eth_n, char *tron_out, size_t tron_n,
236 char *err, size_t err_n);
237
238/* ── UI ↔ main task (main.cpp) ───────────────────────────────── */
239
240typedef struct {
242 uint64_t payload;
243} ui_msg_t;
244
245extern QueueHandle_t s_ui_queue;
246extern std::atomic<bool> s_user_cancelled;
247void ui_event_dispatch(ui_event_t event, uint64_t payload);
248
249/* ── Bring-up (boot.cpp) ─────────────────────────────────────── */
250
252struct pos_hw_t {
253 CryptnoxWallet &wallet;
254 Pn532NfcTransport &transport;
255 CW_CryptoProvider &crypto;
256};
257
258pos_hw_t pos_boot(void);
259
260extern const char *const NOTE_JOIN_FAILED;
261extern const char *const NOTE_NO_TIME;
262[[noreturn]] void boot_fault(ui_boot_err_t kind, const char *detail);
263void wifi_keep_or_drop(bool keep);
265bool wifi_try_saved(void);
266bool wifi_picker(const char *note);
267bool run_wizard(CryptnoxWallet &wallet, Pn532NfcTransport &transport,
268 CW_CryptoProvider &crypto, bool wifi_only);
269bool sync_time(void);
270
271#endif /* POS_APP_H */
What each selectable asset IS — one row per pos_chain_t.
static bool pos_chain_is_native_evm(pos_chain_t c)
true for ETH and POL: no contract, the amount goes in value, 21000 gas, 18 decimals....
Definition assets.h:183
static bool pos_chain_is_tron(pos_chain_t c)
true for the Tron selections; every other one is EVM.
Definition assets.h:165
static bool pos_chain_is_polygon(pos_chain_t c)
true for the Polygon selections. EVM like Ethereum, but its own endpoint, chain id and contracts.
Definition assets.h:174
const char *const NOTE_NO_TIME
Definition boot.cpp:60
const char *const NOTE_JOIN_FAILED
Definition boot.cpp:58
const char * s_card_fault
Definition card_io.cpp:18
What a tapped card is missing, read from its SELECT response.
config.h, plus a default for every key added after the first config.h files shipped — so an older con...
Hex Ethereum-address parsing — a pure, dependency-free unit so it can be unit-tested and fuzzed on th...
#define ETH_ADDR_LEN
Length of a raw (binary) Ethereum address, in bytes.
Definition eth_addr.h:25
Minimal RLP encoder for EIP-1559 (type 2) Ethereum transactions.
static const char *const TAG
Definition eth_rpc.cpp:33
Ethereum JSON-RPC client over HTTPS (nonce / ecrecover parity / raw-tx broadcast / receipt polling)....
eth_rpc_receipt_result_t
Outcome of one eth_getTransactionReceipt poll.
Definition eth_rpc.h:36
The recovery bit of a secp256k1 signature, computed on the device.
Decision-integrity primitives (see docs/HARDENING.md §3, §4).
Original Keccak-256 digest as used by Ethereum.
QueueHandle_t s_ui_queue
Definition main.cpp:23
std::atomic< bool > s_user_cancelled
Definition main.cpp:27
The sale's arithmetic: keypad cents, base units, wei, fees, calldata.
Network bring-up: Wi-Fi station (init/scan/connect/RSSI) and SNTP time sync. No application-protocol ...
Firmware slot handling: receive an image into the idle slot, verify it, and install it only once some...
Dotted version comparison, for deciding whether an update goes forwards or backwards.
const token_cfg_t TOKEN_CFG[]
Definition pay.cpp:32
char s_payout_tron[SETTINGS_PAYOUT_MAX]
Definition pay.cpp:28
const size_t TOKEN_CFG_COUNT
Definition pay.cpp:41
sale_fee_t s_sale_fee
Definition pay.cpp:192
const uint8_t ETH_DERIVE_PATH[20]
Definition pay.cpp:16
pos_addr_t s_dest
Definition pay.cpp:177
pos_addr_t s_tron_dest
Definition pay.cpp:185
inflight_t s_inflight
Definition pay.cpp:219
bool s_payout_bad[2]
Definition pay.cpp:182
char s_payout_eth[SETTINGS_PAYOUT_MAX]
Definition pay.cpp:27
token_t s_token[POS_CHAIN__COUNT]
Definition pay.cpp:30
bool run_wizard(CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, bool wifi_only)
Run the browser wizard until the operator presses Finish.
Definition boot.cpp:269
bool card_sign(CryptnoxWallet &wallet, CW_SecureSession &session, const uint8_t *hash, uint8_t hash_len, const uint8_t *path, uint8_t path_len, const char *pin, size_t pin_chars, uint8_t rs_out[64], char *err_out, size_t err_max)
Have the card sign hash, then close the session.
Definition card_io.cpp:160
static bool chain_is_tron(void)
true when the operator has switched the terminal to Tron.
Definition pos_app.h:74
void eth_rpc_select_for(bool polygon)
Point eth_rpc at the endpoint for the selected EVM network.
Definition pay_evm.cpp:26
bcast_t
Definition pos_app.h:148
@ BCAST_UNKNOWN
Definition pos_app.h:151
@ BCAST_FAILED
Definition pos_app.h:149
@ BCAST_SENT
Definition pos_app.h:150
eth_rpc_receipt_result_t tron_receipt_as_eth(tron_receipt_t r)
Map a Tron receipt onto the Ethereum verdicts the UI flow uses.
Definition pay_tron.cpp:309
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.
Definition pay.cpp:84
bool evm_balance_ok(const pos_amount_t *amount, char *err, size_t err_max)
Refuse an EVM sale the tapped card cannot fund, before it signs.
Definition pay_evm.cpp:91
void eth_rpc_select(void)
Definition pay_evm.cpp:51
uint64_t wall_ms(void)
Unix time in ms, 0 while the clock is unset.
Definition pay.cpp:237
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.
Definition card_io.cpp:233
bcast_t sign_and_broadcast_tron(CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, const pos_amount_t *amount, const pos_addr_t *to, const token_t *token, const char *pin, size_t pin_chars, inflight_t *fl, char *err_out, size_t err_max)
Sign a Tron transfer on the card and broadcast it — TRX or TRC-20.
Definition pay_tron.cpp:125
pos_hw_t pos_boot(void)
Everything before the main loop. Returns the card stack, which lives for the life of the program.
Definition boot.cpp:496
bool token_decimals_ok(pos_chain_t chain, char *err, size_t err_max)
Before the first sale in an operator-set token, read its decimals() and refuse anything but 6.
Definition pay.cpp:115
token_t * active_token(pos_chain_t chain=settings_get_chain())
The selection's token, or NULL for a native coin.
Definition pay.cpp:45
bcast_t sign_and_broadcast(CryptnoxWallet &wallet, Pn532NfcTransport &transport, const pos_amount_t *amount, const pos_addr_t *to, const char *pin, size_t pin_chars, inflight_t *fl, char *err_out, size_t err_max)
Sign an EVM token or coin transfer on the card and broadcast it.
Definition pay_evm.cpp:165
bcast_t pay_sign_and_broadcast(CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_CryptoProvider &crypto, const pos_amount_t *amount, const char *pin, size_t pin_chars, inflight_t *fl, char *err_out, size_t err_max)
Sign and broadcast the reconciled sale on whichever family is selected. The family is read once,...
Definition pay.cpp:347
void evm_fees_wei(bool polygon, uint64_t *max_fee, uint64_t *prio_fee)
The EIP-1559 fees one EVM sale will offer, in wei per gas.
Definition pay_evm.cpp:64
bool card_connect(CryptnoxWallet &wallet, Pn532NfcTransport &transport, CW_SecureSession &session, bool setup=false)
Wait for a card and open a secure channel, cancellable from the UI.
Definition card_io.cpp:79
void sale_fee_text(char *out, size_t n)
The most network fee the customer's card can be charged on top of the sale, for the confirm screen....
Definition pay.cpp:201
void wifi_keep_or_drop(bool keep)
Persist or discard the pending picker credentials, then scrub them.
Definition boot.cpp:77
void boot_fault(ui_boot_err_t kind, const char *detail)
Show a startup fault, then restart. Does not return.
Definition boot.cpp:223
void settle_inflight(void)
Poll the in-flight sale for up to 120 s and show what the chain says.
Definition pay.cpp:255
void inflight_persist(const inflight_t *fl)
Write the sale to NVS just before it leaves the terminal.
Definition pay.cpp:228
const pos_addr_t * active_dest(void)
The reconciled recipient for the chain currently selected.
Definition pay.cpp:188
static bool chain_is_polygon(void)
true when the terminal is charging on Polygon rather than Ethereum.
Definition pos_app.h:79
void tron_addr_to_hex(const uint8_t *addr21, char *out, size_t n)
Format a raw 21-byte Tron address as the "41..." hex the API wants.
Definition pay_tron.cpp:25
static bool chain_is_native_evm(void)
true when charging in the network's own coin (ETH / POL), not a token.
Definition pos_app.h:84
void wait_for_ui_event(ui_event_t want)
Block until the UI reports want, discarding anything else.
Definition boot.cpp:94
bool sync_time(void)
Block on an SNTP sync so TLS certificate validity-period checks run against real time instead of the ...
Definition boot.cpp:483
bool wifi_picker(const char *note)
Run the panel network picker (scan → list → keyboard → connect) until connected.
Definition boot.cpp:151
const char * pin_fail_text(Pn532NfcTransport &transport, const char *wrong)
Why verifyPin said no: the PIN, or the card leaving the field.
Definition card_io.cpp:52
void ui_event_dispatch(ui_event_t event, uint64_t payload)
UI-task callback: forward a touch event to the main task queue.
Definition main.cpp:39
bool wifi_try_saved(void)
Try the saved credentials, staying on the splash while it happens.
Definition boot.cpp:113
The config portal: one web app for setting a terminal up and for administering it afterwards,...
Turn a node's JSON-RPC refusal into a line an operator can act on.
pos_chain_t settings_get_chain(void)
Selected chain, or POS_CHAIN_ETH_USDC if never set.
Definition settings.cpp:165
Persistent device settings stored in NVS (backlight, Wi-Fi creds).
pos_chain_t
Which chain (and therefore which asset) the terminal charges in.
Definition settings.h:33
@ POS_CHAIN__COUNT
Definition settings.h:43
#define SETTINGS_PAYOUT_MAX
Longest payout address plus NUL — "0x" + 40 hex, or 34 base58 Tron.
Definition settings.h:214
WipeGuard & operator=(const WipeGuard &)=delete
~WipeGuard()
Definition pos_app.h:219
uint8_t * buf
Definition pos_app.h:216
size_t len
Definition pos_app.h:217
WipeGuard(uint8_t *b, size_t n)
Definition pos_app.h:218
WipeGuard(const WipeGuard &)=delete
bool tron
Definition pos_app.h:159
char hash[72]
Definition pos_app.h:160
uint8_t to[ETH_ADDR_LEN]
Definition pos_app.h:162
uint8_t payee[ETH_ADDR_LEN]
Definition pos_app.h:163
bool active
Definition pos_app.h:158
bool polygon
Definition pos_app.h:168
uint64_t expiration_ms
Definition pos_app.h:166
pos_amount_t decided
Definition pos_app.h:169
bool token
Definition pos_app.h:164
uint64_t amount
Definition pos_app.h:165
bool broadcast_known
Definition pos_app.h:167
The card stack pos_boot() brought up; lives for the program.
Definition pos_app.h:252
CryptnoxWallet & wallet
Definition pos_app.h:253
CW_CryptoProvider & crypto
Definition pos_app.h:255
Pn532NfcTransport & transport
Definition pos_app.h:254
uint64_t max_fee
Definition pos_app.h:135
uint64_t prio_fee
Definition pos_app.h:136
const char * main
Definition pos_app.h:111
const char * name
Definition pos_app.h:112
pos_chain_t chain
Definition pos_app.h:109
const char * test
Definition pos_app.h:110
A token's contract, dual-stored.
Definition pos_app.h:98
pos_addr_t addr
Definition pos_app.h:100
bool ok
Definition pos_app.h:101
bool checked
Definition pos_app.h:102
char str[SETTINGS_PAYOUT_MAX]
Definition pos_app.h:99
ui_event_t event
Definition pos_app.h:241
uint64_t payload
Definition pos_app.h:242
Tron HTTP API client (Nile testnet): build a TRX transfer, broadcast it signed, poll its receipt.
tron_receipt_t
Outcome of one gettransactioninfobyid poll.
Definition tron_rpc.h:47
Tron transaction hex helpers — protobuf varints, the TransferContract integrity check and the signed-...
Touchscreen UI API: screens, events and transaction states for the CYD (ILI9341 + XPT2046) payment fl...
ui_event_t
Events emitted by the UI task towards the main task.
Definition ui.h:55
ui_boot_err_t
Startup faults shown on UI_SCREEN_BOOT_ERROR.
Definition ui.h:113
Task-watchdog feed that is safe to call from any task.