diff --git a/src/commands/deriveaddress/da_ui_nbgl.c b/src/commands/deriveaddress/da_ui_nbgl.c index f89fa96..2cbb744 100644 --- a/src/commands/deriveaddress/da_ui_nbgl.c +++ b/src/commands/deriveaddress/da_ui_nbgl.c @@ -20,14 +20,17 @@ #include "../../ui/ui_main.h" #include "../../ui/display.h" -#define PK_APPID_ADDR_SIZE \ - MAX_BIP32_STRING_LEN + APPLICATION_ID_STR_LEN + 13 + 1 + P2PK_ADDRESS_STRING_MAX_LEN + 9 + 1 -char pk_appid_addr[PK_APPID_ADDR_SIZE]; +char addr_buf[P2PK_ADDRESS_STRING_MAX_LEN]; +char app_id_buf[APPLICATION_ID_STR_LEN]; void ui_display_address_confirm(bool approved) { set_flow_response(approved); } +void ui_address_flow_end(void) { + set_flow_response(true); +} + int ui_display_address(derive_address_ctx_t* ctx, bool send, uint32_t app_access_token, @@ -64,44 +67,41 @@ int ui_display_address(derive_address_ctx_t* ctx, return res_error(SW_BIP32_BAD_PATH); } - int bip32_str_len = strlen(ctx->bip32_path); + int n_pairs = 0; + + pairs_global[n_pairs].item = "Derivation path"; + pairs_global[n_pairs].value = ctx->bip32_path; + n_pairs++; - memset(pk_appid_addr, 0, PK_APPID_ADDR_SIZE); - strncpy(pk_appid_addr, ctx->bip32_path, bip32_str_len); - int offset = 0; if (app_access_token != 0) { - pk_appid_addr[bip32_str_len] = '\n'; - offset += APPLICATION_ID_STR_LEN + 13; - snprintf(*(&pk_appid_addr) + bip32_str_len + 1, - APPLICATION_ID_STR_LEN + 13, - "Application: 0x%08x", - app_access_token); + pairs_global[n_pairs++] = ui_application_id_screen(app_access_token, app_id_buf); } if (!send) { - pk_appid_addr[bip32_str_len + offset] = '\n'; - strncpy(*(&pk_appid_addr) + bip32_str_len + offset + 1, "Address: ", 9); - strncpy(*(&pk_appid_addr) + bip32_str_len + offset + 1 + 9, - ctx->address, - MEMBER_SIZE(derive_address_ctx_t, address)); + strncpy(addr_buf, ctx->address, MEMBER_SIZE(derive_address_ctx_t, address)); } + pair_list.nbMaxLinesForValue = 0; + pair_list.nbPairs = n_pairs; + pair_list.pairs = pairs_global; + if (send) { // Confirm Send Address - nbgl_useCaseChoice(&WHEEL_ICON, - "Address Export", - pk_appid_addr, - "Confirm", - "Cancel", - ui_display_address_confirm); + nbgl_useCaseReviewLight(STATUS_TYPE_ADDRESS_VERIFIED, + &pair_list, + &C_app_logo_64px, + "Export Ergo address", + NULL, + "Confirm address export", + ui_display_address_confirm); } else { // Confirm Address - nbgl_useCaseChoice(&INFO_I_ICON, - "Confirm Address", - pk_appid_addr, - "Confirm", - "Cancel", - ui_display_address_confirm); + nbgl_useCaseAddressReview(addr_buf, + &pair_list, + &C_app_logo_64px, + "Verify Ergo address", + NULL, + ui_display_address_confirm); } memmove(ctx->raw_address, raw_address, P2PK_ADDRESS_LEN); @@ -116,11 +116,25 @@ int ui_display_address(derive_address_ctx_t* ctx, app_set_current_command(CMD_NONE); res_ok(); } + + if (send) { + nbgl_useCaseStatus("Address exported", true, ui_address_flow_end); + } else { + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_VERIFIED, ui_address_flow_end); + } } else { app_set_current_command(CMD_NONE); res_deny(); + + if (send) { + nbgl_useCaseStatus("Address export\ncancelled", false, ui_address_flow_end); + } else { + nbgl_useCaseReviewStatus(STATUS_TYPE_ADDRESS_REJECTED, ui_address_flow_end); + } } + io_ui_process(); + ui_menu_main(); return 0; diff --git a/src/commands/extpubkey/epk_ui_nbgl.c b/src/commands/extpubkey/epk_ui_nbgl.c index 3813081..be096d0 100644 --- a/src/commands/extpubkey/epk_ui_nbgl.c +++ b/src/commands/extpubkey/epk_ui_nbgl.c @@ -63,8 +63,8 @@ int ui_display_account(extended_public_key_ctx_t* ctx, memmove(ctx->raw_public_key, raw_pub_key, PUBLIC_KEY_LEN); memmove(ctx->chain_code, chain_code, CHAIN_CODE_LEN); - nbgl_useCaseChoice(&WARNING_ICON, - "Ext PubKey Export", + nbgl_useCaseChoice(&C_app_logo_64px, + "Export Ergo public key", pk_appid, "Confirm", "Cancel", diff --git a/src/commands/signtx/operations/stx_op_p2pk.c b/src/commands/signtx/operations/stx_op_p2pk.c index a2c61bd..1ebcce0 100644 --- a/src/commands/signtx/operations/stx_op_p2pk.c +++ b/src/commands/signtx/operations/stx_op_p2pk.c @@ -324,37 +324,28 @@ bool stx_operation_p2pk_should_show_output_confirm_screen( // =========================== // UI -#ifdef HAVE_BAGL -static NOINLINE void ui_stx_operation_p2pk_approve_action(void *context) { - sign_transaction_ui_aprove_ctx_t *ctx = (sign_transaction_ui_aprove_ctx_t *) context; - ui_stx_operation_approve_reject(true, ctx); -} -#endif - -#ifdef HAVE_NBGL -void p2pk_review(bool approved) { - set_flow_response(approved); -} -#endif +static uint8_t signtx_screen = 0; +static uint8_t signtx_outputs_screen = 0; uint16_t ui_stx_operation_p2pk_show_token_and_path(sign_transaction_operation_p2pk_ctx_t *ctx, uint32_t app_access_token, bool is_known_application, void *sign_tx_ctx) { - uint8_t screen = 0; + signtx_screen = 0; + signtx_outputs_screen = 0; #ifdef HAVE_BAGL const ux_flow_step_t *b32_step = ui_bip32_path_screen( ctx->bip32.path, ctx->bip32.len, - "P2PK Signing", + "Start Signing", ctx->ui_approve.bip32_path, MEMBER_SIZE(sign_transaction_operation_p2pk_ui_approve_data_ctx_t, bip32_path), - ui_stx_operation_p2pk_approve_action, - &ctx->ui_approve.ui_approve); + NULL, + NULL); if (b32_step == NULL) { return SW_BIP32_FORMATTING_FAILED; } - ui_add_screen(b32_step, &screen); + ui_add_screen(b32_step, &signtx_screen); #elif HAVE_NBGL bool res = ui_bip32_path_screen( ctx->bip32.path, @@ -364,34 +355,15 @@ uint16_t ui_stx_operation_p2pk_show_token_and_path(sign_transaction_operation_p2 if (!res) { return SW_BIP32_FORMATTING_FAILED; } - // pairs_global[0].item = "P2PK Signing"; - // pairs_global[0].value = ctx->ui_approve.bip32_path; - // screen++; - nbgl_useCaseReviewStreamingStart(TYPE_TRANSACTION, - &C_app_logo_64px, - "P2PK Signing", - ctx->ui_approve.bip32_path, - p2pk_review); - bool approved = io_ui_process(); - if (!approved) { - res_deny(); - ui_menu_main(); - return SW_BIP32_FORMATTING_FAILED; - } #endif if (!ui_stx_add_operation_approve_screens(&ctx->ui_approve.ui_approve, - &screen, + &signtx_screen, app_access_token, is_known_application, sign_tx_ctx)) { return SW_SCREENS_BUFFER_OVERFLOW; } -#ifdef HAVE_BAGL - if (!ui_stx_display_screens(screen)) { - return SW_SCREENS_BUFFER_OVERFLOW; - } -#endif return SW_OK; } @@ -400,20 +372,15 @@ uint16_t ui_stx_operation_p2pk_show_output_confirm_screen( CHECK_PROPER_STATES(ctx, SIGN_TRANSACTION_OPERATION_P2PK_STATE_OUTPUTS_STARTED, SIGN_TRANSACTION_OPERATION_P2PK_STATE_TX_FINISHED); - uint8_t screen = 0; if (!ui_stx_add_output_screens(&ctx->transaction.ui.ui, - &screen, + &signtx_screen, + &signtx_outputs_screen, &ctx->transaction.ui.output, &ctx->transaction.last_approved_change, ctx->network_id)) { return SW_SCREENS_BUFFER_OVERFLOW; } -#ifdef HAVE_BAGL - if (!ui_stx_display_screens(screen)) { - return SW_SCREENS_BUFFER_OVERFLOW; - } -#endif return SW_OK; } @@ -469,19 +436,19 @@ static NOINLINE uint16_t ui_stx_operation_p2pk_show_tx_screen(uint8_t index, uint16_t ui_stx_operation_p2pk_show_confirm_screen(sign_transaction_operation_p2pk_ctx_t *ctx) { CHECK_PROPER_STATE(ctx, SIGN_TRANSACTION_OPERATION_P2PK_STATE_TX_FINISHED); ctx->state = SIGN_TRANSACTION_OPERATION_P2PK_STATE_FINALIZED; - uint8_t screen = 0; if (!ui_stx_add_transaction_screens(&ctx->ui_confirm, - &screen, + &signtx_screen, + &signtx_outputs_screen, &ctx->amounts, - 1, + 0, ui_stx_operation_p2pk_show_tx_screen, ui_stx_operation_p2pk_send_response, (void *) ctx)) { return SW_SCREENS_BUFFER_OVERFLOW; } #ifdef HAVE_BAGL - if (!ui_stx_display_screens(screen)) { + if (!ui_stx_display_screens(signtx_screen)) { return SW_SCREENS_BUFFER_OVERFLOW; } #endif diff --git a/src/commands/signtx/operations/stx_op_p2pk.h b/src/commands/signtx/operations/stx_op_p2pk.h index 02d39a2..1555f8b 100644 --- a/src/commands/signtx/operations/stx_op_p2pk.h +++ b/src/commands/signtx/operations/stx_op_p2pk.h @@ -40,11 +40,11 @@ typedef struct { cx_blake2b_t tx_hash; uint8_t network_id; sign_transaction_amounts_ctx_t amounts; - union { - sign_transaction_operation_p2pk_transaction_ctx_t transaction; - sign_transaction_operation_p2pk_ui_approve_data_ctx_t ui_approve; - sign_transaction_ui_sign_confirm_ctx_t ui_confirm; - }; + + sign_transaction_operation_p2pk_transaction_ctx_t transaction; + sign_transaction_operation_p2pk_ui_approve_data_ctx_t ui_approve; + sign_transaction_ui_sign_confirm_ctx_t ui_confirm; + } sign_transaction_operation_p2pk_ctx_t; //****************** OPERATION CALLS **************** diff --git a/src/commands/signtx/stx_ui.h b/src/commands/signtx/stx_ui.h index 4f60e22..0d9ebf7 100644 --- a/src/commands/signtx/stx_ui.h +++ b/src/commands/signtx/stx_ui.h @@ -28,6 +28,7 @@ bool ui_stx_add_operation_approve_screens(sign_transaction_ui_aprove_ctx_t* ctx, */ bool ui_stx_add_output_screens(sign_transaction_ui_output_confirm_ctx_t* ctx, uint8_t* screen, + uint8_t* output_screen, const sign_transaction_output_info_ctx_t* output, sign_transaction_bip32_path_t* last_approved_change, uint8_t network_id); @@ -40,6 +41,7 @@ bool ui_stx_add_output_screens(sign_transaction_ui_output_confirm_ctx_t* ctx, */ bool ui_stx_add_transaction_screens(sign_transaction_ui_sign_confirm_ctx_t* ctx, uint8_t* screen, + uint8_t* output_screen, const sign_transaction_amounts_ctx_t* amounts, uint8_t op_screen_count, ui_sign_transaction_operation_show_screen_cb screen_cb, diff --git a/src/commands/signtx/stx_ui_bagl.c b/src/commands/signtx/stx_ui_bagl.c index 07c67c1..f7a9291 100644 --- a/src/commands/signtx/stx_ui_bagl.c +++ b/src/commands/signtx/stx_ui_bagl.c @@ -17,9 +17,11 @@ #include "../../ergo/address.h" #include "../../ui/ui_application_id.h" #include "../../ui/ui_approve_reject.h" +#include "../../ui/ui_sign_reject.h" #include "../../ui/ui_dynamic_flow.h" #include "../../ui/ui_menu.h" #include "../../ui/ui_main.h" +#include "../../ui/display.h" // ----- OPERATION APPROVE / REJECT FLOW @@ -32,6 +34,7 @@ void ui_stx_operation_approve_reject(bool approved, sign_transaction_ui_aprove_c app_set_connected_app_id(ctx->app_token_value); sign_tx->state = SIGN_TRANSACTION_STATE_APPROVED; send_response_sign_transaction_session_id(sign_tx->session); + return; } else { app_set_current_command(CMD_NONE); res_deny(); @@ -59,19 +62,13 @@ bool ui_stx_add_operation_approve_screens(sign_transaction_ui_aprove_ctx_t* ctx, ctx->sign_tx_context = sign_tx; ctx->is_known_application = is_known_application; - ui_approve_reject_screens(ui_stx_operation_approve_action, - ctx, - ui_next_sreen_ptr(screen), - ui_next_sreen_ptr(screen)); + ui_stx_operation_approve_action(true, ctx); return true; } // --- OUTPUT APPROVE / REJECT FLOW -// Fist flow step with icon and text -UX_STEP_NOCB(ux_stx_display_output_confirm_step, pn, {&C_icon_warning, "Confirm Output"}); - static NOINLINE void ui_stx_operation_output_confirm_action(bool approved, void* context) { sign_transaction_ui_output_confirm_ctx_t* ctx = (sign_transaction_ui_output_confirm_ctx_t*) context; @@ -87,6 +84,7 @@ static NOINLINE void ui_stx_operation_output_confirm_action(bool approved, void* sizeof(sign_transaction_bip32_path_t)); } res_ok(); + return; } else { app_set_current_command(CMD_NONE); res_deny(); @@ -95,8 +93,15 @@ static NOINLINE void ui_stx_operation_output_confirm_action(bool approved, void* ui_menu_main(); } +uint16_t ui_stx_dynamic_display(uint8_t screen, char* title, char* text) { + strncpy(title, pair_mem_title[screen], 20); + strncpy(text, pair_mem_text[screen], 70); + return SW_OK; +} + bool ui_stx_add_output_screens(sign_transaction_ui_output_confirm_ctx_t* ctx, uint8_t* screen, + uint8_t* output_screen, const sign_transaction_output_info_ctx_t* output, sign_transaction_bip32_path_t* last_approved_change, uint8_t network_id) { @@ -105,41 +110,47 @@ bool ui_stx_add_output_screens(sign_transaction_ui_output_confirm_ctx_t* ctx, memset(ctx, 0, sizeof(sign_transaction_ui_output_confirm_ctx_t)); memset(last_approved_change, 0, sizeof(sign_transaction_bip32_path_t)); - ui_add_screen(&ux_stx_display_output_confirm_step, screen); - uint8_t info_screen_count = 1; // Address screen if (stx_output_info_type(output) != SIGN_TRANSACTION_OUTPUT_INFO_TYPE_BIP32) { uint8_t tokens_count = stx_output_info_used_tokens_count(output); info_screen_count += 1 + (2 * tokens_count); // value screen + tokens (2 for each) } - if (!ui_add_dynamic_flow_screens(screen, - info_screen_count, - ctx->title, - ctx->text, - &ui_stx_display_output_state, - (void*) ctx)) - return false; + ctx->network_id = network_id; + ctx->output = output; + ctx->last_approved_change = last_approved_change; + + ui_stx_operation_output_confirm_action(true, ctx); + + int pair_index = *output_screen; + + for (int i = 0; i < info_screen_count; i++) { + ui_stx_display_output_state(i, + pair_mem_title[pair_index], + pair_mem_text[pair_index], + (void*) ctx); + pair_index++; + } + + *output_screen = pair_index; if (MAX_NUMBER_OF_SCREENS - *screen < 2) return false; - ui_approve_reject_screens(ui_stx_operation_output_confirm_action, - (void*) ctx, - ui_next_sreen_ptr(screen), - ui_next_sreen_ptr(screen)); + explicit_bzero(ctx->last_approved_change, sizeof(sign_transaction_bip32_path_t)); - ctx->network_id = network_id; - ctx->output = output; - ctx->last_approved_change = last_approved_change; + // store last approved change address + if (stx_output_info_type(ctx->output) == SIGN_TRANSACTION_OUTPUT_INFO_TYPE_BIP32) { + memmove(ctx->last_approved_change, + &ctx->output->bip32_path, + sizeof(sign_transaction_bip32_path_t)); + } + res_ok(); return true; } // --- TX ACCEPT / REJECT FLOW -// Fist flow step with icon and text -UX_STEP_NOCB(ux_stx_display_sign_confirm_step, pn, {&C_icon_warning, "Approve Signing"}); - // TX approve/reject callback static NOINLINE void ui_stx_operation_execute_action(bool approved, void* context) { app_set_ui_busy(false); @@ -157,6 +168,7 @@ static NOINLINE void ui_stx_operation_execute_action(bool approved, void* contex bool ui_stx_add_transaction_screens(sign_transaction_ui_sign_confirm_ctx_t* ctx, uint8_t* screen, + uint8_t* output_screen, const sign_transaction_amounts_ctx_t* amounts, uint8_t op_screen_count, ui_sign_transaction_operation_show_screen_cb screen_cb, @@ -168,28 +180,37 @@ bool ui_stx_add_transaction_screens(sign_transaction_ui_sign_confirm_ctx_t* ctx, uint8_t tokens_count = stx_amounts_non_zero_tokens_count(amounts); - ui_add_screen(&ux_stx_display_sign_confirm_step, screen); + ctx->op_screen_count = op_screen_count; + ctx->op_screen_cb = screen_cb; + ctx->op_response_cb = response_cb; + ctx->op_cb_context = cb_context; + ctx->amounts = amounts; + + int pairs_count = op_screen_count + 1 + (2 * tokens_count); + int pair_index = *output_screen; + + for (int i = 0; i < pairs_count; i++) { + ui_stx_display_tx_state(i, + pair_mem_title[pair_index], + pair_mem_text[pair_index], + (void*) ctx); + + pair_index++; + } if (!ui_add_dynamic_flow_screens(screen, - op_screen_count + 2 + (2 * tokens_count), + pair_index, ctx->title, ctx->text, - &ui_stx_display_tx_state, - (void*) ctx)) + &ui_stx_dynamic_display)) return false; if (MAX_NUMBER_OF_SCREENS - *screen < 2) return false; - ui_approve_reject_screens(ui_stx_operation_execute_action, - ctx, - ui_next_sreen_ptr(screen), - ui_next_sreen_ptr(screen)); - - ctx->op_screen_count = op_screen_count; - ctx->op_screen_cb = screen_cb; - ctx->op_response_cb = response_cb; - ctx->op_cb_context = cb_context; - ctx->amounts = amounts; + ui_sign_reject_screens(ui_stx_operation_execute_action, + ctx, + ui_next_sreen_ptr(screen), + ui_next_sreen_ptr(screen)); return true; } diff --git a/src/commands/signtx/stx_ui_common.c b/src/commands/signtx/stx_ui_common.c index 8cdfb01..322f90a 100644 --- a/src/commands/signtx/stx_ui_common.c +++ b/src/commands/signtx/stx_ui_common.c @@ -24,7 +24,7 @@ uint16_t ui_stx_display_output_state(uint8_t screen, char* title, char* text, vo text, text_len); case 1: { // Output Value - strncpy(title, "Output Value", title_len); + strncpy(title, "Amount", title_len); if (!format_erg_amount(ctx->output->value, text, text_len)) { return SW_BUFFER_ERROR; } @@ -45,7 +45,7 @@ uint16_t ui_stx_display_output_state(uint8_t screen, char* title, char* text, vo return SW_ADDRESS_FORMATTING_FAILED; } } else { // Token Value - snprintf(title, title_len, "Token [%d] Value", (int) (screen / 2) + 1); + snprintf(title, title_len, "Token [%d] Raw Value", (int) (screen / 2) + 1); format_u64(text, text_len, ctx->output->tokens[token_idx]); } break; @@ -67,22 +67,22 @@ uint16_t ui_stx_display_tx_state(uint8_t screen, char* title, char* text, void* } screen -= ctx->op_screen_count; switch (screen) { - case 0: { // TX Value + /*case 0: { // TX Value strncpy(title, "Transaction Amount", title_len); if (!format_erg_amount(ctx->amounts->value, text, text_len)) { return SW_BUFFER_ERROR; } break; - } - case 1: { // TX Fee - strncpy(title, "Transaction Fee", title_len); + }*/ + case 0: { // TX Fee + strncpy(title, "Fee", title_len); if (!format_erg_amount(ctx->amounts->fee, text, text_len)) { return SW_BUFFER_ERROR; } break; } default: { // Tokens - screen -= 2; // Decrease index for info screens + screen -= 1; // Decrease index for info screens uint8_t token_idx = stx_amounts_non_zero_token_index(ctx->amounts, screen / 2); if (!IS_ELEMENT_FOUND(token_idx)) { // error. bad index state return SW_BAD_TOKEN_INDEX; @@ -96,7 +96,7 @@ uint16_t ui_stx_display_tx_state(uint8_t screen, char* title, char* text, void* return SW_ADDRESS_FORMATTING_FAILED; } } else { // Token Value - snprintf(title, title_len, "Token [%d] Value", (int) (screen / 2) + 1); + snprintf(title, title_len, "Token [%d] Raw Value", (int) (screen / 2) + 1); int64_t value = ctx->amounts->tokens[token_idx]; if (value < 0) { // output > inputs STRING_ADD_STATIC_TEXT(text, text_len, "Minting: "); diff --git a/src/commands/signtx/stx_ui_common.h b/src/commands/signtx/stx_ui_common.h index 847063e..f4a4b3f 100644 --- a/src/commands/signtx/stx_ui_common.h +++ b/src/commands/signtx/stx_ui_common.h @@ -70,7 +70,7 @@ static inline uint16_t output_info_print_address(const sign_transaction_output_i } case SIGN_TRANSACTION_OUTPUT_INFO_TYPE_ADDRESS: { uint8_t raw_address[P2PK_ADDRESS_LEN]; - strncpy(title, "Address", title_len); + strncpy(title, "To", title_len); if (!ergo_address_from_compressed_pubkey(network_id, ctx->public_key, raw_address)) { return SW_ADDRESS_GENERATION_FAILED; } diff --git a/src/commands/signtx/stx_ui_nbgl.c b/src/commands/signtx/stx_ui_nbgl.c index b0f454c..1d94cd0 100644 --- a/src/commands/signtx/stx_ui_nbgl.c +++ b/src/commands/signtx/stx_ui_nbgl.c @@ -39,7 +39,7 @@ bool ui_stx_add_operation_approve_screens(sign_transaction_ui_aprove_ctx_t* ctx, sign_transaction_ctx_t* sign_tx) { if (MAX_NUMBER_OF_SCREENS - *screen < 3) return false; - int n_pairs = *screen; + int n_pairs = 0; if (!is_known_application && app_access_token != 0) { pairs_global[n_pairs++] = ui_application_id_screen(app_access_token, ctx->app_token); @@ -49,42 +49,16 @@ bool ui_stx_add_operation_approve_screens(sign_transaction_ui_aprove_ctx_t* ctx, ctx->sign_tx_context = sign_tx; ctx->is_known_application = is_known_application; - pair_list.nbMaxLinesForValue = 0; pair_list.nbPairs = n_pairs; - pair_list.pairs = pairs_global; - bool approved = true; - if (n_pairs > 0) { - nbgl_useCaseReviewStreamingContinue(&pair_list, ui_stx_operation_approve_action); - approved = io_ui_process(); - } - - ui_stx_operation_approve_reject(approved, ctx); + ui_stx_operation_approve_reject(true, ctx); return true; } -static nbgl_layoutTagValue_t pair; -static sign_transaction_ui_output_confirm_ctx_t* output_screen_ctx = NULL; - -static nbgl_layoutTagValue_t* getOutputPair(uint8_t index) { - if ((index + 1) % 3 == 0) { - pair.item = ""; - pair.value = ""; - } else { - pair.item = pair_mem_title[index]; - pair.value = pair_mem_text[index]; - - ui_stx_display_output_state(index - index / 3, - pair_mem_title[index], - pair_mem_text[index], - (void*) output_screen_ctx); - } - return &pair; -} - bool ui_stx_add_output_screens(sign_transaction_ui_output_confirm_ctx_t* ctx, uint8_t* screen, + uint8_t* output_screen, const sign_transaction_output_info_ctx_t* output, sign_transaction_bip32_path_t* last_approved_change, uint8_t network_id) { @@ -105,57 +79,39 @@ bool ui_stx_add_output_screens(sign_transaction_ui_output_confirm_ctx_t* ctx, ctx->output = output; ctx->last_approved_change = last_approved_change; - pair_list.nbMaxLinesForValue = 0; - pair_list.pairs = NULL; - pair_list.nbPairs = info_screen_count + info_screen_count / 2; - pair_list.callback = getOutputPair; - pair_list.startIndex = 0; + int pair_index = pair_list.nbPairs; - output_screen_ctx = ctx; + for (int i = 0; i < info_screen_count; i++) { + pairs_global[pair_index].item = pair_mem_title[pair_index]; + pairs_global[pair_index].value = pair_mem_text[pair_index]; - nbgl_useCaseReviewStreamingContinue(&pair_list, ui_stx_operation_approve_action); + ui_stx_display_output_state(i, + pair_mem_title[pair_index], + pair_mem_text[pair_index], + (void*) ctx); + pair_index++; + } - bool approved = io_ui_process(); - app_set_ui_busy(false); - output_screen_ctx = NULL; + *output_screen = pair_index; - if (!approved) { - app_set_current_command(CMD_NONE); - res_deny(); - ui_menu_main(); - return true; - } + pair_list.nbPairs += info_screen_count; explicit_bzero(ctx->last_approved_change, sizeof(sign_transaction_bip32_path_t)); - if (approved) { - // store last approved change address - if (stx_output_info_type(ctx->output) == SIGN_TRANSACTION_OUTPUT_INFO_TYPE_BIP32) { - memmove(ctx->last_approved_change, - &ctx->output->bip32_path, - sizeof(sign_transaction_bip32_path_t)); - } - res_ok(); + // store last approved change address + if (stx_output_info_type(ctx->output) == SIGN_TRANSACTION_OUTPUT_INFO_TYPE_BIP32) { + memmove(ctx->last_approved_change, + &ctx->output->bip32_path, + sizeof(sign_transaction_bip32_path_t)); } + res_ok(); return true; } -static sign_transaction_ui_sign_confirm_ctx_t* sign_confirm_screen_ctx = NULL; - -static nbgl_layoutTagValue_t* getSignConfirmPair(uint8_t index) { - pair.item = pair_mem_title[index]; - pair.value = pair_mem_text[index]; - - ui_stx_display_tx_state(index, - pair_mem_title[index], - pair_mem_text[index], - (void*) sign_confirm_screen_ctx); - return &pair; -} - bool ui_stx_add_transaction_screens(sign_transaction_ui_sign_confirm_ctx_t* ctx, uint8_t* screen, + uint8_t* output_screen, const sign_transaction_amounts_ctx_t* amounts, uint8_t op_screen_count, ui_sign_transaction_operation_show_screen_cb screen_cb, @@ -165,6 +121,9 @@ bool ui_stx_add_transaction_screens(sign_transaction_ui_sign_confirm_ctx_t* ctx, memset(ctx, 0, sizeof(sign_transaction_ui_sign_confirm_ctx_t)); + sign_transaction_operation_p2pk_ctx_t* base_ctx = + (sign_transaction_operation_p2pk_ctx_t*) cb_context; + uint8_t tokens_count = stx_amounts_non_zero_tokens_count(amounts); ctx->op_screen_count = op_screen_count; @@ -173,31 +132,36 @@ bool ui_stx_add_transaction_screens(sign_transaction_ui_sign_confirm_ctx_t* ctx, ctx->op_cb_context = cb_context; ctx->amounts = amounts; - pair_list.nbMaxLinesForValue = 0; - pair_list.pairs = NULL; - pair_list.nbPairs = op_screen_count + 2 + (2 * tokens_count); - pair_list.callback = getSignConfirmPair; - pair_list.startIndex = 0; - - sign_confirm_screen_ctx = ctx; + int pairs_count = op_screen_count + 1 + (2 * tokens_count); + int pair_index = pair_list.nbPairs; - nbgl_useCaseReviewStreamingContinue(&pair_list, ui_stx_operation_approve_action); + for (int i = 0; i < pairs_count; i++) { + pairs_global[pair_index].item = pair_mem_title[pair_index]; + pairs_global[pair_index].value = pair_mem_text[pair_index]; - bool approved = io_ui_process(); - app_set_ui_busy(false); - sign_confirm_screen_ctx = NULL; + ui_stx_display_tx_state(i, + pair_mem_title[pair_index], + pair_mem_text[pair_index], + (void*) ctx); - if (!approved) { - res_deny(); - app_set_current_command(CMD_NONE); - ui_menu_main(); - return true; + pair_index++; } - if (MAX_NUMBER_OF_SCREENS - *screen < 2) return false; + *output_screen = pair_index; - nbgl_useCaseReviewStreamingFinish("Approve Signing", ui_stx_operation_approve_action); - approved = io_ui_process(); + pair_list.nbMaxLinesForValue = 0; + pair_list.pairs = pairs_global; + pair_list.nbPairs = pair_index; + pair_list.startIndex = 0; + + nbgl_useCaseReview(TYPE_TRANSACTION, + &pair_list, + &C_app_logo_64px, + "Start Signing", + base_ctx->ui_approve.bip32_path, + "Sign transaction", + ui_stx_operation_approve_action); + bool approved = io_ui_process(); if (approved) { ctx->op_response_cb(ctx->op_cb_context); diff --git a/src/constants.h b/src/constants.h index 8f0290a..a1565f4 100644 --- a/src/constants.h +++ b/src/constants.h @@ -83,7 +83,7 @@ /** * Max number of screens */ -#define MAX_NUMBER_OF_SCREENS 8 +#define MAX_NUMBER_OF_SCREENS 16 /** * Max length of TX data part diff --git a/src/ui/display.h b/src/ui/display.h index c924075..a433ea3 100644 --- a/src/ui/display.h +++ b/src/ui/display.h @@ -11,12 +11,11 @@ void set_flow_response(bool response); void io_common_process(); bool io_ui_process(); -#ifdef HAVE_NBGL -#define N_UX_PAIRS 10 +#define N_UX_PAIRS TOKEN_MAX_COUNT * 2 + 10 +static char pair_mem_title[N_UX_PAIRS][20]; +static char pair_mem_text[N_UX_PAIRS][70]; +#ifdef HAVE_NBGL static nbgl_layoutTagValueList_t pair_list; extern nbgl_layoutTagValue_t pairs_global[N_UX_PAIRS]; - -static char pair_mem_title[N_UX_PAIRS][20]; -static char pair_mem_text[N_UX_PAIRS][70]; #endif \ No newline at end of file diff --git a/src/ui/ui_dynamic_flow.c b/src/ui/ui_dynamic_flow.c index d1088cd..8c7d475 100644 --- a/src/ui/ui_dynamic_flow.c +++ b/src/ui/ui_dynamic_flow.c @@ -15,7 +15,6 @@ struct ui_dynamic_flow_ctx_t { uint8_t screen_count; uint8_t current_screen; ui_dynamic_flow_show_screen_cb show_cb; - void *cb_context; }; static ux_layout_bnnn_paging_params_t G_ui_dynamic_step_params[1]; @@ -35,8 +34,7 @@ void bnnn_paging_edgecase() { do { \ uint16_t res = G_dynamic_flow_context.show_cb(G_dynamic_flow_context.current_screen, \ (char *) G_ui_dynamic_step_params[0].title, \ - (char *) G_ui_dynamic_step_params[0].text, \ - G_dynamic_flow_context.cb_context); \ + (char *) G_ui_dynamic_step_params[0].text); \ if (res == SW_OK) { \ switch_method(); \ } else { \ @@ -91,14 +89,12 @@ bool ui_add_dynamic_flow_screens(uint8_t *screen, uint8_t dynamic_screen_count, char *title_storage, char *text_storage, - ui_dynamic_flow_show_screen_cb show_cb, - void *cb_ctx) { + ui_dynamic_flow_show_screen_cb show_cb) { if (MAX_NUMBER_OF_SCREENS - *screen < 3) return false; if (dynamic_screen_count == 0 || dynamic_screen_count == INDEX_NOT_EXIST) return false; G_ui_dynamic_step_params[0].title = title_storage; G_ui_dynamic_step_params[0].text = text_storage; - G_dynamic_flow_context.cb_context = cb_ctx; G_dynamic_flow_context.screen_count = dynamic_screen_count; G_dynamic_flow_context.current_screen = INDEX_NOT_EXIST; G_dynamic_flow_context.show_cb = show_cb; diff --git a/src/ui/ui_dynamic_flow.h b/src/ui/ui_dynamic_flow.h index a4aeb0d..c79a12e 100644 --- a/src/ui/ui_dynamic_flow.h +++ b/src/ui/ui_dynamic_flow.h @@ -4,14 +4,13 @@ #include #include -typedef uint16_t (*ui_dynamic_flow_show_screen_cb)(uint8_t, char *, char *, void *); +typedef uint16_t (*ui_dynamic_flow_show_screen_cb)(uint8_t, char *, char *); // Global context pointer will be set to the dynamic flow context. Don't change it. bool ui_add_dynamic_flow_screens(uint8_t *screen, uint8_t dynamic_screen_count, char *title_storage, char *text_storage, - ui_dynamic_flow_show_screen_cb show_cb, - void *cb_ctx); + ui_dynamic_flow_show_screen_cb show_cb); #endif \ No newline at end of file diff --git a/src/ui/ui_menu_nbgl.c b/src/ui/ui_menu_nbgl.c index 26d10af..e370d27 100644 --- a/src/ui/ui_menu_nbgl.c +++ b/src/ui/ui_menu_nbgl.c @@ -5,7 +5,7 @@ #include #include -#define APPTAGLINE "Ergo app for ledger" +#define APPTAGLINE "This app enables signing\ntransactions on the Ergo\nnetwork." #define APPCOPYRIGHT "Ergo App (c) 2024" #define SETTING_INFO_NB 2 diff --git a/src/ui/ui_sign_reject.c b/src/ui/ui_sign_reject.c new file mode 100644 index 0000000..feffe59 --- /dev/null +++ b/src/ui/ui_sign_reject.c @@ -0,0 +1,34 @@ +#ifdef HAVE_BAGL +#include +#include "ui_sign_reject.h" + +static ui_sign_reject_callback G_ui_sign_reject_callback; +static void* G_ui_sign_reject_callback_context; + +// Step with sign button +UX_STEP_CB(ux_sign_step, + pb, + G_ui_sign_reject_callback(true, G_ui_sign_reject_callback_context), + { + &C_icon_validate_14, + "Sign transaction", + }); +// Step with reject button +UX_STEP_CB(ux_sign_reject_step, + pb, + G_ui_sign_reject_callback(false, G_ui_sign_reject_callback_context), + { + &C_icon_crossmark, + "Reject", + }); + +void ui_sign_reject_screens(ui_sign_reject_callback cb, + void* context, + const ux_flow_step_t** sign, + const ux_flow_step_t** reject) { + G_ui_sign_reject_callback = cb; + G_ui_sign_reject_callback_context = context; + *sign = &ux_sign_step; + *reject = &ux_sign_reject_step; +} +#endif \ No newline at end of file diff --git a/src/ui/ui_sign_reject.h b/src/ui/ui_sign_reject.h new file mode 100644 index 0000000..11f4fe0 --- /dev/null +++ b/src/ui/ui_sign_reject.h @@ -0,0 +1,13 @@ +#pragma once +#ifdef HAVE_BAGL +#include +#include + +typedef void (*ui_sign_reject_callback)(bool, void*); + +void ui_sign_reject_screens(ui_sign_reject_callback cb, + void* context, + const ux_flow_step_t** sign, + const ux_flow_step_t** reject); + +#endif \ No newline at end of file diff --git a/tests/helpers/flow.js b/tests/helpers/flow.js index 3311fd6..1c67786 100644 --- a/tests/helpers/flow.js +++ b/tests/helpers/flow.js @@ -69,6 +69,8 @@ class AuthTokenFlows { for (let i = 0; i < flowsCount; i++) { let flow = await this.screens.readFlow(); flows.push(mergePagedScreens(flow)); + // try to click on "Sign transaction" button first + await this.screens.clickOn('Sign transaction'); await this.screens.clickOn('Approve'); if (i != flowsCount - 1 && await this.screens.isReadyMainScreen()) { // we have more flows this.screens.removeCurrentScreen(); // Wait for new screen in the readFlow diff --git a/tests/helpers/hooks.js b/tests/helpers/hooks.js index 22f31b9..81dfa33 100644 --- a/tests/helpers/hooks.js +++ b/tests/helpers/hooks.js @@ -17,8 +17,8 @@ exports.mochaHooks = { if (!this.model) { throw new Error("No model. Provide model with --model= parameter"); } - if (["nanox", "nanosp", "flex", "stax", "hid"].indexOf(this.model) < 0) { - throw new Error("Unknown model: " + this.model + ", supports: nanos, nanox, nanosp, hid"); + if (["nanox", "nanosp", "hid"].indexOf(this.model) < 0) { + throw new Error("Unknown model: " + this.model + ", supports: nanox, nanosp, hid"); } if (this.model === "hid") { this.transport = await HidTransport.create(); diff --git a/tests/transaction-tests.js b/tests/transaction-tests.js index 66f42e2..e15896d 100644 --- a/tests/transaction-tests.js +++ b/tests/transaction-tests.js @@ -17,33 +17,24 @@ function signTxFlows({ device }, auth, from, to, change, tokens_to = undefined, } flows[i++].push({ header: null, body: 'Approve' }, { header: null, body: 'Reject' }); // accept tx screen - flows[i] = [{ header: 'P2PK Signing', body: removeMasterNode(from.path.toString()) }]; - /*if (!auth) { - flows[i].push({ header: 'Application', body: '0x00000000' }); - }*/ - flows[i++].push({ header: null, body: 'Approve' }, { header: null, body: 'Reject' }); + if (to || change) { + flows[i] = [{ header: 'Start Signing', body: removeMasterNode(from.path.toString()) }]; + } // output screen if (to) { - flows[i] = [{ header: null, body: 'Confirm Output' }, - { header: 'Address', body: to.toBase58() }, - { header: 'Output Value', body: '0.100000000 ERG' }]; + flows[i].push(...[ + { header: 'To', body: to.toBase58() }, + { header: 'Amount', body: '0.100000000 ERG' }]); if (tokens_to) { flows[i].push(...tokens_to); } - flows[i++].push({ header: null, body: 'Approve' }, { header: null, body: 'Reject' }); } // change screen if (change && (from.acc_index != change.acc_index || change.addr_index >= 19)) { - flows[i++] = [{ header: null, body: 'Confirm Output' }, - { header: 'Change', body: removeMasterNode(change.path.toString()) }, - { header: null, body: 'Approve' }, - { header: null, body: 'Reject' }]; + flows[i].push({ header: 'Change', body: removeMasterNode(change.path.toString()) }); } if (to && change) { - flows[i] = [{ header: null, body: 'Approve Signing' }, - { header: 'P2PK Path', body: removeMasterNode(from.path.toString()) }, - { header: 'Transaction Amount', body: '0.100000000 ERG' }, - { header: 'Transaction Fee', body: '0.001000000 ERG' }]; + flows[i].push({ header: 'Fee', body: '0.001000000 ERG' }); if (tokens_tx) { flows[i].push(...tokens_tx); } - flows[i++].push({ header: null, body: 'Approve' }, { header: null, body: 'Reject' }); + flows[i++].push({ header: null, body: 'Sign transaction' }, { header: null, body: 'Reject' }); } return flows; } @@ -240,7 +231,7 @@ describe("Transaction Tests", function () { .build(); const tokensFlow = [ { header: 'Token [1]', body: ellipsize(test.model, tokenId) }, - { header: 'Token [1] Value', body: '1000' } + { header: 'Token [1] Raw Value', body: '1000' } ]; const expectedFlows = signTxFlows(test, auth, from, to, change, tokensFlow); return { appTx, ergoTx, input: uInputs[0], @@ -270,7 +261,7 @@ describe("Transaction Tests", function () { .build(); const tokensFlow = [ { header: 'Token [1]', body: ellipsize(test.model, tokenId) }, - { header: 'Token [1] Value', body: 'Burning: 1000' } + { header: 'Token [1] Raw Value', body: 'Burning: 1000' } ]; const expectedFlows = signTxFlows(test, auth, from, to, change, null, tokensFlow); return { appTx, ergoTx, input: uInputs[0], @@ -298,11 +289,11 @@ describe("Transaction Tests", function () { const tokenId = uInputs[0].box_id().to_str().toUpperCase(); const tokensOutFlow = [ { header: 'Token [1]', body: ellipsize(test.model, tokenId) }, - { header: 'Token [1] Value', body: '1000' } + { header: 'Token [1] Raw Value', body: '1000' } ]; const tokensTxFlow = [ { header: 'Token [1]', body: ellipsize(test.model, tokenId) }, - { header: 'Token [1] Value', body: 'Minting: 1000' } + { header: 'Token [1] Raw Value', body: 'Minting: 1000' } ]; const expectedFlows = signTxFlows(test, auth, from, to, change, tokensOutFlow, tokensTxFlow); return { appTx, ergoTx, input: uInputs[0], @@ -333,13 +324,13 @@ describe("Transaction Tests", function () { const oTokenId = uInputs[0].box_id().to_str().toUpperCase(); const tokensOutFlow = [ { header: 'Token [1]', body: ellipsize(test.model, oTokenId) }, - { header: 'Token [1] Value', body: '5678' } + { header: 'Token [1] Raw Value', body: '5678' } ]; const tokensTxFlow = [ { header: 'Token [1]', body: ellipsize(test.model, oTokenId) }, - { header: 'Token [1] Value', body: 'Minting: 5678' }, + { header: 'Token [1] Raw Value', body: 'Minting: 5678' }, { header: 'Token [2]', body: ellipsize(test.model, iTokenId) }, - { header: 'Token [2] Value', body: 'Burning: 1234' } + { header: 'Token [2] Raw Value', body: 'Burning: 1234' } ]; const expectedFlows = signTxFlows(test, auth, from, to, change, tokensOutFlow, tokensTxFlow); return { appTx, ergoTx, input: uInputs[0],