From f6666165d8507b674039c2a05e2012bc43ff7a02 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 14 Jul 2025 17:06:47 +0000 Subject: [PATCH 01/15] added ui layout for export xpub --- main/button_events.h | 1 + main/process/dashboard.c | 4 ++++ main/ui/dashboard.c | 15 +++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/main/button_events.h b/main/button_events.h index 7a24058d..c8b5fce5 100644 --- a/main/button_events.h +++ b/main/button_events.h @@ -172,6 +172,7 @@ typedef enum { BTN_SETTINGS_USBSTORAGE_HELP, BTN_SETTINGS_USBSTORAGE_SIGN, BTN_SETTINGS_USBSTORAGE_EXPORT, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB, BTN_SETTINGS_USBSTORAGE_EXIT, #endif BTN_SETTINGS_BLE, diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 9f47d917..5a410652 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -182,6 +182,7 @@ gui_activity_t* make_unlocked_settings_activity(void); gui_activity_t* make_wallet_settings_activity(void); gui_activity_t* make_device_settings_activity(void); gui_activity_t* make_usbstorage_settings_activity(bool unlocked); + gui_activity_t* make_authentication_activity(bool initialised_and_pin_unlocked); gui_activity_t* make_prefs_settings_activity(bool initialised_and_locked, gui_view_node_t** qr_mode_network_item); gui_activity_t* make_display_settings_activity(void); @@ -2385,6 +2386,9 @@ static void handle_settings(const bool startup_menu) case BTN_SETTINGS_USBSTORAGE_EXPORT: // FIXME: implement break; + case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB: + //FIXME: implement create make_usbstorage_export_xpub_activity() + break; #endif case BTN_SETTINGS_OTP_VIEW: handle_view_otps(); diff --git a/main/ui/dashboard.c b/main/ui/dashboard.c index ae4dc9c8..9ad43c3d 100644 --- a/main/ui/dashboard.c +++ b/main/ui/dashboard.c @@ -349,10 +349,25 @@ gui_activity_t* make_usbstorage_settings_activity(const bool unlocked) { .txt = "Firmware Upgrade", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_FW }, { .txt = "Sign", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_SIGN }, //{ .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT }, + { .txt = "Export Xpub", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB }, }; return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, unlocked ? 2 : 1); } + +gui_activity_t* make_usbstorage_export_xpub_activity(void) +{ +//FIXME: implement activity + btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXIT }, + { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } }; + + btn_data_t menubtns[] = { + { .txt = "implement me", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_FW }, + }; + + return make_menu_activity("Export Xpub", hdrbtns, 2, menubtns, 1); +} + #endif gui_activity_t* make_device_settings_activity(void) From be049447d426a4190375a057e11e9a70f178275c Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Sat, 19 Jul 2025 15:04:36 +0000 Subject: [PATCH 02/15] added xpub options menu --- main/button_events.h | 2 ++ main/process/dashboard.c | 15 +++++++++++---- main/ui/dashboard.c | 25 ++++++++++++++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/main/button_events.h b/main/button_events.h index c8b5fce5..c259c6af 100644 --- a/main/button_events.h +++ b/main/button_events.h @@ -173,6 +173,8 @@ typedef enum { BTN_SETTINGS_USBSTORAGE_SIGN, BTN_SETTINGS_USBSTORAGE_EXPORT, BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION, BTN_SETTINGS_USBSTORAGE_EXIT, #endif BTN_SETTINGS_BLE, diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 5a410652..48a4daef 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -182,6 +182,8 @@ gui_activity_t* make_unlocked_settings_activity(void); gui_activity_t* make_wallet_settings_activity(void); gui_activity_t* make_device_settings_activity(void); gui_activity_t* make_usbstorage_settings_activity(bool unlocked); +gui_activity_t* make_usbstorage_export_xpub_activity(void); +gui_activity_t* make_usbstorage_export_xpub_options_activity(void); gui_activity_t* make_authentication_activity(bool initialised_and_pin_unlocked); gui_activity_t* make_prefs_settings_activity(bool initialised_and_locked, gui_view_node_t** qr_mode_network_item); @@ -2383,11 +2385,16 @@ static void handle_settings(const bool startup_menu) act = make_usbstorage_settings_activity(keychain_get()); break; - case BTN_SETTINGS_USBSTORAGE_EXPORT: - // FIXME: implement - break; case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB: - //FIXME: implement create make_usbstorage_export_xpub_activity() + act = make_usbstorage_export_xpub_activity(); + break; + + case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS: + act = make_usbstorage_export_xpub_options_activity(); + break; + + case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION: + //FIXME: implement break; #endif case BTN_SETTINGS_OTP_VIEW: diff --git a/main/ui/dashboard.c b/main/ui/dashboard.c index 9ad43c3d..0f597226 100644 --- a/main/ui/dashboard.c +++ b/main/ui/dashboard.c @@ -307,7 +307,9 @@ gui_activity_t* make_locked_settings_activity(void) btn_data_t menubtns[] = { { .txt = "BIP39 Passphrase", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_BIP39_PASSPHRASE }, { .txt = "Device", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_DEVICE }, - { .txt = "Temporary Signer", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_TEMPORARY_WALLET_LOGIN } }; + { .txt = "Temporary Signer", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_TEMPORARY_WALLET_LOGIN }, + { .txt = "USB Storage", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE }, + }; return make_menu_activity("Options", hdrbtns, 2, menubtns, sizeof(menubtns) / sizeof(btn_data_t)); } @@ -352,22 +354,35 @@ gui_activity_t* make_usbstorage_settings_activity(const bool unlocked) { .txt = "Export Xpub", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB }, }; - return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, unlocked ? 2 : 1); + return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, 3); } gui_activity_t* make_usbstorage_export_xpub_activity(void) { -//FIXME: implement activity btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXIT }, { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } }; btn_data_t menubtns[] = { - { .txt = "implement me", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_FW }, + { .txt = "Options", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, + { .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION}, }; - return make_menu_activity("Export Xpub", hdrbtns, 2, menubtns, 1); + return make_menu_activity("Export Xpub", hdrbtns, 2, menubtns, 2); } +gui_activity_t* make_usbstorage_export_xpub_options_activity(void) +{ + btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_XPUB_OPTIONS}, + { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } }; + + btn_data_t menubtns[] = { + { .txt = "Script: ", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, + { .txt = "Wallet: ", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, + { .txt = "Account Index: ", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, + }; + + return make_menu_activity("Export Xpub", hdrbtns, 2, menubtns, 3); +} #endif gui_activity_t* make_device_settings_activity(void) From 511e0ffafd645e039f80bf24f40f69f4c24b4dbc Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 21 Jul 2025 19:22:04 +0000 Subject: [PATCH 03/15] exporting xpub functionality all inside usbmode.c --- components/libwally-core/upstream | 2 +- main/process/dashboard.c | 3 ++- main/ui/dashboard.c | 2 +- main/usbhmsc/usbmode.c | 36 +++++++++++++++++++++++++++++++ main/usbhmsc/usbmode.h | 3 +++ 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/components/libwally-core/upstream b/components/libwally-core/upstream index 8fc41b9d..12ab3fa2 160000 --- a/components/libwally-core/upstream +++ b/components/libwally-core/upstream @@ -1 +1 @@ -Subproject commit 8fc41b9d2b152868953176176cff9890aa268c8d +Subproject commit 12ab3fa22cbdc26040a9900d02cdb03da5164c85 diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 48a4daef..1981d1ce 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -2386,7 +2386,8 @@ static void handle_settings(const bool startup_menu) break; case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB: - act = make_usbstorage_export_xpub_activity(); + usbstorage_export_xpub(NULL); + act = make_usbstorage_settings_activity(NULL); break; case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS: diff --git a/main/ui/dashboard.c b/main/ui/dashboard.c index 0f597226..6d321f97 100644 --- a/main/ui/dashboard.c +++ b/main/ui/dashboard.c @@ -372,7 +372,7 @@ gui_activity_t* make_usbstorage_export_xpub_activity(void) gui_activity_t* make_usbstorage_export_xpub_options_activity(void) { - btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_XPUB_OPTIONS}, + btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } }; btn_data_t menubtns[] = { diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index a0449500..dbf70fd2 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -902,4 +902,40 @@ bool usbstorage_sign_psbt(const char* extra_path) const usbstorage_action_context_t ctx = { .extra_path = extra_path }; return handle_usbstorage_action("Sign PSBT", sign_usb_psbt, &ctx, is_async); } + +static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { + JADE_ASSERT(ctx); + + char outpath[MAX_FILENAME_SIZE]; + int len = snprintf(outpath, sizeof(outpath), + "%s/test-xpub.txt", USBSTORAGE_MOUNT_POINT); + JADE_ASSERT(len > 0 && len < sizeof(outpath)); + + const char payload[] = "Hello, World!"; + const size_t payload_len = sizeof(payload) - 1; + size_t written = write_buffer_to_file(outpath, + (const uint8_t*)payload, + payload_len); + + if (written != payload_len) { + const char* msg[] = { "Failed to save", "xpub file" }; + await_error_activity(msg, 2); + return false; + } + + size_t mountlen = strlen(USBSTORAGE_MOUNT_POINT); + const char* msg[] = { + "Wrote xpub to:", + outpath + mountlen + 1 + }; + await_message_activity(msg, 2); + return true; +} + +bool usbstorage_export_xpub(const char* extra_path) { + const bool is_async = false; + const usbstorage_action_context_t ctx = { .extra_path = extra_path, .ctx = NULL }; + return handle_usbstorage_action("Export Xpub", export_usb_xpub_fn, &ctx, is_async); +} + #endif // AMALGAMATED_BUILD diff --git a/main/usbhmsc/usbmode.h b/main/usbhmsc/usbmode.h index 1c0bae9a..4c1f3382 100644 --- a/main/usbhmsc/usbmode.h +++ b/main/usbhmsc/usbmode.h @@ -8,5 +8,8 @@ bool usbstorage_firmware_ota(const char* extra_path); // Sign PSBT file, and write updated file bool usbstorage_sign_psbt(const char* extra_path); +// Write xpub file to usb +bool usbstorage_export_xpub(const char* extra_path); + #endif /* USBMODE_H_ */ From 5a7459a569f8b38e8a63194db6872f819b119e64 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 21 Jul 2025 20:12:04 +0000 Subject: [PATCH 04/15] added test xpub export --- main/usbhmsc/usbmode.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index dbf70fd2..5032505b 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -905,19 +905,34 @@ bool usbstorage_sign_psbt(const char* extra_path) static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { JADE_ASSERT(ctx); + uint32_t path[EXPORT_XPUB_PATH_LEN]; + size_t path_len = 0; + script_variant_t script_variant = P2PKH; + uint16_t account_index = 0; + wallet_get_default_xpub_export_path(script_variant, + account_index, + path, + EXPORT_XPUB_PATH_LEN, + &path_len); + + char *xpub = NULL; + network_t network_id = 0x02; + if (!wallet_get_xpub(network_id, path, path_len, &xpub) || xpub == NULL) { + //await_error_activity((const char*[]){"Unable to derive xpub"}, 1); + return false; + } char outpath[MAX_FILENAME_SIZE]; int len = snprintf(outpath, sizeof(outpath), "%s/test-xpub.txt", USBSTORAGE_MOUNT_POINT); JADE_ASSERT(len > 0 && len < sizeof(outpath)); - - const char payload[] = "Hello, World!"; - const size_t payload_len = sizeof(payload) - 1; + int xpub_len = strlen(xpub); + size_t written = write_buffer_to_file(outpath, - (const uint8_t*)payload, - payload_len); + (const uint8_t*)xpub, + xpub_len); - if (written != payload_len) { + if (written != xpub_len) { const char* msg[] = { "Failed to save", "xpub file" }; await_error_activity(msg, 2); return false; From 814d50ea67e75118251829d7d21676eb4c9acc0c Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 21 Jul 2025 23:33:22 +0000 Subject: [PATCH 05/15] added export wallet descriptor --- main/usbhmsc/usbmode.c | 65 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index 5032505b..b3fcc235 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -903,11 +903,23 @@ bool usbstorage_sign_psbt(const char* extra_path) return handle_usbstorage_action("Sign PSBT", sign_usb_psbt, &ctx, is_async); } +static const char* _desc_func_for_variant(script_variant_t v) { + switch (v) { + case P2PKH: return "pkh("; + case P2WPKH: return "wpkh("; + case P2WPKH_P2SH: return "sh(wpkh("; + case P2TR: return "tr("; + default: return ""; + } +} + static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { JADE_ASSERT(ctx); uint32_t path[EXPORT_XPUB_PATH_LEN]; size_t path_len = 0; + // get script variant from user script_variant_t script_variant = P2PKH; + // get account index from user uint16_t account_index = 0; wallet_get_default_xpub_export_path(script_variant, account_index, @@ -916,23 +928,64 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { &path_len); char *xpub = NULL; - network_t network_id = 0x02; + + network_t network_id; + + if (keychain_get_network_type_restriction() == NETWORK_TYPE_TEST) { + network_id = NETWORK_BITCOIN_TESTNET; + } else { + network_id = NETWORK_BITCOIN; + } + if (!wallet_get_xpub(network_id, path, path_len, &xpub) || xpub == NULL) { //await_error_activity((const char*[]){"Unable to derive xpub"}, 1); return false; } + // get fingerprint + // then take the whole string and put it together with + // scriptvariant([fingerprint/84'/0'accountindex]xpub/0/*) + uint8_t user_fingerprint[BIP32_KEY_FINGERPRINT_LEN]; + wallet_get_fingerprint(user_fingerprint, sizeof(user_fingerprint)); + + char* fphex = NULL; + wally_hex_from_bytes(user_fingerprint, sizeof(user_fingerprint), &fphex); + map_string(fphex, toupper); + + char pathstr[64] = {0}; + char elem[16]; + for (size_t i = 0; i < path_len; i++) { + uint32_t idx = path[i] & ~0x80000000; + bool hard = (path[i] & 0x80000000) != 0; + if (i) strlcat(pathstr, "/", sizeof(pathstr)); + snprintf(elem, sizeof(elem), "%u%s", (unsigned)idx, hard ? "'" : ""); + strlcat(pathstr, elem, sizeof(pathstr)); + } + + char desc[512]; + const char* wrap = _desc_func_for_variant(script_variant); + const bool is_sh_wpkh = (script_variant == P2WPKH_P2SH); + if (is_sh_wpkh) { + // remember to close two parentheses + snprintf(desc, sizeof(desc), + "%s[%s/%s]%s/0/*))", + wrap, fphex, pathstr, xpub); + } else { + snprintf(desc, sizeof(desc), + "%s[%s/%s]%s/0/*)", + wrap, fphex, pathstr, xpub); + } + size_t dlen = strlen(desc); char outpath[MAX_FILENAME_SIZE]; int len = snprintf(outpath, sizeof(outpath), - "%s/test-xpub.txt", USBSTORAGE_MOUNT_POINT); + "%s/wallet.desc", USBSTORAGE_MOUNT_POINT); JADE_ASSERT(len > 0 && len < sizeof(outpath)); - int xpub_len = strlen(xpub); size_t written = write_buffer_to_file(outpath, - (const uint8_t*)xpub, - xpub_len); + (const uint8_t*)desc, + dlen); - if (written != xpub_len) { + if (written != dlen) { const char* msg[] = { "Failed to save", "xpub file" }; await_error_activity(msg, 2); return false; From ed2f4e7aa302ef6a14c8c5f926f3e538aaf61b18 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Tue, 22 Jul 2025 22:25:50 +0000 Subject: [PATCH 06/15] added options flow --- main/button_events.h | 4 + main/usbhmsc/usbmode.c | 211 +++++++++++++++++++++++++++++++---------- 2 files changed, 165 insertions(+), 50 deletions(-) diff --git a/main/button_events.h b/main/button_events.h index c259c6af..2d4cdbfb 100644 --- a/main/button_events.h +++ b/main/button_events.h @@ -175,6 +175,10 @@ typedef enum { BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB, BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS, BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_VARIANT, + BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACCOUNT, + BTN_SETTINGS_EXPORT_XPUB_BACK, + BTN_SETTINGS_EXPORT_XPUB_EXIT, BTN_SETTINGS_USBSTORAGE_EXIT, #endif BTN_SETTINGS_BLE, diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index b3fcc235..104ff25a 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -37,6 +37,9 @@ int sign_psbt(const network_t network_id, struct wally_psbt* psbt, const char** #define MIN_PSBT_FILE_SIZE 32 #define MAX_PSBT_FILE_SIZE MAX_INPUT_MSG_SIZE +#define VARIANT_COUNT 8 +#define MAX_ACCOUNTS 3 + static const char FW_SUFFIX[] = "_fw.bin"; static const char HASH_SUFFIX[] = ".hash"; @@ -61,6 +64,11 @@ typedef struct { void* ctx; } usbstorage_action_context_t; +typedef struct { + script_variant_t variant; + uint16_t account; +} export_xpub_ctx_t; + // Function/action to call on a usb-storage directory typedef bool (*usbstorage_action_fn_t)(const usbstorage_action_context_t* ctx); @@ -897,46 +905,149 @@ static bool sign_usb_psbt(const usbstorage_action_context_t* ctx) // After any signatures are added, the file is written in the same format. bool usbstorage_sign_psbt(const char* extra_path) { - // extra_path is optional const bool is_async = false; const usbstorage_action_context_t ctx = { .extra_path = extra_path }; return handle_usbstorage_action("Sign PSBT", sign_usb_psbt, &ctx, is_async); } -static const char* _desc_func_for_variant(script_variant_t v) { - switch (v) { - case P2PKH: return "pkh("; - case P2WPKH: return "wpkh("; - case P2WPKH_P2SH: return "sh(wpkh("; - case P2TR: return "tr("; - default: return ""; +static const char* _desc_func_for_variant(script_variant_t v) +{ + const char *s = get_script_variant_string(v); + if (!s) return ""; + + const char *p = strchr(s, '('); + if (!p) return ""; + size_t len = p - s + 1; + static char buf[32]; + if (len >= sizeof(buf)) return ""; + memcpy(buf, s, len); + buf[len] = '\0'; + return buf; +} + +static gui_activity_t* make_export_xpub_prompt_activity(void) { + const char* message[] = { + "Save wallet details to", + "connected storage device?" + }; + + btn_data_t ftrbtns[] = { + { .txt = "Options", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS, .borders = GUI_BORDER_TOPRIGHT }, + { .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION, .borders = GUI_BORDER_TOPLEFT } + }; + return make_show_message_activity( + message, 2, + "Export Xpub", + NULL, 0, + ftrbtns, 2 + ); +} + +static gui_activity_t* make_export_xpub_options_activity( + export_xpub_ctx_t* opts, + gui_view_node_t** variant_item, + gui_view_node_t** account_item) +{ + + btn_data_t hdr[] = { + {.txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_EXPORT_XPUB_BACK}, + {.txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } + }; + + + gui_make_text(variant_item, "", TFT_WHITE); + gui_set_align(*variant_item, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); + gui_make_text(account_item, "", TFT_WHITE); + gui_set_align(*account_item, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); + + btn_data_t menu[] = { + { .content = *variant_item, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_VARIANT}, + { .content = *account_item, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACCOUNT}, + }; + gui_activity_t* act = make_menu_activity("xpub options", hdr, 2, menu, 2); + + gui_update_text(*variant_item, _desc_func_for_variant(opts->variant)); + { + char buf[16]; + snprintf(buf, sizeof(buf), "Acct %u", opts->account); + gui_update_text(*account_item, buf); } + + return act; } static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { - JADE_ASSERT(ctx); + JADE_ASSERT(ctx); + export_xpub_ctx_t default_opts = { .variant = P2PKH, .account = 0 }; + export_xpub_ctx_t* opts = ctx->ctx ? (export_xpub_ctx_t*)ctx->ctx : &default_opts; + + while (true) { + gui_activity_t* prompt = make_export_xpub_prompt_activity(); + gui_set_current_activity(prompt); + + int32_t ev; + gui_activity_wait_event(prompt, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev, NULL, 0); + + if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS) { + gui_view_node_t *var_item = NULL, *acct_item = NULL; + gui_activity_t* opts_act = make_export_xpub_options_activity( + opts, &var_item, &acct_item + ); + gui_set_current_activity(opts_act); + + while (true) { + gui_activity_wait_event( + opts_act, + GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, + &ev, NULL, 0 + ); + + switch (ev) { + case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_VARIANT: + opts->variant = (opts->variant + 1) % VARIANT_COUNT; + gui_update_text(var_item, _desc_func_for_variant(opts->variant)); + break; + case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACCOUNT: + opts->account = (opts->account + 1) % MAX_ACCOUNTS; + { + char buf[16]; + snprintf(buf, sizeof(buf), "Acct %u", opts->account); + gui_update_text(acct_item, buf); + } + break; + case BTN_SETTINGS_EXPORT_XPUB_BACK: + goto PROMPT_LOOP; + } + } + } + else if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION) { + + goto DO_EXPORT; + } + else { + return false; + } + +PROMPT_LOOP: ; + } + +DO_EXPORT: uint32_t path[EXPORT_XPUB_PATH_LEN]; size_t path_len = 0; - // get script variant from user - script_variant_t script_variant = P2PKH; - // get account index from user - uint16_t account_index = 0; - wallet_get_default_xpub_export_path(script_variant, - account_index, + wallet_get_default_xpub_export_path(opts->variant, + opts->account, path, EXPORT_XPUB_PATH_LEN, &path_len); char *xpub = NULL; - network_t network_id; - - if (keychain_get_network_type_restriction() == NETWORK_TYPE_TEST) { - network_id = NETWORK_BITCOIN_TESTNET; - } else { - network_id = NETWORK_BITCOIN; - } - + network_t network_id; + if (keychain_get_network_type_restriction() == NETWORK_TYPE_TEST) { + network_id = NETWORK_BITCOIN_TESTNET; + } else { + network_id = NETWORK_BITCOIN; + } if (!wallet_get_xpub(network_id, path, path_len, &xpub) || xpub == NULL) { //await_error_activity((const char*[]){"Unable to derive xpub"}, 1); return false; @@ -962,10 +1073,9 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { } char desc[512]; - const char* wrap = _desc_func_for_variant(script_variant); - const bool is_sh_wpkh = (script_variant == P2WPKH_P2SH); + const char* wrap = _desc_func_for_variant(opts->variant); + const bool is_sh_wpkh = (opts->variant == P2WPKH_P2SH || opts->variant == MULTI_P2WSH_P2SH); if (is_sh_wpkh) { - // remember to close two parentheses snprintf(desc, sizeof(desc), "%s[%s/%s]%s/0/*))", wrap, fphex, pathstr, xpub); @@ -976,34 +1086,35 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { } size_t dlen = strlen(desc); - char outpath[MAX_FILENAME_SIZE]; - int len = snprintf(outpath, sizeof(outpath), - "%s/wallet.desc", USBSTORAGE_MOUNT_POINT); - JADE_ASSERT(len > 0 && len < sizeof(outpath)); - - size_t written = write_buffer_to_file(outpath, - (const uint8_t*)desc, - dlen); - - if (written != dlen) { - const char* msg[] = { "Failed to save", "xpub file" }; - await_error_activity(msg, 2); - return false; - } + char outpath[MAX_FILENAME_SIZE]; + int len = snprintf(outpath, sizeof(outpath), + "%s/jade-xpub.txt", USBSTORAGE_MOUNT_POINT); + JADE_ASSERT(len > 0 && len < sizeof(outpath)); - size_t mountlen = strlen(USBSTORAGE_MOUNT_POINT); - const char* msg[] = { - "Wrote xpub to:", - outpath + mountlen + 1 - }; - await_message_activity(msg, 2); - return true; + size_t written = write_buffer_to_file(outpath, + (const uint8_t*)desc, + dlen); + + if (written != dlen) { + const char* msg[] = { "Failed to save", "xpub file" }; + await_error_activity(msg, 2); + return false; + } + + size_t mountlen = strlen(USBSTORAGE_MOUNT_POINT); + const char* msg[] = { + "Wrote xpub to:", + outpath + mountlen + 1 + }; + await_message_activity(msg, 2); + return true; } bool usbstorage_export_xpub(const char* extra_path) { - const bool is_async = false; - const usbstorage_action_context_t ctx = { .extra_path = extra_path, .ctx = NULL }; - return handle_usbstorage_action("Export Xpub", export_usb_xpub_fn, &ctx, is_async); + const bool is_async = false; + export_xpub_ctx_t options = { P2PKH, 0 }; + usbstorage_action_context_t ctx = { .extra_path = extra_path, .ctx = &options}; + return handle_usbstorage_action("Export Xpub", export_usb_xpub_fn, &ctx, is_async); } #endif // AMALGAMATED_BUILD From 4e47bd21cccc7e89c3315d9219cf23424a8126f0 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Wed, 23 Jul 2025 20:51:52 +0000 Subject: [PATCH 07/15] added back button in export dialog menu --- main/usbhmsc/usbmode.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index 104ff25a..f841b627 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -931,6 +931,12 @@ static gui_activity_t* make_export_xpub_prompt_activity(void) { "connected storage device?" }; + + btn_data_t hdr[] = { + {.txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_EXPORT_XPUB_BACK}, + {.txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } + }; + btn_data_t ftrbtns[] = { { .txt = "Options", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS, .borders = GUI_BORDER_TOPRIGHT }, { .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION, .borders = GUI_BORDER_TOPLEFT } @@ -938,7 +944,7 @@ static gui_activity_t* make_export_xpub_prompt_activity(void) { return make_show_message_activity( message, 2, "Export Xpub", - NULL, 0, + hdr, 2, ftrbtns, 2 ); } @@ -1024,6 +1030,9 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { goto DO_EXPORT; } + else if (ev == BTN_SETTINGS_EXPORT_XPUB_BACK) { + goto EXIT_EXPORT_XPUB; + } else { return false; } @@ -1108,6 +1117,8 @@ PROMPT_LOOP: ; }; await_message_activity(msg, 2); return true; +EXIT_EXPORT_XPUB: + return true; } bool usbstorage_export_xpub(const char* extra_path) { From bf5d3e98a833b926bb357fab73103005129aebbf Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Thu, 24 Jul 2025 19:25:52 +0000 Subject: [PATCH 08/15] updated options to match that of qrmodes --- main/usbhmsc/usbmode.c | 367 +++++++++++++++++++++++++++++------------ 1 file changed, 259 insertions(+), 108 deletions(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index f841b627..ed9e8adc 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -910,21 +910,229 @@ bool usbstorage_sign_psbt(const char* extra_path) return handle_usbstorage_action("Sign PSBT", sign_usb_psbt, &ctx, is_async); } -static const char* _desc_func_for_variant(script_variant_t v) +gui_activity_t* make_xpub_options_activity( + gui_view_node_t** script_textbox, gui_view_node_t** wallet_textbox, gui_view_node_t** account_textbox) { - const char *s = get_script_variant_string(v); - if (!s) return ""; - - const char *p = strchr(s, '('); - if (!p) return ""; - size_t len = p - s + 1; - static char buf[32]; - if (len >= sizeof(buf)) return ""; - memcpy(buf, s, len); - buf[len] = '\0'; - return buf; + btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_XPUB_OPTIONS_EXIT }, + { .txt = "?", .font = GUI_TITLE_FONT, .ev_id = BTN_XPUB_OPTIONS_HELP } }; + + gui_make_text(script_textbox, "Script", TFT_WHITE); + gui_set_align(*script_textbox, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); + + gui_make_text(wallet_textbox, "Wallet", TFT_WHITE); + gui_set_align(*wallet_textbox, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); + + gui_make_text(account_textbox, "Account Index", TFT_WHITE); + gui_set_align(*account_textbox, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); + + btn_data_t menubtns[] + = { { .content = *script_textbox, .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS_SCRIPTTYPE }, + { .content = *wallet_textbox, .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS_WALLETTYPE }, + { .content = *account_textbox, .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS_ACCOUNT } }; + + return make_menu_activity("Xpub Options", hdrbtns, 2, menubtns, 3); +} + + +static script_variant_t xpub_usb_script_variant_from_flags(const uint32_t qr_flags) +{ + //TODO - make more general remove qr + // unset/default is treated as 'high' (ie. the middle value) + if (contains_flags(qr_flags, QR_XPUB_TAPROOT)) { + return P2TR; + } + if (contains_flags(qr_flags, QR_XPUB_MULTISIG)) { + return contains_flags(qr_flags, QR_XPUB_WITNESS | QR_XPUB_LEGACY) ? MULTI_P2WSH_P2SH + : contains_flags(qr_flags, QR_XPUB_LEGACY) ? MULTI_P2SH + : MULTI_P2WSH; + } + return contains_flags(qr_flags, QR_XPUB_WITNESS | QR_XPUB_LEGACY) ? P2WPKH_P2SH + : contains_flags(qr_flags, QR_XPUB_LEGACY) ? P2PKH + : P2WPKH; +} + + +static inline bool contains_script_flags(const uint32_t flags, const uint32_t test_flags) +{ + return (flags & test_flags) == test_flags; +} + +static void rotate_xpub_scripttypes(uint32_t* flags, const bool reverse) +{ + JADE_ASSERT(flags); + //TODO - remove qr from the flags and define them for general use + + if (reverse) { + if (contains_flags(*flags, QR_XPUB_LEGACY | QR_XPUB_WITNESS)) { + *flags &= ~QR_XPUB_WITNESS; + } else if (contains_flags(*flags, QR_XPUB_LEGACY)) { + *flags ^= (QR_XPUB_LEGACY | QR_XPUB_TAPROOT); + } else if (contains_flags(*flags, QR_XPUB_TAPROOT)) { + *flags ^= (QR_XPUB_WITNESS | QR_XPUB_TAPROOT); + } else if (contains_flags(*flags, QR_XPUB_WITNESS)) { + *flags |= QR_XPUB_LEGACY; + } else { // ie. currently 0/default/uninitialised - treat as 'segwit v0' + *flags |= (QR_XPUB_LEGACY | QR_XPUB_WITNESS); + } + } else { + if (contains_flags(*flags, QR_XPUB_LEGACY | QR_XPUB_WITNESS)) { + *flags &= ~QR_XPUB_LEGACY; + } else if (contains_flags(*flags, QR_XPUB_WITNESS)) { + *flags ^= (QR_XPUB_WITNESS | QR_XPUB_TAPROOT); + } else if (contains_flags(*flags, QR_XPUB_TAPROOT)) { + *flags ^= (QR_XPUB_LEGACY | QR_XPUB_TAPROOT); + } else if (contains_flags(*flags, QR_XPUB_LEGACY)) { + *flags |= QR_XPUB_WITNESS; + } else { // ie. currently 0/default/uninitialised - treat as 'segwit v0' + *flags |= QR_XPUB_TAPROOT; + } + } +} + +static inline const char* xpub_usb_scripttype_desc_from_flags(const uint32_t qr_flags) +{ + //TODO - make more general remove qr + // unset/default is treated as 'high' (ie. the middle value) + return contains_flags(qr_flags, QR_XPUB_WITNESS | QR_XPUB_LEGACY) ? "Wrapped Segwit" + : contains_flags(qr_flags, QR_XPUB_LEGACY) ? "Legacy" + : contains_flags(qr_flags, QR_XPUB_TAPROOT) ? "Taproot" + : "Native Segwit"; +} + +static inline const char* xpub_usb_wallettype_desc_from_flags(const uint32_t qr_flags) +{ + //TODO - make more general remove qr + // unset/default is treated as singlesig + return contains_flags(qr_flags, QR_XPUB_MULTISIG) ? "Multisig" : "Singlesig"; +} + + +static bool handle_usb_xpub_options(uint32_t* qr_flags) +{ + JADE_ASSERT(qr_flags); + + uint16_t account_index = (*qr_flags) >> ACCOUNT_INDEX_FLAGS_SHIFT; + + char buf[8]; + int rc = snprintf(buf, sizeof(buf), "%u", account_index); + JADE_ASSERT(rc > 0 && rc < sizeof(buf)); + + gui_view_node_t* script_item = NULL; + gui_view_node_t* wallet_item = NULL; + gui_view_node_t* account_item = NULL; + gui_activity_t* const act = make_xpub_options_activity(&script_item, &wallet_item, &account_item); + update_menu_item(script_item, "Script", xpub_usb_scripttype_desc_from_flags(*qr_flags)); + update_menu_item(wallet_item, "Wallet", xpub_usb_wallettype_desc_from_flags(*qr_flags)); + update_menu_item(account_item, "Account Index", buf); + gui_set_current_activity(act); + + gui_view_node_t* script_textbox = NULL; + gui_activity_t* const act_scripttype = make_carousel_activity("Script Type", NULL, &script_textbox); + gui_update_text(script_textbox, xpub_usb_scripttype_desc_from_flags(*qr_flags)); + + gui_view_node_t* wallet_textbox = NULL; + gui_activity_t* const act_wallettype = make_carousel_activity("Wallet Type", NULL, &wallet_textbox); + gui_update_text(wallet_textbox, xpub_usb_wallettype_desc_from_flags(*qr_flags)); + + pin_insert_t pin_insert = { .initial_state = ZERO, .pin_digits_shown = true }; + make_pin_insert_activity(&pin_insert, "Account Index", "Enter index:"); + JADE_ASSERT(pin_insert.activity); + + const uint32_t initial_flags = *qr_flags; + while (true) { + // Show, and await button click + gui_set_current_activity(act); + + int32_t ev_id; +#ifndef CONFIG_DEBUG_UNATTENDED_CI + const bool ret = gui_activity_wait_event(act, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0); +#else + gui_activity_wait_event(act, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, NULL, NULL, + CONFIG_DEBUG_UNATTENDED_CI_TIMEOUT_MS / portTICK_PERIOD_MS); + const bool ret = true; + ev_id = BTN_XPUB_OPTIONS_EXIT; +#endif + if (ret) { + if (ev_id == BTN_XPUB_OPTIONS_SCRIPTTYPE) { + gui_set_current_activity(act_scripttype); + while (true) { + gui_update_text(script_textbox, xpub_scripttype_desc_from_flags(*qr_flags)); + if (gui_activity_wait_event(act_scripttype, GUI_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) { + if (ev_id == GUI_WHEEL_LEFT_EVENT) { + rotate_scripttypes(qr_flags, true); + } else if (ev_id == GUI_WHEEL_RIGHT_EVENT) { + rotate_scripttypes(qr_flags, false); + } else if (ev_id == gui_get_click_event()) { + // Done + break; + } + } + } + update_menu_item(script_item, "Script", xpub_scripttype_desc_from_flags(*qr_flags)); + } else if (ev_id == BTN_XPUB_OPTIONS_WALLETTYPE) { + gui_set_current_activity(act_wallettype); + while (true) { + gui_update_text(wallet_textbox, xpub_wallettype_desc_from_flags(*qr_flags)); + if (gui_activity_wait_event(act_wallettype, GUI_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) { + if (ev_id == GUI_WHEEL_LEFT_EVENT || ev_id == GUI_WHEEL_RIGHT_EVENT) { + *qr_flags ^= QR_XPUB_MULTISIG; // toggle + } else if (ev_id == gui_get_click_event()) { + // Done + break; + } + } + } + update_menu_item(wallet_item, "Wallet", xpub_wallettype_desc_from_flags(*qr_flags)); + } else if (ev_id == BTN_XPUB_OPTIONS_ACCOUNT) { + + while (true) { + reset_pin(&pin_insert, NULL); + gui_set_current_activity(pin_insert.activity); + if (!run_pin_entry_loop(&pin_insert)) { + // User abandoned index entry + break; + } + + // Get entered digits as single numeric value + const uint32_t new_account_index = get_pin_as_number(&pin_insert); + if (new_account_index < ACCOUNT_INDEX_MAX) { + account_index = new_account_index; + + // Update the display + const int ret = snprintf(buf, sizeof(buf), "%u", account_index); + JADE_ASSERT(ret > 0 && ret < sizeof(buf)); + update_menu_item(account_item, "Account Index", buf); + break; + } else { + // Show message and retry + const int ret = snprintf(buf, sizeof(buf), "%u", ACCOUNT_INDEX_MAX); + JADE_ASSERT(ret > 0 && ret < sizeof(buf)); + const char* message[] = { "Account index must", "be less than", buf }; + await_error_activity(message, 3); + } + } + } else if (ev_id == BTN_XPUB_OPTIONS_HELP) { + await_qr_help_activity("blkstrm.com/xpub"); + } else if (ev_id == BTN_XPUB_OPTIONS_EXIT) { + // Done + break; + } + } + } + + // If updated, persist prefereces + *qr_flags = (uint16_t)(*qr_flags); + *qr_flags |= (((uint32_t)account_index) << ACCOUNT_INDEX_FLAGS_SHIFT); + if (initial_flags == *qr_flags) { + return false; + } + + // Return to indicate if any options were updated + storage_set_qr_flags(*qr_flags); + return true; } + static gui_activity_t* make_export_xpub_prompt_activity(void) { const char* message[] = { "Save wallet details to", @@ -949,43 +1157,11 @@ static gui_activity_t* make_export_xpub_prompt_activity(void) { ); } -static gui_activity_t* make_export_xpub_options_activity( - export_xpub_ctx_t* opts, - gui_view_node_t** variant_item, - gui_view_node_t** account_item) -{ - - btn_data_t hdr[] = { - {.txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_EXPORT_XPUB_BACK}, - {.txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } - }; - - - gui_make_text(variant_item, "", TFT_WHITE); - gui_set_align(*variant_item, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - gui_make_text(account_item, "", TFT_WHITE); - gui_set_align(*account_item, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - btn_data_t menu[] = { - { .content = *variant_item, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_VARIANT}, - { .content = *account_item, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACCOUNT}, - }; - gui_activity_t* act = make_menu_activity("xpub options", hdr, 2, menu, 2); - - gui_update_text(*variant_item, _desc_func_for_variant(opts->variant)); - { - char buf[16]; - snprintf(buf, sizeof(buf), "Acct %u", opts->account); - gui_update_text(*account_item, buf); - } +static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { - return act; -} -static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { - JADE_ASSERT(ctx); - export_xpub_ctx_t default_opts = { .variant = P2PKH, .account = 0 }; - export_xpub_ctx_t* opts = ctx->ctx ? (export_xpub_ctx_t*)ctx->ctx : &default_opts; + uint32_t qr_flags = storage_get_qr_flags(); while (true) { gui_activity_t* prompt = make_export_xpub_prompt_activity(); @@ -995,36 +1171,7 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { gui_activity_wait_event(prompt, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev, NULL, 0); if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS) { - gui_view_node_t *var_item = NULL, *acct_item = NULL; - gui_activity_t* opts_act = make_export_xpub_options_activity( - opts, &var_item, &acct_item - ); - gui_set_current_activity(opts_act); - - while (true) { - gui_activity_wait_event( - opts_act, - GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, - &ev, NULL, 0 - ); - - switch (ev) { - case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_VARIANT: - opts->variant = (opts->variant + 1) % VARIANT_COUNT; - gui_update_text(var_item, _desc_func_for_variant(opts->variant)); - break; - case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACCOUNT: - opts->account = (opts->account + 1) % MAX_ACCOUNTS; - { - char buf[16]; - snprintf(buf, sizeof(buf), "Acct %u", opts->account); - gui_update_text(acct_item, buf); - } - break; - case BTN_SETTINGS_EXPORT_XPUB_BACK: - goto PROMPT_LOOP; - } - } + handle_usb_xpub_options(&qr_flags); } else if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION) { @@ -1037,20 +1184,34 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { return false; } -PROMPT_LOOP: ; } DO_EXPORT: + const script_variant_t script_variant = xpub_script_variant_from_flags(qr_flags); + const uint16_t account_index = qr_flags >> ACCOUNT_INDEX_FLAGS_SHIFT; + + const char *prefix; + + switch (script_variant) { + case P2PKH: prefix = "pkh"; break; + case P2WPKH_P2SH: prefix = "sh(wpkh"; break; + case P2WPKH: prefix = "wpkh"; break; + case P2TR: prefix = "tr"; break; + case MULTI_P2SH: prefix = "sh(multi"; break; + case MULTI_P2WSH_P2SH: prefix = "sh(wsh(multi"; break; + case MULTI_P2WSH: prefix = "wsh(multi"; break; + default: prefix = "unknown"; break; + } + uint32_t path[EXPORT_XPUB_PATH_LEN]; size_t path_len = 0; - wallet_get_default_xpub_export_path(opts->variant, - opts->account, + wallet_get_default_xpub_export_path(script_variant, + account_index, path, EXPORT_XPUB_PATH_LEN, &path_len); char *xpub = NULL; - network_t network_id; if (keychain_get_network_type_restriction() == NETWORK_TYPE_TEST) { network_id = NETWORK_BITCOIN_TESTNET; @@ -1061,6 +1222,12 @@ PROMPT_LOOP: ; //await_error_activity((const char*[]){"Unable to derive xpub"}, 1); return false; } + + char pathstr[MAX_PATH_STR_LEN(EXPORT_XPUB_PATH_LEN)]; + const bool ret = wallet_bip32_path_as_str(path, path_len, pathstr, sizeof(pathstr)); + + JADE_ASSERT(ret); + // get fingerprint // then take the whole string and put it together with // scriptvariant([fingerprint/84'/0'accountindex]xpub/0/*) @@ -1071,40 +1238,24 @@ PROMPT_LOOP: ; wally_hex_from_bytes(user_fingerprint, sizeof(user_fingerprint), &fphex); map_string(fphex, toupper); - char pathstr[64] = {0}; - char elem[16]; - for (size_t i = 0; i < path_len; i++) { - uint32_t idx = path[i] & ~0x80000000; - bool hard = (path[i] & 0x80000000) != 0; - if (i) strlcat(pathstr, "/", sizeof(pathstr)); - snprintf(elem, sizeof(elem), "%u%s", (unsigned)idx, hard ? "'" : ""); - strlcat(pathstr, elem, sizeof(pathstr)); - } - - char desc[512]; - const char* wrap = _desc_func_for_variant(opts->variant); - const bool is_sh_wpkh = (opts->variant == P2WPKH_P2SH || opts->variant == MULTI_P2WSH_P2SH); - if (is_sh_wpkh) { - snprintf(desc, sizeof(desc), - "%s[%s/%s]%s/0/*))", - wrap, fphex, pathstr, xpub); - } else { - snprintf(desc, sizeof(desc), - "%s[%s/%s]%s/0/*)", - wrap, fphex, pathstr, xpub); - } - size_t dlen = strlen(desc); - + char descriptor[512]; + int n = snprintf(descriptor, sizeof(descriptor), + "%s([%s/%s]%s/0/*)%s", + prefix, + fphex, + pathstr + 2, /* drop leading "m/" */ + xpub, + prefix[0]=='s' && strchr(prefix,'(') ? ")" : ""); + JADE_ASSERT(n > 0 && n < (int)sizeof(descriptor)); char outpath[MAX_FILENAME_SIZE]; int len = snprintf(outpath, sizeof(outpath), "%s/jade-xpub.txt", USBSTORAGE_MOUNT_POINT); JADE_ASSERT(len > 0 && len < sizeof(outpath)); - size_t written = write_buffer_to_file(outpath, - (const uint8_t*)desc, - dlen); + (const uint8_t*)descriptor, + (size_t)n); - if (written != dlen) { + if (written != (size_t)n) { const char* msg[] = { "Failed to save", "xpub file" }; await_error_activity(msg, 2); return false; From 7f18e211cd3350f8b48ef5b28011ea322884ecca Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Thu, 24 Jul 2025 21:29:24 +0000 Subject: [PATCH 09/15] cleanup redundent code --- main/process/dashboard.c | 10 ----- main/ui/dashboard.c | 26 ------------- main/usbhmsc/usbmode.c | 82 ++-------------------------------------- 3 files changed, 4 insertions(+), 114 deletions(-) diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 1981d1ce..0dd9e538 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -182,8 +182,6 @@ gui_activity_t* make_unlocked_settings_activity(void); gui_activity_t* make_wallet_settings_activity(void); gui_activity_t* make_device_settings_activity(void); gui_activity_t* make_usbstorage_settings_activity(bool unlocked); -gui_activity_t* make_usbstorage_export_xpub_activity(void); -gui_activity_t* make_usbstorage_export_xpub_options_activity(void); gui_activity_t* make_authentication_activity(bool initialised_and_pin_unlocked); gui_activity_t* make_prefs_settings_activity(bool initialised_and_locked, gui_view_node_t** qr_mode_network_item); @@ -2389,14 +2387,6 @@ static void handle_settings(const bool startup_menu) usbstorage_export_xpub(NULL); act = make_usbstorage_settings_activity(NULL); break; - - case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS: - act = make_usbstorage_export_xpub_options_activity(); - break; - - case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION: - //FIXME: implement - break; #endif case BTN_SETTINGS_OTP_VIEW: handle_view_otps(); diff --git a/main/ui/dashboard.c b/main/ui/dashboard.c index 6d321f97..9c345a4b 100644 --- a/main/ui/dashboard.c +++ b/main/ui/dashboard.c @@ -357,32 +357,6 @@ gui_activity_t* make_usbstorage_settings_activity(const bool unlocked) return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, 3); } -gui_activity_t* make_usbstorage_export_xpub_activity(void) -{ - btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXIT }, - { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } }; - - btn_data_t menubtns[] = { - { .txt = "Options", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, - { .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION}, - }; - - return make_menu_activity("Export Xpub", hdrbtns, 2, menubtns, 2); -} - -gui_activity_t* make_usbstorage_export_xpub_options_activity(void) -{ - btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, - { .txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } }; - - btn_data_t menubtns[] = { - { .txt = "Script: ", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, - { .txt = "Wallet: ", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, - { .txt = "Account Index: ", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS}, - }; - - return make_menu_activity("Export Xpub", hdrbtns, 2, menubtns, 3); -} #endif gui_activity_t* make_device_settings_activity(void) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index ed9e8adc..74a7e7cc 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -933,80 +933,6 @@ gui_activity_t* make_xpub_options_activity( return make_menu_activity("Xpub Options", hdrbtns, 2, menubtns, 3); } - -static script_variant_t xpub_usb_script_variant_from_flags(const uint32_t qr_flags) -{ - //TODO - make more general remove qr - // unset/default is treated as 'high' (ie. the middle value) - if (contains_flags(qr_flags, QR_XPUB_TAPROOT)) { - return P2TR; - } - if (contains_flags(qr_flags, QR_XPUB_MULTISIG)) { - return contains_flags(qr_flags, QR_XPUB_WITNESS | QR_XPUB_LEGACY) ? MULTI_P2WSH_P2SH - : contains_flags(qr_flags, QR_XPUB_LEGACY) ? MULTI_P2SH - : MULTI_P2WSH; - } - return contains_flags(qr_flags, QR_XPUB_WITNESS | QR_XPUB_LEGACY) ? P2WPKH_P2SH - : contains_flags(qr_flags, QR_XPUB_LEGACY) ? P2PKH - : P2WPKH; -} - - -static inline bool contains_script_flags(const uint32_t flags, const uint32_t test_flags) -{ - return (flags & test_flags) == test_flags; -} - -static void rotate_xpub_scripttypes(uint32_t* flags, const bool reverse) -{ - JADE_ASSERT(flags); - //TODO - remove qr from the flags and define them for general use - - if (reverse) { - if (contains_flags(*flags, QR_XPUB_LEGACY | QR_XPUB_WITNESS)) { - *flags &= ~QR_XPUB_WITNESS; - } else if (contains_flags(*flags, QR_XPUB_LEGACY)) { - *flags ^= (QR_XPUB_LEGACY | QR_XPUB_TAPROOT); - } else if (contains_flags(*flags, QR_XPUB_TAPROOT)) { - *flags ^= (QR_XPUB_WITNESS | QR_XPUB_TAPROOT); - } else if (contains_flags(*flags, QR_XPUB_WITNESS)) { - *flags |= QR_XPUB_LEGACY; - } else { // ie. currently 0/default/uninitialised - treat as 'segwit v0' - *flags |= (QR_XPUB_LEGACY | QR_XPUB_WITNESS); - } - } else { - if (contains_flags(*flags, QR_XPUB_LEGACY | QR_XPUB_WITNESS)) { - *flags &= ~QR_XPUB_LEGACY; - } else if (contains_flags(*flags, QR_XPUB_WITNESS)) { - *flags ^= (QR_XPUB_WITNESS | QR_XPUB_TAPROOT); - } else if (contains_flags(*flags, QR_XPUB_TAPROOT)) { - *flags ^= (QR_XPUB_LEGACY | QR_XPUB_TAPROOT); - } else if (contains_flags(*flags, QR_XPUB_LEGACY)) { - *flags |= QR_XPUB_WITNESS; - } else { // ie. currently 0/default/uninitialised - treat as 'segwit v0' - *flags |= QR_XPUB_TAPROOT; - } - } -} - -static inline const char* xpub_usb_scripttype_desc_from_flags(const uint32_t qr_flags) -{ - //TODO - make more general remove qr - // unset/default is treated as 'high' (ie. the middle value) - return contains_flags(qr_flags, QR_XPUB_WITNESS | QR_XPUB_LEGACY) ? "Wrapped Segwit" - : contains_flags(qr_flags, QR_XPUB_LEGACY) ? "Legacy" - : contains_flags(qr_flags, QR_XPUB_TAPROOT) ? "Taproot" - : "Native Segwit"; -} - -static inline const char* xpub_usb_wallettype_desc_from_flags(const uint32_t qr_flags) -{ - //TODO - make more general remove qr - // unset/default is treated as singlesig - return contains_flags(qr_flags, QR_XPUB_MULTISIG) ? "Multisig" : "Singlesig"; -} - - static bool handle_usb_xpub_options(uint32_t* qr_flags) { JADE_ASSERT(qr_flags); @@ -1021,18 +947,18 @@ static bool handle_usb_xpub_options(uint32_t* qr_flags) gui_view_node_t* wallet_item = NULL; gui_view_node_t* account_item = NULL; gui_activity_t* const act = make_xpub_options_activity(&script_item, &wallet_item, &account_item); - update_menu_item(script_item, "Script", xpub_usb_scripttype_desc_from_flags(*qr_flags)); - update_menu_item(wallet_item, "Wallet", xpub_usb_wallettype_desc_from_flags(*qr_flags)); + update_menu_item(script_item, "Script", xpub_scripttype_desc_from_flags(*qr_flags)); + update_menu_item(wallet_item, "Wallet", xpub_wallettype_desc_from_flags(*qr_flags)); update_menu_item(account_item, "Account Index", buf); gui_set_current_activity(act); gui_view_node_t* script_textbox = NULL; gui_activity_t* const act_scripttype = make_carousel_activity("Script Type", NULL, &script_textbox); - gui_update_text(script_textbox, xpub_usb_scripttype_desc_from_flags(*qr_flags)); + gui_update_text(script_textbox, xpub_scripttype_desc_from_flags(*qr_flags)); gui_view_node_t* wallet_textbox = NULL; gui_activity_t* const act_wallettype = make_carousel_activity("Wallet Type", NULL, &wallet_textbox); - gui_update_text(wallet_textbox, xpub_usb_wallettype_desc_from_flags(*qr_flags)); + gui_update_text(wallet_textbox, xpub_wallettype_desc_from_flags(*qr_flags)); pin_insert_t pin_insert = { .initial_state = ZERO, .pin_digits_shown = true }; make_pin_insert_activity(&pin_insert, "Account Index", "Enter index:"); From eab73926e9c4e328010f9bbde5c8d4c567c7a60f Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Thu, 24 Jul 2025 21:41:56 +0000 Subject: [PATCH 10/15] removed more redundent code --- main/usbhmsc/usbmode.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index 74a7e7cc..eaa8070b 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -910,29 +910,6 @@ bool usbstorage_sign_psbt(const char* extra_path) return handle_usbstorage_action("Sign PSBT", sign_usb_psbt, &ctx, is_async); } -gui_activity_t* make_xpub_options_activity( - gui_view_node_t** script_textbox, gui_view_node_t** wallet_textbox, gui_view_node_t** account_textbox) -{ - btn_data_t hdrbtns[] = { { .txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_XPUB_OPTIONS_EXIT }, - { .txt = "?", .font = GUI_TITLE_FONT, .ev_id = BTN_XPUB_OPTIONS_HELP } }; - - gui_make_text(script_textbox, "Script", TFT_WHITE); - gui_set_align(*script_textbox, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - - gui_make_text(wallet_textbox, "Wallet", TFT_WHITE); - gui_set_align(*wallet_textbox, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - - gui_make_text(account_textbox, "Account Index", TFT_WHITE); - gui_set_align(*account_textbox, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE); - - btn_data_t menubtns[] - = { { .content = *script_textbox, .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS_SCRIPTTYPE }, - { .content = *wallet_textbox, .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS_WALLETTYPE }, - { .content = *account_textbox, .font = GUI_DEFAULT_FONT, .ev_id = BTN_XPUB_OPTIONS_ACCOUNT } }; - - return make_menu_activity("Xpub Options", hdrbtns, 2, menubtns, 3); -} - static bool handle_usb_xpub_options(uint32_t* qr_flags) { JADE_ASSERT(qr_flags); @@ -946,7 +923,7 @@ static bool handle_usb_xpub_options(uint32_t* qr_flags) gui_view_node_t* script_item = NULL; gui_view_node_t* wallet_item = NULL; gui_view_node_t* account_item = NULL; - gui_activity_t* const act = make_xpub_options_activity(&script_item, &wallet_item, &account_item); + gui_activity_t* const act = make_xpub_qr_options_activity(&script_item, &wallet_item, &account_item); update_menu_item(script_item, "Script", xpub_scripttype_desc_from_flags(*qr_flags)); update_menu_item(wallet_item, "Wallet", xpub_wallettype_desc_from_flags(*qr_flags)); update_menu_item(account_item, "Account Index", buf); From d66715658a5cc3fd8a9fb4062d5096bf17400bc5 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Thu, 24 Jul 2025 22:00:03 +0000 Subject: [PATCH 11/15] removed unused button events and added unlocked check for usb options --- main/button_events.h | 3 - main/process/dashboard.c | 3 +- main/ui/dashboard.c | 3 +- main/usbhmsc/usbmode.c | 128 +-------------------------------------- 4 files changed, 4 insertions(+), 133 deletions(-) diff --git a/main/button_events.h b/main/button_events.h index 2d4cdbfb..e433ca06 100644 --- a/main/button_events.h +++ b/main/button_events.h @@ -171,12 +171,9 @@ typedef enum { BTN_SETTINGS_USBSTORAGE_BACK, BTN_SETTINGS_USBSTORAGE_HELP, BTN_SETTINGS_USBSTORAGE_SIGN, - BTN_SETTINGS_USBSTORAGE_EXPORT, BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB, BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS, BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION, - BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_VARIANT, - BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACCOUNT, BTN_SETTINGS_EXPORT_XPUB_BACK, BTN_SETTINGS_EXPORT_XPUB_EXIT, BTN_SETTINGS_USBSTORAGE_EXIT, diff --git a/main/process/dashboard.c b/main/process/dashboard.c index 0dd9e538..d2dd0aab 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -2384,8 +2384,9 @@ static void handle_settings(const bool startup_menu) break; case BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB: + JADE_ASSERT(keychain_get()); usbstorage_export_xpub(NULL); - act = make_usbstorage_settings_activity(NULL); + act = make_usbstorage_settings_activity(keychain_get()); break; #endif case BTN_SETTINGS_OTP_VIEW: diff --git a/main/ui/dashboard.c b/main/ui/dashboard.c index 9c345a4b..c66e17fd 100644 --- a/main/ui/dashboard.c +++ b/main/ui/dashboard.c @@ -350,11 +350,10 @@ gui_activity_t* make_usbstorage_settings_activity(const bool unlocked) btn_data_t menubtns[] = { { .txt = "Firmware Upgrade", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_FW }, { .txt = "Sign", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_SIGN }, - //{ .txt = "Export", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT }, { .txt = "Export Xpub", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB }, }; - return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, 3); + return make_menu_activity("USB Storage", hdrbtns, 2, menubtns, unlocked ? 3: 1); } #endif diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index eaa8070b..b4066e28 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -910,132 +910,6 @@ bool usbstorage_sign_psbt(const char* extra_path) return handle_usbstorage_action("Sign PSBT", sign_usb_psbt, &ctx, is_async); } -static bool handle_usb_xpub_options(uint32_t* qr_flags) -{ - JADE_ASSERT(qr_flags); - - uint16_t account_index = (*qr_flags) >> ACCOUNT_INDEX_FLAGS_SHIFT; - - char buf[8]; - int rc = snprintf(buf, sizeof(buf), "%u", account_index); - JADE_ASSERT(rc > 0 && rc < sizeof(buf)); - - gui_view_node_t* script_item = NULL; - gui_view_node_t* wallet_item = NULL; - gui_view_node_t* account_item = NULL; - gui_activity_t* const act = make_xpub_qr_options_activity(&script_item, &wallet_item, &account_item); - update_menu_item(script_item, "Script", xpub_scripttype_desc_from_flags(*qr_flags)); - update_menu_item(wallet_item, "Wallet", xpub_wallettype_desc_from_flags(*qr_flags)); - update_menu_item(account_item, "Account Index", buf); - gui_set_current_activity(act); - - gui_view_node_t* script_textbox = NULL; - gui_activity_t* const act_scripttype = make_carousel_activity("Script Type", NULL, &script_textbox); - gui_update_text(script_textbox, xpub_scripttype_desc_from_flags(*qr_flags)); - - gui_view_node_t* wallet_textbox = NULL; - gui_activity_t* const act_wallettype = make_carousel_activity("Wallet Type", NULL, &wallet_textbox); - gui_update_text(wallet_textbox, xpub_wallettype_desc_from_flags(*qr_flags)); - - pin_insert_t pin_insert = { .initial_state = ZERO, .pin_digits_shown = true }; - make_pin_insert_activity(&pin_insert, "Account Index", "Enter index:"); - JADE_ASSERT(pin_insert.activity); - - const uint32_t initial_flags = *qr_flags; - while (true) { - // Show, and await button click - gui_set_current_activity(act); - - int32_t ev_id; -#ifndef CONFIG_DEBUG_UNATTENDED_CI - const bool ret = gui_activity_wait_event(act, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0); -#else - gui_activity_wait_event(act, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, NULL, NULL, - CONFIG_DEBUG_UNATTENDED_CI_TIMEOUT_MS / portTICK_PERIOD_MS); - const bool ret = true; - ev_id = BTN_XPUB_OPTIONS_EXIT; -#endif - if (ret) { - if (ev_id == BTN_XPUB_OPTIONS_SCRIPTTYPE) { - gui_set_current_activity(act_scripttype); - while (true) { - gui_update_text(script_textbox, xpub_scripttype_desc_from_flags(*qr_flags)); - if (gui_activity_wait_event(act_scripttype, GUI_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) { - if (ev_id == GUI_WHEEL_LEFT_EVENT) { - rotate_scripttypes(qr_flags, true); - } else if (ev_id == GUI_WHEEL_RIGHT_EVENT) { - rotate_scripttypes(qr_flags, false); - } else if (ev_id == gui_get_click_event()) { - // Done - break; - } - } - } - update_menu_item(script_item, "Script", xpub_scripttype_desc_from_flags(*qr_flags)); - } else if (ev_id == BTN_XPUB_OPTIONS_WALLETTYPE) { - gui_set_current_activity(act_wallettype); - while (true) { - gui_update_text(wallet_textbox, xpub_wallettype_desc_from_flags(*qr_flags)); - if (gui_activity_wait_event(act_wallettype, GUI_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0)) { - if (ev_id == GUI_WHEEL_LEFT_EVENT || ev_id == GUI_WHEEL_RIGHT_EVENT) { - *qr_flags ^= QR_XPUB_MULTISIG; // toggle - } else if (ev_id == gui_get_click_event()) { - // Done - break; - } - } - } - update_menu_item(wallet_item, "Wallet", xpub_wallettype_desc_from_flags(*qr_flags)); - } else if (ev_id == BTN_XPUB_OPTIONS_ACCOUNT) { - - while (true) { - reset_pin(&pin_insert, NULL); - gui_set_current_activity(pin_insert.activity); - if (!run_pin_entry_loop(&pin_insert)) { - // User abandoned index entry - break; - } - - // Get entered digits as single numeric value - const uint32_t new_account_index = get_pin_as_number(&pin_insert); - if (new_account_index < ACCOUNT_INDEX_MAX) { - account_index = new_account_index; - - // Update the display - const int ret = snprintf(buf, sizeof(buf), "%u", account_index); - JADE_ASSERT(ret > 0 && ret < sizeof(buf)); - update_menu_item(account_item, "Account Index", buf); - break; - } else { - // Show message and retry - const int ret = snprintf(buf, sizeof(buf), "%u", ACCOUNT_INDEX_MAX); - JADE_ASSERT(ret > 0 && ret < sizeof(buf)); - const char* message[] = { "Account index must", "be less than", buf }; - await_error_activity(message, 3); - } - } - } else if (ev_id == BTN_XPUB_OPTIONS_HELP) { - await_qr_help_activity("blkstrm.com/xpub"); - } else if (ev_id == BTN_XPUB_OPTIONS_EXIT) { - // Done - break; - } - } - } - - // If updated, persist prefereces - *qr_flags = (uint16_t)(*qr_flags); - *qr_flags |= (((uint32_t)account_index) << ACCOUNT_INDEX_FLAGS_SHIFT); - if (initial_flags == *qr_flags) { - return false; - } - - // Return to indicate if any options were updated - storage_set_qr_flags(*qr_flags); - return true; -} - - static gui_activity_t* make_export_xpub_prompt_activity(void) { const char* message[] = { "Save wallet details to", @@ -1074,7 +948,7 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { gui_activity_wait_event(prompt, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev, NULL, 0); if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_OPTIONS) { - handle_usb_xpub_options(&qr_flags); + handle_xpub_options(&qr_flags); } else if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION) { From c1a820d5d8c1b3de3aae5355e511736cc738cff5 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Thu, 24 Jul 2025 22:21:59 +0000 Subject: [PATCH 12/15] removed unused struct --- main/usbhmsc/usbmode.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index b4066e28..d66ae262 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -64,11 +64,6 @@ typedef struct { void* ctx; } usbstorage_action_context_t; -typedef struct { - script_variant_t variant; - uint16_t account; -} export_xpub_ctx_t; - // Function/action to call on a usb-storage directory typedef bool (*usbstorage_action_fn_t)(const usbstorage_action_context_t* ctx); @@ -916,7 +911,6 @@ static gui_activity_t* make_export_xpub_prompt_activity(void) { "connected storage device?" }; - btn_data_t hdr[] = { {.txt = "=", .font = JADE_SYMBOLS_16x16_FONT, .ev_id = BTN_SETTINGS_EXPORT_XPUB_BACK}, {.txt = NULL, .font = GUI_DEFAULT_FONT, .ev_id = GUI_BUTTON_EVENT_NONE } @@ -934,10 +928,8 @@ static gui_activity_t* make_export_xpub_prompt_activity(void) { ); } - static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { - uint32_t qr_flags = storage_get_qr_flags(); while (true) { @@ -1051,8 +1043,7 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { bool usbstorage_export_xpub(const char* extra_path) { const bool is_async = false; - export_xpub_ctx_t options = { P2PKH, 0 }; - usbstorage_action_context_t ctx = { .extra_path = extra_path, .ctx = &options}; + usbstorage_action_context_t ctx = { .extra_path = NULL, .ctx = NULL}; return handle_usbstorage_action("Export Xpub", export_usb_xpub_fn, &ctx, is_async); } From d45125f8ab451d1f7f3de223c764ce1f88e8947a Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Fri, 25 Jul 2025 18:08:25 +0000 Subject: [PATCH 13/15] added cleanup --- main/usbhmsc/usbmode.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index d66ae262..9f2c0d9c 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -930,6 +930,9 @@ static gui_activity_t* make_export_xpub_prompt_activity(void) { static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { + bool ok = false; + char *fphex = NULL; + char *xpub = NULL; uint32_t qr_flags = storage_get_qr_flags(); while (true) { @@ -943,19 +946,16 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { handle_xpub_options(&qr_flags); } else if (ev == BTN_SETTINGS_USBSTORAGE_EXPORT_XPUB_ACTION) { - - goto DO_EXPORT; + break; } else if (ev == BTN_SETTINGS_EXPORT_XPUB_BACK) { - goto EXIT_EXPORT_XPUB; + return true; } else { return false; } } - -DO_EXPORT: const script_variant_t script_variant = xpub_script_variant_from_flags(qr_flags); const uint16_t account_index = qr_flags >> ACCOUNT_INDEX_FLAGS_SHIFT; @@ -980,16 +980,17 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { EXPORT_XPUB_PATH_LEN, &path_len); - char *xpub = NULL; network_t network_id; if (keychain_get_network_type_restriction() == NETWORK_TYPE_TEST) { network_id = NETWORK_BITCOIN_TESTNET; } else { network_id = NETWORK_BITCOIN; } + if (!wallet_get_xpub(network_id, path, path_len, &xpub) || xpub == NULL) { - //await_error_activity((const char*[]){"Unable to derive xpub"}, 1); - return false; + const char* msg[] = { "unable to get", "xpub from path" }; + await_error_activity(msg, 2); + goto cleanup; } char pathstr[MAX_PATH_STR_LEN(EXPORT_XPUB_PATH_LEN)]; @@ -997,14 +998,16 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { JADE_ASSERT(ret); - // get fingerprint - // then take the whole string and put it together with - // scriptvariant([fingerprint/84'/0'accountindex]xpub/0/*) uint8_t user_fingerprint[BIP32_KEY_FINGERPRINT_LEN]; wallet_get_fingerprint(user_fingerprint, sizeof(user_fingerprint)); - char* fphex = NULL; - wally_hex_from_bytes(user_fingerprint, sizeof(user_fingerprint), &fphex); + JADE_WALLY_VERIFY(wally_hex_from_bytes(user_fingerprint, sizeof(user_fingerprint), &fphex)); + if (!fphex) { + const char* msg[] = { "unable to get", "fingerprint from wallet" }; + await_error_activity(msg, 2); + goto cleanup; + }; + map_string(fphex, toupper); char descriptor[512]; @@ -1027,7 +1030,7 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { if (written != (size_t)n) { const char* msg[] = { "Failed to save", "xpub file" }; await_error_activity(msg, 2); - return false; + goto cleanup; } size_t mountlen = strlen(USBSTORAGE_MOUNT_POINT); @@ -1036,9 +1039,11 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { outpath + mountlen + 1 }; await_message_activity(msg, 2); - return true; -EXIT_EXPORT_XPUB: - return true; + ok = true; +cleanup: + if (xpub) wally_free_string(xpub); + if (fphex) wally_free_string(fphex); + return ok; } bool usbstorage_export_xpub(const char* extra_path) { From c639a093b3cea6ba5d8ce6b4dd3b1d59fa732d2b Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Fri, 25 Jul 2025 18:24:16 +0000 Subject: [PATCH 14/15] removed unnecessary definitions --- main/process/dashboard.c | 1 - main/usbhmsc/usbmode.c | 3 --- 2 files changed, 4 deletions(-) diff --git a/main/process/dashboard.c b/main/process/dashboard.c index d2dd0aab..708bdce6 100644 --- a/main/process/dashboard.c +++ b/main/process/dashboard.c @@ -182,7 +182,6 @@ gui_activity_t* make_unlocked_settings_activity(void); gui_activity_t* make_wallet_settings_activity(void); gui_activity_t* make_device_settings_activity(void); gui_activity_t* make_usbstorage_settings_activity(bool unlocked); - gui_activity_t* make_authentication_activity(bool initialised_and_pin_unlocked); gui_activity_t* make_prefs_settings_activity(bool initialised_and_locked, gui_view_node_t** qr_mode_network_item); gui_activity_t* make_display_settings_activity(void); diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index 9f2c0d9c..581841ea 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -37,9 +37,6 @@ int sign_psbt(const network_t network_id, struct wally_psbt* psbt, const char** #define MIN_PSBT_FILE_SIZE 32 #define MAX_PSBT_FILE_SIZE MAX_INPUT_MSG_SIZE -#define VARIANT_COUNT 8 -#define MAX_ACCOUNTS 3 - static const char FW_SUFFIX[] = "_fw.bin"; static const char HASH_SUFFIX[] = ".hash"; From 8cde923073eb6ac62a366c3728abfaf438ccb412 Mon Sep 17 00:00:00 2001 From: Austin-Fulbright Date: Mon, 28 Jul 2025 20:13:01 +0000 Subject: [PATCH 15/15] added lowercase fingerprint and reformatted descriptor string --- main/usbhmsc/usbmode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c index 581841ea..0365997e 100644 --- a/main/usbhmsc/usbmode.c +++ b/main/usbhmsc/usbmode.c @@ -1005,11 +1005,11 @@ static bool export_usb_xpub_fn(const usbstorage_action_context_t* ctx) { goto cleanup; }; - map_string(fphex, toupper); + map_string(fphex, tolower); char descriptor[512]; int n = snprintf(descriptor, sizeof(descriptor), - "%s([%s/%s]%s/0/*)%s", + "%s([%s/%s]%s)%s", prefix, fphex, pathstr + 2, /* drop leading "m/" */