2737 lines
80 KiB
C
2737 lines
80 KiB
C
/*******************************************************************************
|
|
* Ledger Blue - Bitcoin Wallet
|
|
* (c) 2016 Ledger
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
********************************************************************************/
|
|
|
|
#include "os.h"
|
|
#include "cx.h"
|
|
|
|
#include "os_io_seproxyhal.h"
|
|
#include "string.h"
|
|
|
|
#include "btchip_internal.h"
|
|
|
|
#include "btchip_bagl_extensions.h"
|
|
|
|
#include "segwit_addr.h"
|
|
|
|
#include "glyphs.h"
|
|
|
|
#define __NAME3(a, b, c) a##b##c
|
|
#define NAME3(a, b, c) __NAME3(a, b, c)
|
|
|
|
#ifdef HAVE_U2F
|
|
|
|
#include "u2f_service.h"
|
|
#include "u2f_transport.h"
|
|
|
|
volatile unsigned char u2fMessageBuffer[U2F_MAX_MESSAGE_SIZE];
|
|
|
|
extern void USB_power_U2F(unsigned char enabled, unsigned char fido);
|
|
extern bool fidoActivated;
|
|
volatile uint8_t fidoTransport;
|
|
|
|
#endif
|
|
|
|
bagl_element_t tmp_element;
|
|
|
|
unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B];
|
|
|
|
#define BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH 10
|
|
#define BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH 8
|
|
#define MAX_CHAR_PER_LINE 25
|
|
|
|
#define COLOR_BG_1 0xF9F9F9
|
|
#define COLOR_APP COLOR_HDR // bitcoin 0xFCB653
|
|
#define COLOR_APP_LIGHT COLOR_DB // bitcoin 0xFEDBA9
|
|
|
|
#if defined(TARGET_BLUE)
|
|
#include "qrcodegen.h"
|
|
|
|
union {
|
|
struct {
|
|
char addressSummary[40]; // beginning of the output address ... end of
|
|
char fullAmount[65]; // full amount
|
|
char fullAddress[65];
|
|
// the address
|
|
char feesAmount[40]; // fees
|
|
} tmp;
|
|
|
|
struct {
|
|
char addressSummary[MAX_CHAR_PER_LINE + 1];
|
|
bagl_icon_details_t icon_details;
|
|
unsigned int colors[2];
|
|
unsigned char qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(3)];
|
|
} tmpqr;
|
|
|
|
unsigned int dummy; // ensure the whole vars is aligned for the CM0 to
|
|
// operate correctly
|
|
} vars;
|
|
|
|
#else
|
|
|
|
union {
|
|
struct {
|
|
// char addressSummary[40]; // beginning of the output address ... end
|
|
// of
|
|
|
|
char fullAddress[43]; // the address
|
|
char fullAmount[20]; // full amount
|
|
char feesAmount[20]; // fees
|
|
} tmp;
|
|
|
|
/*
|
|
struct {
|
|
bagl_icon_details_t icon_details;
|
|
unsigned int colors[2];
|
|
unsigned char qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(3)];
|
|
} tmpqr;
|
|
|
|
unsigned int dummy; // ensure the whole vars is aligned for the CM0 to
|
|
operate correctly
|
|
*/
|
|
} vars;
|
|
#endif
|
|
|
|
unsigned int io_seproxyhal_touch_verify_cancel(const bagl_element_t *e);
|
|
unsigned int io_seproxyhal_touch_verify_ok(const bagl_element_t *e);
|
|
unsigned int
|
|
io_seproxyhal_touch_message_signature_verify_cancel(const bagl_element_t *e);
|
|
unsigned int
|
|
io_seproxyhal_touch_message_signature_verify_ok(const bagl_element_t *e);
|
|
unsigned int io_seproxyhal_touch_display_cancel(const bagl_element_t *e);
|
|
unsigned int io_seproxyhal_touch_display_ok(const bagl_element_t *e);
|
|
unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e);
|
|
void ui_idle(void);
|
|
|
|
ux_state_t ux;
|
|
|
|
// display stepped screens
|
|
unsigned int ux_step;
|
|
unsigned int ux_step_count;
|
|
|
|
#ifdef HAVE_U2F
|
|
volatile u2f_service_t u2fService;
|
|
|
|
void u2f_proxy_response(u2f_service_t *service, unsigned int tx) {
|
|
os_memset(service->messageBuffer, 0, 5);
|
|
os_memmove(service->messageBuffer + 5, G_io_apdu_buffer, tx);
|
|
service->messageBuffer[tx + 5] = 0x90;
|
|
service->messageBuffer[tx + 6] = 0x00;
|
|
u2f_send_fragmented_response(service, U2F_CMD_MSG, service->messageBuffer,
|
|
tx + 7, true);
|
|
}
|
|
|
|
#endif
|
|
const bagl_element_t *ui_menu_item_out_over(const bagl_element_t *e) {
|
|
// the selection rectangle is after the none|touchable
|
|
e = (const bagl_element_t *)(((unsigned int)e) + sizeof(bagl_element_t));
|
|
return e;
|
|
}
|
|
|
|
#if defined(TARGET_BLUE)
|
|
|
|
unsigned int io_seproxyhal_touch_settings(const bagl_element_t *e);
|
|
const bagl_element_t ui_idle_blue[] = {
|
|
// type userid x y w h str rad
|
|
// fill fg bg fid iid txt touchparams... ]
|
|
{{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
// erase screen (only under the status bar)
|
|
{{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP,
|
|
COLOR_APP, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
/// TOP STATUS BAR
|
|
{{BAGL_LABELINE, 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP,
|
|
BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
COINID_UPCASE,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
#ifdef HAVE_U2F
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 56, 44, 0, 0,
|
|
BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT,
|
|
BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
BAGL_FONT_SYMBOLS_0_SETTINGS,
|
|
0,
|
|
COLOR_APP,
|
|
0xFFFFFF,
|
|
io_seproxyhal_touch_settings,
|
|
NULL,
|
|
NULL},
|
|
#endif
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0,
|
|
BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT,
|
|
BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
BAGL_FONT_SYMBOLS_0_DASHBOARD,
|
|
0,
|
|
COLOR_APP,
|
|
0xFFFFFF,
|
|
io_seproxyhal_touch_exit,
|
|
NULL,
|
|
NULL},
|
|
|
|
// BADGE_<COINID>.GIF
|
|
{{BAGL_ICON, 0x00, 135, 178, 50, 50, 0, 0, BAGL_FILL, 0, COLOR_BG_1, 0, 0},
|
|
&NAME3(C_blue_badge_, COINID, ),
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x00, 0, 270, 320, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_LIGHT_16_22PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Open " COINID_NAME " wallet",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x00, 0, 308, 320, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Connect your Ledger Blue and open your",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x00, 0, 331, 320, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"preferred wallet to view your accounts.",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x00, 0, 450, 320, 14, 0, 0, 0, 0x999999, COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_8_11PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Validation requests will show automatically.",
|
|
10,
|
|
0,
|
|
COLOR_BG_1,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
};
|
|
|
|
unsigned int ui_idle_blue_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
return 0;
|
|
}
|
|
#endif // #if defined(TARGET_BLUE)
|
|
|
|
#if defined(TARGET_NANOS)
|
|
|
|
const ux_menu_entry_t menu_main[];
|
|
const ux_menu_entry_t menu_settings[];
|
|
const ux_menu_entry_t menu_settings_browser[];
|
|
|
|
#ifdef HAVE_U2F
|
|
// change the setting
|
|
void menu_settings_browser_change(unsigned int enabled) {
|
|
fidoTransport = enabled;
|
|
nvm_write(&N_btchip.fidoTransport, (void *)&fidoTransport, sizeof(uint8_t));
|
|
USB_power_U2F(0, 0);
|
|
USB_power_U2F(1, N_btchip.fidoTransport);
|
|
// go back to the menu entry
|
|
UX_MENU_DISPLAY(0, menu_settings, NULL);
|
|
}
|
|
|
|
// show the currently activated entry
|
|
void menu_settings_browser_init(unsigned int ignored) {
|
|
UNUSED(ignored);
|
|
UX_MENU_DISPLAY(N_btchip.fidoTransport ? 1 : 0, menu_settings_browser,
|
|
NULL);
|
|
}
|
|
|
|
const ux_menu_entry_t menu_settings_browser[] = {
|
|
{NULL, menu_settings_browser_change, 0, NULL, "No", NULL, 0, 0},
|
|
{NULL, menu_settings_browser_change, 1, NULL, "Yes", NULL, 0, 0},
|
|
UX_MENU_END};
|
|
|
|
const ux_menu_entry_t menu_settings[] = {
|
|
{NULL, menu_settings_browser_init, 0, NULL, "Browser support", NULL, 0, 0},
|
|
{menu_main, NULL, 1, &C_nanos_icon_back, "Back", NULL, 61, 40},
|
|
UX_MENU_END};
|
|
#endif // HAVE_U2F
|
|
|
|
const ux_menu_entry_t menu_about[] = {
|
|
{NULL, NULL, 0, NULL, "Version", APPVERSION, 0, 0},
|
|
#ifdef HAVE_U2F
|
|
{menu_main, NULL, 2, &C_nanos_icon_back, "Back", NULL, 61, 40},
|
|
#else
|
|
{menu_main, NULL, 1, &C_nanos_icon_back, "Back", NULL, 61, 40},
|
|
#endif // HAVE_U2F
|
|
UX_MENU_END};
|
|
|
|
const ux_menu_entry_t menu_main[] = {
|
|
{NULL, NULL, 0, &NAME3(C_nanos_badge_, COINID, ), "Use wallet to",
|
|
"view accounts", 33, 12},
|
|
#ifdef HAVE_U2F
|
|
{menu_settings, NULL, 0, NULL, "Settings", NULL, 0, 0},
|
|
#endif // HAVE_U2F
|
|
{menu_about, NULL, 0, NULL, "About", NULL, 0, 0},
|
|
{NULL, os_sched_exit, 0, &C_nanos_icon_dashboard, "Quit app", NULL, 50, 29},
|
|
UX_MENU_END};
|
|
|
|
#endif // #if defined(TARGET_NANOS)
|
|
|
|
#if defined(TARGET_BLUE)
|
|
// reuse vars.tmp.addressSummary for each line content
|
|
typedef void (*callback_t)(void);
|
|
callback_t ui_details_back_callback;
|
|
|
|
// don't perform any draw/color change upon finger event over settings
|
|
const bagl_element_t *ui_settings_out_over(const bagl_element_t *e) {
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef HAVE_U2F
|
|
|
|
const bagl_element_t *ui_settings_blue_toggle_browser(const bagl_element_t *e) {
|
|
// swap setting and request redraw of settings elements
|
|
uint8_t setting = N_btchip.fidoTransport ? 0 : 1;
|
|
nvm_write(&N_btchip.fidoTransport, (void *)&setting, sizeof(uint8_t));
|
|
|
|
// only refresh settings mutable drawn elements
|
|
UX_REDISPLAY_IDX(8);
|
|
|
|
// won't redisplay the bagl_none
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
unsigned int ui_settings_back_callback(const bagl_element_t *e) {
|
|
// go back to idle
|
|
ui_idle();
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_U2F
|
|
|
|
const bagl_element_t ui_settings_blue[] = {
|
|
// type userid x y w h str rad
|
|
// fill fg bg fid iid txt touchparams... ]
|
|
{{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
// erase screen (only under the status bar)
|
|
{{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP,
|
|
COLOR_APP, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
/// TOP STATUS BAR
|
|
{{BAGL_LABELINE, 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP,
|
|
BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"SETTINGS",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0,
|
|
BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT,
|
|
BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
BAGL_FONT_SYMBOLS_0_LEFT,
|
|
0,
|
|
COLOR_APP,
|
|
0xFFFFFF,
|
|
ui_settings_back_callback,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0,
|
|
BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT,
|
|
BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
BAGL_FONT_SYMBOLS_0_DASHBOARD,
|
|
0,
|
|
COLOR_APP,
|
|
0xFFFFFF,
|
|
io_seproxyhal_touch_exit,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x00, 30, 105, 160, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
"Browser support",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x00, 30, 126, 260, 30, 0, 0, BAGL_FILL, 0x999999,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0},
|
|
"Enable integrated browser support",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 78, 320, 68, 0, 0, BAGL_FILL,
|
|
0xFFFFFF, 0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0xEEEEEE,
|
|
0x000000,
|
|
ui_settings_blue_toggle_browser,
|
|
ui_settings_out_over,
|
|
ui_settings_out_over},
|
|
|
|
{{BAGL_ICON, 0x01, 258, 98, 32, 18, 0, 0, BAGL_FILL, 0x000000, COLOR_BG_1,
|
|
0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
};
|
|
|
|
const bagl_element_t *ui_settings_blue_prepro(const bagl_element_t *e) {
|
|
// none elements are skipped
|
|
if ((e->component.type & (~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) {
|
|
return 0;
|
|
}
|
|
// swap icon buffer to be displayed depending on if corresponding setting is
|
|
// enabled or not.
|
|
if (e->component.userid) {
|
|
os_memmove(&tmp_element, e, sizeof(bagl_element_t));
|
|
switch (e->component.userid) {
|
|
case 0x01:
|
|
// swap icon content
|
|
if (N_btchip.fidoTransport) {
|
|
tmp_element.text = &C_blue_icon_toggle_set;
|
|
} else {
|
|
tmp_element.text = &C_blue_icon_toggle_reset;
|
|
}
|
|
break;
|
|
}
|
|
return &tmp_element;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
unsigned int ui_settings_blue_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
return 0;
|
|
}
|
|
#endif // #if defined(TARGET_BLUE)
|
|
|
|
#if defined(TARGET_BLUE)
|
|
const char *ui_details_title;
|
|
const char *ui_details_content;
|
|
|
|
#endif
|
|
|
|
const bagl_element_t *
|
|
ui_details_blue_back_callback(const bagl_element_t *element) {
|
|
ui_details_back_callback();
|
|
return 0;
|
|
}
|
|
|
|
const bagl_element_t ui_details_blue[] = {
|
|
// erase screen (only under the status bar)
|
|
{{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP,
|
|
COLOR_APP, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
/// TOP STATUS BAR
|
|
{{BAGL_LABELINE, 0x01, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP,
|
|
BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 19, 50, 44, 0, 0,
|
|
BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT,
|
|
BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
BAGL_FONT_SYMBOLS_0_LEFT,
|
|
0,
|
|
COLOR_APP,
|
|
0xFFFFFF,
|
|
ui_details_blue_back_callback,
|
|
NULL,
|
|
NULL},
|
|
//{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0,
|
|
//BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT,
|
|
//BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
//0 }, BAGL_FONT_SYMBOLS_0_DASHBOARD, 0, COLOR_APP, 0xFFFFFF,
|
|
//io_seproxyhal_touch_exit, NULL, NULL},
|
|
|
|
{{BAGL_LABELINE, 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0},
|
|
"VALUE",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x10, 30, 136, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x11, 30, 159, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x12, 30, 182, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x13, 30, 205, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x14, 30, 228, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x15, 30, 251, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x16, 30, 274, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x17, 30, 297, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x18, 30, 320, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
//"..." at the end if too much
|
|
{{BAGL_LABELINE, 0x19, 30, 343, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x00, 0, 450, 320, 14, 0, 0, 0, 0x999999, COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_8_11PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Review the whole value before continuing.",
|
|
10,
|
|
0,
|
|
COLOR_BG_1,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
};
|
|
|
|
const bagl_element_t *ui_details_blue_prepro(const bagl_element_t *element) {
|
|
if (element->component.userid == 1) {
|
|
os_memmove(&tmp_element, element, sizeof(bagl_element_t));
|
|
tmp_element.text = ui_details_title;
|
|
return &tmp_element;
|
|
} else if (element->component.userid > 0) {
|
|
unsigned int length = strlen(ui_details_content);
|
|
if (length >= (element->component.userid & 0xF) * MAX_CHAR_PER_LINE) {
|
|
os_memset(vars.tmp.addressSummary, 0, MAX_CHAR_PER_LINE + 1);
|
|
os_memmove(
|
|
vars.tmp.addressSummary,
|
|
ui_details_content +
|
|
(element->component.userid & 0xF) * MAX_CHAR_PER_LINE,
|
|
MIN(length -
|
|
(element->component.userid & 0xF) * MAX_CHAR_PER_LINE,
|
|
MAX_CHAR_PER_LINE));
|
|
return 1;
|
|
}
|
|
// nothing to draw for this line
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
unsigned int ui_details_blue_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
return 0;
|
|
}
|
|
|
|
void ui_details_init(const char *title, const char *content,
|
|
callback_t back_callback) {
|
|
ui_details_title = title;
|
|
ui_details_content = content;
|
|
ui_details_back_callback = back_callback;
|
|
UX_DISPLAY(ui_details_blue, ui_details_blue_prepro);
|
|
}
|
|
|
|
// redisplay transaction validation when exiting the details
|
|
void ui_transaction_blue_init(void);
|
|
|
|
bagl_element_callback_t ui_transaction_blue_ok;
|
|
bagl_element_callback_t ui_transaction_blue_cancel;
|
|
|
|
const bagl_element_t *ui_transaction_blue_ok_callback(const bagl_element_t *e) {
|
|
return ui_transaction_blue_ok(e);
|
|
}
|
|
|
|
const bagl_element_t *
|
|
ui_transaction_blue_cancel_callback(const bagl_element_t *e) {
|
|
return ui_transaction_blue_cancel(e);
|
|
}
|
|
|
|
typedef enum {
|
|
TRANSACTION_FULL,
|
|
TRANSACTION_OUTPUT,
|
|
TRANSACTION_FINALIZE,
|
|
TRANSACTION_P2SH,
|
|
TRANSACTION_MESSAGE,
|
|
} ui_transaction_blue_state_t;
|
|
ui_transaction_blue_state_t G_ui_transaction_blue_state;
|
|
// pointer to value to be displayed
|
|
const char *ui_transaction_blue_values[3];
|
|
// variable part of the structure
|
|
const char *const ui_transaction_blue_details_name[][5] = {
|
|
/*TRANSACTION_FULL*/
|
|
{
|
|
"AMOUNT", "ADDRESS", "FEES", "CONFIRM TRANSACTION",
|
|
"Transaction details",
|
|
},
|
|
|
|
/*TRANSACTION_OUTPUT*/
|
|
{
|
|
"OUTPUT#", "ADDRESS", "AMOUNT", "CONFIRM OUTPUT", "Transaction output",
|
|
},
|
|
|
|
/*TRANSACTION_FINALIZE*/
|
|
{"AMOUNT", "FEES", NULL, "CONFIRM TRANSACTION", "Transaction details"},
|
|
|
|
/*TRANSACTION_P2SH*/
|
|
{
|
|
NULL, NULL, NULL, "CONFIRM P2SH", "P2SH Transaction",
|
|
},
|
|
|
|
/*TRANSACTION_MESSAGE*/
|
|
{
|
|
"HASH", NULL, NULL, "SIGN MESSAGE", "Message signature",
|
|
},
|
|
};
|
|
|
|
const bagl_element_t *ui_transaction_blue_1_details(const bagl_element_t *e) {
|
|
if (strlen(ui_transaction_blue_values[0]) *
|
|
BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH >=
|
|
160) {
|
|
// display details screen
|
|
ui_details_init(
|
|
ui_transaction_blue_details_name[G_ui_transaction_blue_state][0],
|
|
ui_transaction_blue_values[0], ui_transaction_blue_init);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const bagl_element_t *ui_transaction_blue_2_details(const bagl_element_t *e) {
|
|
if (strlen(ui_transaction_blue_values[1]) *
|
|
BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH >=
|
|
160) {
|
|
ui_details_init(
|
|
ui_transaction_blue_details_name[G_ui_transaction_blue_state][1],
|
|
ui_transaction_blue_values[1], ui_transaction_blue_init);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const bagl_element_t *ui_transaction_blue_3_details(const bagl_element_t *e) {
|
|
if (strlen(ui_transaction_blue_values[2]) *
|
|
BAGL_FONT_OPEN_SANS_REGULAR_10_13PX_AVG_WIDTH >=
|
|
160) {
|
|
ui_details_init(
|
|
ui_transaction_blue_details_name[G_ui_transaction_blue_state][2],
|
|
ui_transaction_blue_values[2], ui_transaction_blue_init);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const bagl_element_t ui_transaction_blue[] = {
|
|
{{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
// erase screen (only under the status bar)
|
|
{{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP,
|
|
COLOR_APP, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
/// TOP STATUS BAR
|
|
{{BAGL_LABELINE, 0x60, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP,
|
|
BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
// BADGE_TRANSACTION.GIF
|
|
{{BAGL_ICON, 0x40, 30, 98, 50, 50, 0, 0, BAGL_FILL, 0, COLOR_BG_1, 0, 0},
|
|
&C_blue_badge_transaction,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x50, 100, 117, 320, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x00, 100, 138, 320, 30, 0, 0, BAGL_FILL, 0x999999,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_8_11PX, 0},
|
|
"Check and confirm values",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x70, 30, 196, 100, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
// x-18 when ...
|
|
{{BAGL_LABELINE, 0x10, 130, 200, 160, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_LIGHT_16_22PX | BAGL_FONT_ALIGNMENT_RIGHT,
|
|
0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x20, 284, 196, 6, 16, 0, 0, BAGL_FILL, 0x999999,
|
|
COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0},
|
|
BAGL_FONT_SYMBOLS_0_MINIRIGHT,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 168, 320, 48, 0, 9, BAGL_FILL,
|
|
0xFFFFFF, 0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0xEEEEEE,
|
|
0x000000,
|
|
ui_transaction_blue_1_details,
|
|
ui_menu_item_out_over,
|
|
ui_menu_item_out_over},
|
|
{{BAGL_RECTANGLE, 0x20, 0, 168, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
COLOR_BG_1, 0, 0},
|
|
NULL,
|
|
0,
|
|
0x41CCB4,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
// separator when second details is to be displayed
|
|
{{BAGL_RECTANGLE, 0x31, 30, 216, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0,
|
|
0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x71, 30, 245, 100, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
// x-18 when ...
|
|
{{BAGL_LABELINE, 0x11, 130, 245, 160, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x21, 284, 245, 6, 16, 0, 0, BAGL_FILL, 0x999999,
|
|
COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0},
|
|
BAGL_FONT_SYMBOLS_0_MINIRIGHT,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 217, 320, 48, 0, 9, BAGL_FILL,
|
|
0xFFFFFF, 0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0xEEEEEE,
|
|
0x000000,
|
|
ui_transaction_blue_2_details,
|
|
ui_menu_item_out_over,
|
|
ui_menu_item_out_over},
|
|
{{BAGL_RECTANGLE, 0x21, 0, 217, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
COLOR_BG_1, 0, 0},
|
|
NULL,
|
|
0,
|
|
0x41CCB4,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
// separator when third details is to be displayed
|
|
{{BAGL_RECTANGLE, 0x32, 30, 265, 260, 1, 1, 0, 0, 0xEEEEEE, COLOR_BG_1, 0,
|
|
0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x72, 30, 294, 100, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
// x-18 when ...
|
|
{{BAGL_LABELINE, 0x12, 130, 294, 160, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_10_13PX | BAGL_FONT_ALIGNMENT_RIGHT, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x22, 284, 294, 6, 16, 0, 0, BAGL_FILL, 0x999999,
|
|
COLOR_BG_1, BAGL_FONT_SYMBOLS_0 | BAGL_FONT_ALIGNMENT_RIGHT, 0},
|
|
BAGL_FONT_SYMBOLS_0_MINIRIGHT,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_NONE | BAGL_FLAG_TOUCHABLE, 0x00, 0, 266, 320, 48, 0, 9, BAGL_FILL,
|
|
0xFFFFFF, 0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0xEEEEEE,
|
|
0x000000,
|
|
ui_transaction_blue_3_details,
|
|
ui_menu_item_out_over,
|
|
ui_menu_item_out_over},
|
|
{{BAGL_RECTANGLE, 0x22, 0, 266, 5, 48, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
COLOR_BG_1, 0, 0},
|
|
NULL,
|
|
0,
|
|
0x41CCB4,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0, 18,
|
|
BAGL_FILL, 0xCCCCCC, COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
"REJECT",
|
|
0,
|
|
0xB7B7B7,
|
|
COLOR_BG_1,
|
|
ui_transaction_blue_cancel_callback,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0, 18,
|
|
BAGL_FILL, 0x41ccb4, COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
"CONFIRM",
|
|
0,
|
|
0x3ab7a2,
|
|
COLOR_BG_1,
|
|
ui_transaction_blue_ok_callback,
|
|
NULL,
|
|
NULL},
|
|
};
|
|
|
|
const bagl_element_t *
|
|
ui_transaction_blue_prepro(const bagl_element_t *element) {
|
|
if (element->component.userid == 0) {
|
|
return 1;
|
|
}
|
|
// none elements are skipped
|
|
if ((element->component.type & (~BAGL_FLAG_TOUCHABLE)) == BAGL_NONE) {
|
|
return 0;
|
|
} else {
|
|
switch (element->component.userid & 0xF0) {
|
|
// icon
|
|
case 0x40:
|
|
return 1;
|
|
break;
|
|
|
|
// TITLE
|
|
case 0x60:
|
|
os_memmove(&tmp_element, element, sizeof(bagl_element_t));
|
|
tmp_element.text =
|
|
ui_transaction_blue_details_name[G_ui_transaction_blue_state]
|
|
[3];
|
|
return &tmp_element;
|
|
break;
|
|
|
|
// SUBLINE
|
|
case 0x50:
|
|
os_memmove(&tmp_element, element, sizeof(bagl_element_t));
|
|
tmp_element.text =
|
|
ui_transaction_blue_details_name[G_ui_transaction_blue_state]
|
|
[4];
|
|
return &tmp_element;
|
|
break;
|
|
|
|
// details label
|
|
case 0x70:
|
|
if (!ui_transaction_blue_details_name[G_ui_transaction_blue_state]
|
|
[element->component.userid &
|
|
0xF]) {
|
|
return NULL;
|
|
}
|
|
os_memmove(&tmp_element, element, sizeof(bagl_element_t));
|
|
tmp_element.text =
|
|
ui_transaction_blue_details_name[G_ui_transaction_blue_state]
|
|
[element->component.userid &
|
|
0xF];
|
|
return &tmp_element;
|
|
|
|
// detail value
|
|
case 0x10:
|
|
// won't display
|
|
if (!ui_transaction_blue_details_name[G_ui_transaction_blue_state]
|
|
[element->component.userid &
|
|
0xF]) {
|
|
return NULL;
|
|
}
|
|
// always display the value
|
|
os_memmove(&tmp_element, element, sizeof(bagl_element_t));
|
|
tmp_element.text =
|
|
ui_transaction_blue_values[(element->component.userid & 0xF)];
|
|
|
|
// x -= 18 when overflow is detected
|
|
if (strlen(ui_transaction_blue_values[(element->component.userid &
|
|
0xF)]) *
|
|
BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH >=
|
|
160) {
|
|
tmp_element.component.x -= 18;
|
|
}
|
|
return &tmp_element;
|
|
break;
|
|
|
|
// right arrow and left selection rectangle
|
|
case 0x20:
|
|
if (!ui_transaction_blue_details_name[G_ui_transaction_blue_state]
|
|
[element->component.userid &
|
|
0xF]) {
|
|
return NULL;
|
|
}
|
|
if (strlen(ui_transaction_blue_values[(element->component.userid &
|
|
0xF)]) *
|
|
BAGL_FONT_OPEN_SANS_LIGHT_16_22PX_AVG_WIDTH <
|
|
160) {
|
|
return NULL;
|
|
}
|
|
|
|
// horizontal delimiter
|
|
case 0x30:
|
|
return ui_transaction_blue_details_name[G_ui_transaction_blue_state]
|
|
[element->component.userid &
|
|
0xF] != NULL
|
|
? element
|
|
: NULL;
|
|
}
|
|
}
|
|
return element;
|
|
}
|
|
unsigned int ui_transaction_blue_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
return 0;
|
|
}
|
|
|
|
const bagl_element_t ui_display_address_blue[] = {
|
|
{{BAGL_RECTANGLE, 0x00, 0, 68, 320, 413, 0, 0, BAGL_FILL, COLOR_BG_1,
|
|
0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
// erase screen (only under the status bar)
|
|
{{BAGL_RECTANGLE, 0x00, 0, 20, 320, 48, 0, 0, BAGL_FILL, COLOR_APP,
|
|
COLOR_APP, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
/// TOP STATUS BAR
|
|
{{BAGL_LABELINE, 0x00, 0, 45, 320, 30, 0, 0, BAGL_FILL, 0xFFFFFF, COLOR_APP,
|
|
BAGL_FONT_OPEN_SANS_SEMIBOLD_10_13PX | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"CONFIRM ACCOUNT",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
//{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 264, 19, 56, 44, 0, 0,
|
|
//BAGL_FILL, COLOR_APP, COLOR_APP_LIGHT,
|
|
//BAGL_FONT_SYMBOLS_0|BAGL_FONT_ALIGNMENT_CENTER|BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
//0 }, BAGL_FONT_SYMBOLS_0_DASHBOARD, 0, COLOR_APP, 0xFFFFFF,
|
|
//io_seproxyhal_touch_exit, NULL, NULL},
|
|
|
|
{{BAGL_LABELINE, 0x00, 30, 106, 320, 30, 0, 0, BAGL_FILL, 0x999999,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_SEMIBOLD_8_11PX, 0},
|
|
"ACCOUNT",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x10, 30, 126, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x11, 30, 139, 260, 30, 0, 0, BAGL_FILL, 0x000000,
|
|
COLOR_BG_1, BAGL_FONT_OPEN_SANS_REGULAR_10_13PX, 0},
|
|
vars.tmp.addressSummary,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_RECTANGLE, 0x02, 320 / 2 - 0x1D * 8 / 2, 150, 8, 8, 0, 0, BAGL_FILL,
|
|
0xFFFFFF, 0x000000, 0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 40, 414, 115, 36, 0, 18,
|
|
BAGL_FILL, 0xCCCCCC, COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
"REJECT",
|
|
0,
|
|
0xB7B7B7,
|
|
COLOR_BG_1,
|
|
io_seproxyhal_touch_display_cancel,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_RECTANGLE | BAGL_FLAG_TOUCHABLE, 0x00, 165, 414, 115, 36, 0, 18,
|
|
BAGL_FILL, 0x41ccb4, COLOR_BG_1,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11_14PX | BAGL_FONT_ALIGNMENT_CENTER |
|
|
BAGL_FONT_ALIGNMENT_MIDDLE,
|
|
0},
|
|
"CONFIRM",
|
|
0,
|
|
0x3ab7a2,
|
|
COLOR_BG_1,
|
|
io_seproxyhal_touch_display_ok,
|
|
NULL,
|
|
NULL},
|
|
};
|
|
|
|
unsigned int ui_display_address_blue_prepro(const bagl_element_t *element) {
|
|
bagl_icon_details_t *icon_details = &vars.tmpqr.icon_details;
|
|
bagl_element_t *icon_component = element;
|
|
if (element->component.userid > 0) {
|
|
unsigned int length = strlen(G_io_apdu_buffer + 200);
|
|
switch (element->component.userid) {
|
|
// qrcode, need magnifying
|
|
case 0x02: {
|
|
unsigned int x, y, x_off, y_off, bit;
|
|
#define PIXEL_SIZE 5
|
|
os_memmove(&tmp_element, element, sizeof(bagl_element_t));
|
|
tmp_element.component.width = PIXEL_SIZE;
|
|
tmp_element.component.height = PIXEL_SIZE;
|
|
x_off = 320 / 2 - vars.tmpqr.qrcode[0] * PIXEL_SIZE / 2;
|
|
y_off =
|
|
139 + (414 - 139) / 2 - vars.tmpqr.qrcode[0] * PIXEL_SIZE / 2;
|
|
bit = 0;
|
|
|
|
y = 0;
|
|
x = 0;
|
|
tmp_element.component.fgcolor =
|
|
vars.tmpqr.qrcode[1 + (bit >> 3)] & (1 << (bit & 0x7))
|
|
? 0x00000000
|
|
: 0xFFFFFFFF;
|
|
tmp_element.component.x = x_off + x * PIXEL_SIZE;
|
|
tmp_element.component.y = y_off + y * PIXEL_SIZE;
|
|
bit++;
|
|
x = 1;
|
|
goto send_and_next;
|
|
|
|
for (y = 0; y < vars.tmpqr.qrcode[0]; y++) {
|
|
for (x = 0; x < vars.tmpqr.qrcode[0]; x++) {
|
|
send_and_next:
|
|
io_seproxyhal_display(&tmp_element);
|
|
// tmp_element.component.fgcolor =
|
|
// vars.tmpqr.qrcode[1+((y*0x1D+x)>>3)]&(1<<((y*0x1D+x)&0x7))
|
|
// ? 0x00000000: 0xFFFFFFFF;
|
|
tmp_element.component.fgcolor =
|
|
vars.tmpqr.qrcode[1 + (bit >> 3)] & (1 << (bit & 0x7))
|
|
? 0x00000000
|
|
: 0xFFFFFFFF;
|
|
tmp_element.component.x = x_off + x * PIXEL_SIZE;
|
|
tmp_element.component.y = y_off + y * PIXEL_SIZE;
|
|
bit++;
|
|
io_seproxyhal_spi_recv(G_io_seproxyhal_spi_buffer,
|
|
sizeof(G_io_seproxyhal_spi_buffer),
|
|
0);
|
|
}
|
|
}
|
|
// don't use the common draw method, we've already drawn the
|
|
// component
|
|
return 0;
|
|
}
|
|
|
|
// address lines
|
|
case 0x10:
|
|
case 0x11:
|
|
default:
|
|
if (length >=
|
|
(element->component.userid & 0xF) * MAX_CHAR_PER_LINE) {
|
|
os_memset(vars.tmpqr.addressSummary, 0, MAX_CHAR_PER_LINE + 1);
|
|
os_memmove(vars.tmpqr.addressSummary,
|
|
G_io_apdu_buffer + 200 +
|
|
(element->component.userid & 0xF) *
|
|
MAX_CHAR_PER_LINE,
|
|
MIN(length -
|
|
(element->component.userid & 0xF) *
|
|
MAX_CHAR_PER_LINE,
|
|
MAX_CHAR_PER_LINE));
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
// nothing to draw for this line
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
unsigned int ui_display_address_blue_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
return 0;
|
|
}
|
|
#endif // #if defined(TARGET_BLUE)
|
|
|
|
#if defined(TARGET_NANOS)
|
|
|
|
const bagl_element_t ui_display_address_nanos[] = {
|
|
// type userid x y w h str rad
|
|
// fill fg bg fid iid txt touchparams... ]
|
|
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
|
|
0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CROSS},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CHECK},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
//{{BAGL_ICON , 0x01, 21, 9, 14, 14, 0, 0, 0
|
|
//, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0,
|
|
//0, NULL, NULL, NULL },
|
|
{{BAGL_LABELINE, 0x01, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Confirm",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"address",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x02, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Address",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
// Hax, avoid wasting space
|
|
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
G_io_apdu_buffer + 199,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
//{{BAGL_ICON , 0x00, 1, 1, 32, 32, 0, 0, 0
|
|
//, 0xFFFFFF, 0x000000, 0, 0 }, &vars.tmpqr.icon_details, 0, 0, 0, NULL,
|
|
//NULL, NULL },
|
|
};
|
|
|
|
unsigned int ui_display_address_nanos_prepro(const bagl_element_t *element) {
|
|
if (element->component.userid > 0) {
|
|
unsigned int display = (ux_step == element->component.userid - 1);
|
|
if (display) {
|
|
switch (element->component.userid) {
|
|
case 1:
|
|
UX_CALLBACK_SET_INTERVAL(2000);
|
|
break;
|
|
case 2:
|
|
UX_CALLBACK_SET_INTERVAL(MAX(
|
|
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
|
|
break;
|
|
}
|
|
}
|
|
return display;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
unsigned int ui_display_address_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter);
|
|
|
|
const bagl_element_t ui_verify_nanos[] = {
|
|
// type userid x y w h str rad
|
|
// fill fg bg fid iid txt touchparams... ]
|
|
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
|
|
0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CROSS},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CHECK},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
//{{BAGL_ICON , 0x01, 21, 9, 14, 14, 0, 0, 0
|
|
//, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0,
|
|
//0, NULL, NULL, NULL },
|
|
{{BAGL_LABELINE, 0x01, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Confirm",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"transaction",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x02, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Amount",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
vars.tmp.fullAmount,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x03, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Address",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x03, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
vars.tmp.fullAddress,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x04, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Fees",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x04, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
vars.tmp.feesAmount,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
};
|
|
unsigned int ui_verify_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter);
|
|
|
|
const bagl_element_t ui_verify_output_nanos[] = {
|
|
// type userid x y w h str rad
|
|
// fill fg bg fid iid txt touchparams... ]
|
|
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
|
|
0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CROSS},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CHECK},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
//{{BAGL_ICON , 0x01, 21, 9, 14, 14, 0, 0, 0
|
|
//, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0,
|
|
//0, NULL, NULL, NULL },
|
|
{{BAGL_LABELINE, 0x01, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Confirm",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
vars.tmp.feesAmount,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x02, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Amount",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
vars.tmp.fullAmount,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x03, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Address",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x03, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
vars.tmp.fullAddress,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL}};
|
|
unsigned int ui_verify_output_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter);
|
|
|
|
const bagl_element_t ui_finalize_nanos[] = {
|
|
// type userid x y w h str rad
|
|
// fill fg bg fid iid txt touchparams... ]
|
|
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
|
|
0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CROSS},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CHECK},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
//{{BAGL_ICON , 0x01, 21, 9, 14, 14, 0, 0, 0
|
|
//, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0,
|
|
//0, NULL, NULL, NULL },
|
|
{{BAGL_LABELINE, 0x01, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Confirm",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x01, 0, 26, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"transaction",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x02, 0, 12, 128, 12, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Fees",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
vars.tmp.feesAmount,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
/* TODO
|
|
{{BAGL_LABELINE , 0x02, 0, 12, 128, 12, 0, 0, 0
|
|
, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px|BAGL_FONT_ALIGNMENT_CENTER, 0 }, "Amount",
|
|
0, 0, 0, NULL, NULL, NULL },
|
|
{{BAGL_LABELINE , 0x02, 23, 26, 82, 12, 0x80|10,
|
|
0, 0 , 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px|BAGL_FONT_ALIGNMENT_CENTER, 26 },
|
|
vars.tmp.fullAmount, 0, 0, 0, NULL, NULL, NULL },
|
|
*/
|
|
};
|
|
unsigned int ui_finalize_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter);
|
|
|
|
// display or not according to step, and adjust delay
|
|
unsigned int ui_verify_prepro(const bagl_element_t *element) {
|
|
if (element->component.userid > 0) {
|
|
unsigned int display = (ux_step == element->component.userid - 1);
|
|
if (display) {
|
|
switch (element->component.userid) {
|
|
case 1:
|
|
UX_CALLBACK_SET_INTERVAL(2000);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
UX_CALLBACK_SET_INTERVAL(MAX(
|
|
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
|
|
break;
|
|
}
|
|
}
|
|
return display;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
unsigned int ui_verify_output_prepro(const bagl_element_t *element) {
|
|
if (element->component.userid > 0) {
|
|
unsigned int display = (ux_step == element->component.userid - 1);
|
|
if (display) {
|
|
switch (element->component.userid) {
|
|
case 1:
|
|
UX_CALLBACK_SET_INTERVAL(2000);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
UX_CALLBACK_SET_INTERVAL(MAX(
|
|
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
|
|
break;
|
|
}
|
|
}
|
|
return display;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
unsigned int ui_finalize_prepro(const bagl_element_t *element) {
|
|
if (element->component.userid > 0) {
|
|
unsigned int display = (ux_step == element->component.userid - 1);
|
|
if (display) {
|
|
switch (element->component.userid) {
|
|
case 1:
|
|
UX_CALLBACK_SET_INTERVAL(2000);
|
|
break;
|
|
case 2:
|
|
UX_CALLBACK_SET_INTERVAL(MAX(
|
|
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
|
|
break;
|
|
}
|
|
}
|
|
return display;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const bagl_element_t ui_verify_message_signature_nanos[] = {
|
|
// type userid x y w h str rad
|
|
// fill fg bg fid iid txt touchparams... ]
|
|
{{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000, 0xFFFFFF,
|
|
0, 0},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CROSS},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
|
BAGL_GLYPH_ICON_CHECK},
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
//{{BAGL_ICON , 0x01, 28, 9, 14, 14, 0, 0, 0
|
|
//, 0xFFFFFF, 0x000000, 0, BAGL_GLYPH_ICON_TRANSACTION_BADGE }, NULL, 0, 0,
|
|
//0, NULL, NULL, NULL },
|
|
{{BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Sign the",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x01, 0, 26, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"message",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
{{BAGL_LABELINE, 0x02, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
|
"Message hash",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
{{BAGL_LABELINE, 0x02, 23, 26, 82, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
|
|
BAGL_FONT_OPEN_SANS_EXTRABOLD_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
|
|
vars.tmp.fullAddress,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL},
|
|
|
|
};
|
|
unsigned int
|
|
ui_verify_message_signature_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter);
|
|
|
|
unsigned int ui_verify_message_prepro(const bagl_element_t *element) {
|
|
if (element->component.userid > 0) {
|
|
unsigned int display = (ux_step == element->component.userid - 1);
|
|
if (display) {
|
|
switch (element->component.userid) {
|
|
case 1:
|
|
UX_CALLBACK_SET_INTERVAL(2000);
|
|
break;
|
|
case 2:
|
|
UX_CALLBACK_SET_INTERVAL(MAX(
|
|
3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
|
|
break;
|
|
}
|
|
}
|
|
return display;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#endif // #if defined(TARGET_NANOS)
|
|
|
|
void ui_idle(void) {
|
|
ux_step_count = 0;
|
|
|
|
#if defined(TARGET_BLUE)
|
|
UX_DISPLAY(ui_idle_blue, NULL);
|
|
#elif defined(TARGET_NANOS)
|
|
UX_MENU_DISPLAY(0, menu_main, NULL);
|
|
#endif // #if TARGET_ID
|
|
}
|
|
|
|
#ifdef TARGET_BLUE
|
|
unsigned int io_seproxyhal_touch_settings(const bagl_element_t *e) {
|
|
UX_DISPLAY(ui_settings_blue, ui_settings_blue_prepro);
|
|
return 0; // do not redraw button, screen has switched
|
|
}
|
|
|
|
unsigned int io_seproxyhal_touch_exit(const bagl_element_t *e) {
|
|
// go back to the home screen
|
|
os_sched_exit(0);
|
|
return 0; // DO NOT REDRAW THE BUTTON
|
|
}
|
|
#endif // TARGET_BLUE
|
|
|
|
unsigned int io_seproxyhal_touch_verify_cancel(const bagl_element_t *e) {
|
|
// user denied the transaction, tell the USB side
|
|
if (!btchip_bagl_user_action(0)) {
|
|
// redraw ui
|
|
ui_idle();
|
|
}
|
|
return 0; // DO NOT REDRAW THE BUTTON
|
|
}
|
|
|
|
unsigned int io_seproxyhal_touch_verify_ok(const bagl_element_t *e) {
|
|
// user accepted the transaction, tell the USB side
|
|
if (!btchip_bagl_user_action(1)) {
|
|
// redraw ui
|
|
ui_idle();
|
|
}
|
|
return 0; // DO NOT REDRAW THE BUTTON
|
|
}
|
|
|
|
unsigned int
|
|
io_seproxyhal_touch_message_signature_verify_cancel(const bagl_element_t *e) {
|
|
// user denied the transaction, tell the USB side
|
|
btchip_bagl_user_action_message_signing(0);
|
|
// redraw ui
|
|
ui_idle();
|
|
return 0; // DO NOT REDRAW THE BUTTON
|
|
}
|
|
|
|
unsigned int
|
|
io_seproxyhal_touch_message_signature_verify_ok(const bagl_element_t *e) {
|
|
// user accepted the transaction, tell the USB side
|
|
btchip_bagl_user_action_message_signing(1);
|
|
// redraw ui
|
|
ui_idle();
|
|
return 0; // DO NOT REDRAW THE BUTTON
|
|
}
|
|
|
|
unsigned int io_seproxyhal_touch_display_cancel(const bagl_element_t *e) {
|
|
// user denied the transaction, tell the USB side
|
|
btchip_bagl_user_action_display(0);
|
|
// redraw ui
|
|
ui_idle();
|
|
return 0; // DO NOT REDRAW THE BUTTON
|
|
}
|
|
|
|
unsigned int io_seproxyhal_touch_display_ok(const bagl_element_t *e) {
|
|
// user accepted the transaction, tell the USB side
|
|
btchip_bagl_user_action_display(1);
|
|
// redraw ui
|
|
ui_idle();
|
|
return 0; // DO NOT REDRAW THE BUTTON
|
|
}
|
|
|
|
#if defined(TARGET_NANOS)
|
|
unsigned int ui_verify_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
switch (button_mask) {
|
|
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
|
|
io_seproxyhal_touch_verify_cancel(NULL);
|
|
break;
|
|
|
|
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
|
|
io_seproxyhal_touch_verify_ok(NULL);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned int ui_verify_output_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
switch (button_mask) {
|
|
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
|
|
io_seproxyhal_touch_verify_cancel(NULL);
|
|
break;
|
|
|
|
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
|
|
io_seproxyhal_touch_verify_ok(NULL);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned int ui_finalize_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
switch (button_mask) {
|
|
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
|
|
io_seproxyhal_touch_verify_cancel(NULL);
|
|
break;
|
|
|
|
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
|
|
io_seproxyhal_touch_verify_ok(NULL);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned int
|
|
ui_verify_message_signature_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
switch (button_mask) {
|
|
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
|
|
io_seproxyhal_touch_message_signature_verify_cancel(NULL);
|
|
break;
|
|
|
|
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
|
|
io_seproxyhal_touch_message_signature_verify_ok(NULL);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned int ui_display_address_nanos_button(unsigned int button_mask,
|
|
unsigned int button_mask_counter) {
|
|
switch (button_mask) {
|
|
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
|
|
io_seproxyhal_touch_display_cancel(NULL);
|
|
break;
|
|
|
|
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
|
|
io_seproxyhal_touch_display_ok(NULL);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif // #if defined(TARGET_NANOS)
|
|
|
|
#if defined(TARGET_BLUE)
|
|
void ui_transaction_blue_init(void) {
|
|
UX_DISPLAY(ui_transaction_blue, ui_transaction_blue_prepro);
|
|
}
|
|
|
|
void ui_transaction_full_blue_init(void) {
|
|
ui_transaction_blue_ok =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_ok;
|
|
ui_transaction_blue_cancel =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_cancel;
|
|
G_ui_transaction_blue_state = TRANSACTION_FULL;
|
|
ui_transaction_blue_values[0] = vars.tmp.fullAmount;
|
|
ui_transaction_blue_values[1] = vars.tmp.fullAddress;
|
|
ui_transaction_blue_values[2] = vars.tmp.feesAmount;
|
|
ui_transaction_blue_init();
|
|
}
|
|
|
|
void ui_transaction_output_blue_init(void) {
|
|
ui_transaction_blue_ok =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_ok;
|
|
ui_transaction_blue_cancel =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_cancel;
|
|
G_ui_transaction_blue_state = TRANSACTION_OUTPUT;
|
|
snprintf(
|
|
vars.tmp.addressSummary, sizeof(vars.tmp.addressSummary), "%d / %d",
|
|
btchip_context_D.totalOutputs - btchip_context_D.remainingOutputs + 1,
|
|
btchip_context_D.totalOutputs);
|
|
ui_transaction_blue_values[0] = vars.tmp.addressSummary;
|
|
ui_transaction_blue_values[1] = vars.tmp.fullAddress;
|
|
ui_transaction_blue_values[2] = vars.tmp.fullAmount;
|
|
ui_transaction_blue_init();
|
|
}
|
|
|
|
void ui_transaction_finalize_blue_init(void) {
|
|
ui_transaction_blue_ok =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_ok;
|
|
ui_transaction_blue_cancel =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_cancel;
|
|
G_ui_transaction_blue_state = TRANSACTION_FINALIZE;
|
|
ui_transaction_blue_values[0] = vars.tmp.fullAmount;
|
|
ui_transaction_blue_values[1] = vars.tmp.feesAmount;
|
|
ui_transaction_blue_values[2] = NULL;
|
|
ui_transaction_blue_init();
|
|
}
|
|
|
|
void ui_message_signature_blue_init(void) {
|
|
ui_transaction_blue_ok = (bagl_element_callback_t)
|
|
io_seproxyhal_touch_message_signature_verify_ok;
|
|
ui_transaction_blue_cancel = (bagl_element_callback_t)
|
|
io_seproxyhal_touch_message_signature_verify_cancel;
|
|
snprintf(vars.tmp.fullAmount, 65, "%.*H", 32, vars.tmp.fullAmount);
|
|
G_ui_transaction_blue_state = TRANSACTION_MESSAGE;
|
|
ui_transaction_blue_values[0] = vars.tmp.fullAmount;
|
|
ui_transaction_blue_values[1] = NULL;
|
|
ui_transaction_blue_values[2] = NULL;
|
|
ui_transaction_blue_init();
|
|
}
|
|
|
|
void ui_transaction_p2sh_blue_init(void) {
|
|
ui_transaction_blue_ok =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_ok;
|
|
ui_transaction_blue_cancel =
|
|
(bagl_element_callback_t)io_seproxyhal_touch_verify_cancel;
|
|
G_ui_transaction_blue_state = TRANSACTION_P2SH;
|
|
ui_transaction_blue_values[0] = NULL;
|
|
ui_transaction_blue_values[1] = NULL;
|
|
ui_transaction_blue_values[2] = NULL;
|
|
ui_transaction_blue_init();
|
|
}
|
|
#endif // #if defined(TARGET_BLUE)
|
|
|
|
// override point, but nothing more to do
|
|
void io_seproxyhal_display(const bagl_element_t *element) {
|
|
if ((element->component.type & (~BAGL_TYPE_FLAGS_MASK)) != BAGL_NONE) {
|
|
io_seproxyhal_display_default((bagl_element_t *)element);
|
|
}
|
|
}
|
|
|
|
unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) {
|
|
switch (channel & ~(IO_FLAGS)) {
|
|
case CHANNEL_KEYBOARD:
|
|
break;
|
|
|
|
// multiplexed io exchange over a SPI channel and TLV encapsulated protocol
|
|
case CHANNEL_SPI:
|
|
if (tx_len) {
|
|
io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len);
|
|
|
|
if (channel & IO_RESET_AFTER_REPLIED) {
|
|
reset();
|
|
}
|
|
return 0; // nothing received from the master so far (it's a tx
|
|
// transaction)
|
|
} else {
|
|
return io_seproxyhal_spi_recv(G_io_apdu_buffer,
|
|
sizeof(G_io_apdu_buffer), 0);
|
|
}
|
|
|
|
default:
|
|
THROW(INVALID_PARAMETER);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned char io_event(unsigned char channel) {
|
|
// nothing done with the event, throw an error on the transport layer if
|
|
// needed
|
|
|
|
// can't have more than one tag in the reply, not supported yet.
|
|
switch (G_io_seproxyhal_spi_buffer[0]) {
|
|
case SEPROXYHAL_TAG_FINGER_EVENT:
|
|
UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer);
|
|
break;
|
|
|
|
case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT:
|
|
UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer);
|
|
break;
|
|
|
|
case SEPROXYHAL_TAG_STATUS_EVENT:
|
|
if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID &&
|
|
!(U4BE(G_io_seproxyhal_spi_buffer, 3) &
|
|
SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) {
|
|
THROW(EXCEPTION_IO_RESET);
|
|
}
|
|
// no break is intentional
|
|
default:
|
|
UX_DEFAULT_EVENT();
|
|
break;
|
|
|
|
case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
|
|
UX_DISPLAYED_EVENT({});
|
|
break;
|
|
|
|
case SEPROXYHAL_TAG_TICKER_EVENT:
|
|
UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {
|
|
// don't redisplay if UX not allowed (pin locked in the common bolos
|
|
// ux ?)
|
|
if (ux_step_count && UX_ALLOWED) {
|
|
// prepare next screen
|
|
ux_step = (ux_step + 1) % ux_step_count;
|
|
// redisplay screen
|
|
UX_REDISPLAY();
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
|
|
// close the event if not done previously (by a display or whatever)
|
|
if (!io_seproxyhal_spi_is_status_sent()) {
|
|
io_seproxyhal_general_status();
|
|
}
|
|
|
|
// command has been processed, DO NOT reset the current APDU transport
|
|
return 1;
|
|
}
|
|
|
|
uint8_t prepare_fees() {
|
|
if (btchip_context_D.transactionContext.relaxed) {
|
|
os_memmove(vars.tmp.feesAmount, "UNKNOWN", 7);
|
|
vars.tmp.feesAmount[7] = '\0';
|
|
} else {
|
|
unsigned char fees[8];
|
|
unsigned short textSize;
|
|
if (transaction_amount_sub_be(
|
|
fees, btchip_context_D.transactionContext.transactionAmount,
|
|
btchip_context_D.totalOutputAmount)) {
|
|
PRINTF("Error : Fees not consistent");
|
|
goto error;
|
|
}
|
|
os_memmove(vars.tmp.feesAmount, btchip_context_D.shortCoinId,
|
|
btchip_context_D.shortCoinIdLength);
|
|
vars.tmp.feesAmount[btchip_context_D.shortCoinIdLength] = ' ';
|
|
btchip_context_D.tmp =
|
|
(unsigned char *)(vars.tmp.feesAmount +
|
|
btchip_context_D.shortCoinIdLength + 1);
|
|
textSize = btchip_convert_hex_amount_to_displayable(fees);
|
|
vars.tmp.feesAmount[textSize + btchip_context_D.shortCoinIdLength + 1] =
|
|
'\0';
|
|
}
|
|
return 1;
|
|
error:
|
|
return 0;
|
|
}
|
|
|
|
uint8_t prepare_single_output() {
|
|
// TODO : special display for OP_RETURN
|
|
unsigned char amount[8];
|
|
char tmp[80];
|
|
unsigned int offset = 0;
|
|
unsigned char versionSize;
|
|
int addressOffset;
|
|
unsigned char address[22];
|
|
unsigned short version;
|
|
unsigned short textSize;
|
|
unsigned char nativeSegwit;
|
|
|
|
vars.tmp.fullAddress[0] = '\0';
|
|
btchip_swap_bytes(amount, btchip_context_D.currentOutput + offset, 8);
|
|
offset += 8;
|
|
nativeSegwit = btchip_output_script_is_native_witness(
|
|
btchip_context_D.currentOutput + offset);
|
|
if (btchip_output_script_is_op_return(btchip_context_D.currentOutput +
|
|
offset)) {
|
|
strcpy(vars.tmp.fullAddress, "OP_RETURN");
|
|
}
|
|
#ifdef HAVE_QTUM_SUPPORT
|
|
else if (btchip_output_script_is_op_create(btchip_context_D.currentOutput +
|
|
offset)) {
|
|
strcpy(vars.tmp.fullAddress, "OP_CREATE");
|
|
} else if (btchip_output_script_is_op_call(btchip_context_D.currentOutput +
|
|
offset)) {
|
|
strcpy(vars.tmp.fullAddress, "OP_CALL");
|
|
}
|
|
#endif
|
|
else if (nativeSegwit) {
|
|
addressOffset = offset + OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET;
|
|
} else if (btchip_output_script_is_regular(btchip_context_D.currentOutput +
|
|
offset)) {
|
|
addressOffset = offset + 4;
|
|
version = btchip_context_D.payToAddressVersion;
|
|
} else {
|
|
addressOffset = offset + 3;
|
|
version = btchip_context_D.payToScriptHashVersion;
|
|
}
|
|
if (vars.tmp.fullAddress[0] == 0) {
|
|
if (!nativeSegwit) {
|
|
if (version > 255) {
|
|
versionSize = 2;
|
|
address[0] = (version >> 8);
|
|
address[1] = version;
|
|
} else {
|
|
versionSize = 1;
|
|
address[0] = version;
|
|
}
|
|
os_memmove(address + versionSize,
|
|
btchip_context_D.currentOutput + addressOffset, 20);
|
|
|
|
// Prepare address
|
|
textSize = btchip_public_key_to_encoded_base58(
|
|
address, 20 + versionSize, (unsigned char *)tmp, sizeof(tmp),
|
|
version, 1);
|
|
tmp[textSize] = '\0';
|
|
}
|
|
#ifdef NATIVE_SEGWIT_PREFIX
|
|
else {
|
|
textSize = segwit_addr_encode(
|
|
tmp, NATIVE_SEGWIT_PREFIX, 0,
|
|
btchip_context_D.currentOutput + addressOffset,
|
|
btchip_context_D.currentOutput[addressOffset - 1]);
|
|
}
|
|
#endif
|
|
|
|
strcpy(vars.tmp.fullAddress, tmp);
|
|
}
|
|
|
|
// Prepare amount
|
|
|
|
os_memmove(vars.tmp.fullAmount, btchip_context_D.shortCoinId,
|
|
btchip_context_D.shortCoinIdLength);
|
|
vars.tmp.fullAmount[btchip_context_D.shortCoinIdLength] = ' ';
|
|
btchip_context_D.tmp =
|
|
(unsigned char *)(vars.tmp.fullAmount +
|
|
btchip_context_D.shortCoinIdLength + 1);
|
|
textSize = btchip_convert_hex_amount_to_displayable(amount);
|
|
vars.tmp.fullAmount[textSize + btchip_context_D.shortCoinIdLength + 1] =
|
|
'\0';
|
|
|
|
return 1;
|
|
}
|
|
|
|
uint8_t prepare_full_output(uint8_t checkOnly) {
|
|
unsigned int offset = 0;
|
|
int numberOutputs;
|
|
int i;
|
|
unsigned int currentPos = 0;
|
|
unsigned char amount[8], totalOutputAmount[8], fees[8];
|
|
char tmp[80];
|
|
unsigned char outputPos = 0, changeFound = 0;
|
|
if (btchip_context_D.transactionContext.relaxed &&
|
|
!btchip_context_D.transactionContext.consumeP2SH) {
|
|
if (!checkOnly) {
|
|
PRINTF("Error : Mixed inputs");
|
|
}
|
|
goto error;
|
|
}
|
|
if (btchip_context_D.transactionContext.consumeP2SH) {
|
|
if (checkOnly) {
|
|
goto error;
|
|
}
|
|
vars.tmp.fullAmount[0] = '\0';
|
|
vars.tmp.feesAmount[0] = '\0';
|
|
strcpy(vars.tmp.fullAddress, "P2SH");
|
|
return 1;
|
|
}
|
|
// Parse output, locate the change output location
|
|
os_memset(totalOutputAmount, 0, sizeof(totalOutputAmount));
|
|
numberOutputs = btchip_context_D.currentOutput[offset++];
|
|
if (numberOutputs > 3) {
|
|
if (!checkOnly) {
|
|
PRINTF("Error : Too many outputs");
|
|
}
|
|
goto error;
|
|
}
|
|
for (i = 0; i < numberOutputs; i++) {
|
|
unsigned char nullAmount = 1;
|
|
unsigned int j;
|
|
unsigned char isOpReturn, isP2sh, isNativeSegwit;
|
|
#ifdef HAVE_QTUM_SUPPORT
|
|
unsigned char isOpCreate, isOpCall;
|
|
#endif
|
|
for (j = 0; j < 8; j++) {
|
|
if (btchip_context_D.currentOutput[offset + j] != 0) {
|
|
nullAmount = 0;
|
|
break;
|
|
}
|
|
}
|
|
btchip_swap_bytes(amount, btchip_context_D.currentOutput + offset, 8);
|
|
transaction_amount_add_be(totalOutputAmount, totalOutputAmount, amount);
|
|
offset += 8; // skip amount
|
|
isOpReturn = btchip_output_script_is_op_return(
|
|
btchip_context_D.currentOutput + offset);
|
|
isP2sh = btchip_output_script_is_p2sh(btchip_context_D.currentOutput +
|
|
offset);
|
|
isNativeSegwit = btchip_output_script_is_native_witness(
|
|
btchip_context_D.currentOutput + offset);
|
|
#ifdef HAVE_QTUM_SUPPORT
|
|
isOpCreate = btchip_output_script_is_op_create(
|
|
btchip_context_D.currentOutput + offset);
|
|
isOpCall = btchip_output_script_is_op_call(
|
|
btchip_context_D.currentOutput + offset);
|
|
if (!btchip_output_script_is_regular(btchip_context_D.currentOutput +
|
|
offset) &&
|
|
!isP2sh && !(nullAmount && isOpReturn) && !isOpCreate &&
|
|
!isOpCall) {
|
|
#else
|
|
if (!btchip_output_script_is_regular(btchip_context_D.currentOutput +
|
|
offset) &&
|
|
!isP2sh && !(nullAmount && isOpReturn)) {
|
|
#endif
|
|
if (!checkOnly) {
|
|
PRINTF("Error : Unrecognized input script");
|
|
}
|
|
goto error;
|
|
}
|
|
#ifdef HAVE_QTUM_SUPPORT
|
|
if (btchip_context_D.tmpCtx.output.changeInitialized && !isOpReturn &&
|
|
!isOpCreate && !isOpCall) {
|
|
#else
|
|
if (btchip_context_D.tmpCtx.output.changeInitialized && !isOpReturn) {
|
|
#endif
|
|
unsigned char addressOffset =
|
|
(isNativeSegwit ? OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET
|
|
: isP2sh ? OUTPUT_SCRIPT_P2SH_PRE_LENGTH
|
|
: OUTPUT_SCRIPT_REGULAR_PRE_LENGTH);
|
|
if (os_memcmp(btchip_context_D.currentOutput + offset +
|
|
addressOffset,
|
|
btchip_context_D.tmpCtx.output.changeAddress + 1,
|
|
20) == 0) {
|
|
if (changeFound) {
|
|
if (!checkOnly) {
|
|
PRINTF("Error : Multiple change output found");
|
|
}
|
|
goto error;
|
|
}
|
|
changeFound = 1;
|
|
} else {
|
|
outputPos = currentPos;
|
|
}
|
|
}
|
|
offset += 1 + btchip_context_D.currentOutput[offset];
|
|
currentPos++;
|
|
}
|
|
if (btchip_context_D.tmpCtx.output.changeInitialized && !changeFound) {
|
|
if (!checkOnly) {
|
|
PRINTF("Error : change output not found");
|
|
}
|
|
goto error;
|
|
}
|
|
if (transaction_amount_sub_be(
|
|
fees, btchip_context_D.transactionContext.transactionAmount,
|
|
totalOutputAmount)) {
|
|
if (!checkOnly) {
|
|
PRINTF("Error : Fees not consistent");
|
|
}
|
|
goto error;
|
|
}
|
|
if (!checkOnly) {
|
|
// Format validation message
|
|
currentPos = 0;
|
|
offset = 1;
|
|
btchip_context_D.tmp = (unsigned char *)tmp;
|
|
for (i = 0; i < numberOutputs; i++) {
|
|
#ifdef HAVE_QTUM_SUPPORT
|
|
if (!btchip_output_script_is_op_return(
|
|
btchip_context_D.currentOutput + offset + 8) &&
|
|
!btchip_output_script_is_op_create(
|
|
btchip_context_D.currentOutput + offset + 8) &&
|
|
!btchip_output_script_is_op_call(
|
|
btchip_context_D.currentOutput + offset + 8)) {
|
|
#else
|
|
if (!btchip_output_script_is_op_return(
|
|
btchip_context_D.currentOutput + offset + 8)) {
|
|
#endif
|
|
unsigned char versionSize;
|
|
int addressOffset;
|
|
unsigned char address[22];
|
|
unsigned short version;
|
|
unsigned char isNativeSegwit;
|
|
isNativeSegwit = btchip_output_script_is_native_witness(
|
|
btchip_context_D.currentOutput + offset);
|
|
btchip_swap_bytes(amount,
|
|
btchip_context_D.currentOutput + offset, 8);
|
|
offset += 8;
|
|
if (!isNativeSegwit) {
|
|
if (btchip_output_script_is_regular(
|
|
btchip_context_D.currentOutput + offset)) {
|
|
addressOffset = offset + 4;
|
|
version = btchip_context_D.payToAddressVersion;
|
|
} else {
|
|
addressOffset = offset + 3;
|
|
version = btchip_context_D.payToScriptHashVersion;
|
|
}
|
|
if (version > 255) {
|
|
versionSize = 2;
|
|
address[0] = (version >> 8);
|
|
address[1] = version;
|
|
} else {
|
|
versionSize = 1;
|
|
address[0] = version;
|
|
}
|
|
os_memmove(address + versionSize,
|
|
btchip_context_D.currentOutput + addressOffset,
|
|
20);
|
|
}
|
|
if (currentPos == outputPos) {
|
|
unsigned short textSize;
|
|
if (!isNativeSegwit) {
|
|
// Prepare address
|
|
textSize = btchip_public_key_to_encoded_base58(
|
|
address, 20 + versionSize, (unsigned char *)tmp,
|
|
sizeof(tmp), version, 1);
|
|
tmp[textSize] = '\0';
|
|
}
|
|
#ifdef NATIVE_SEGWIT_PREFIX
|
|
else {
|
|
textSize = segwit_addr_encode(
|
|
tmp, NATIVE_SEGWIT_PREFIX, 0,
|
|
btchip_context_D.currentOutput + offset +
|
|
OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET,
|
|
btchip_context_D.currentOutput
|
|
[offset +
|
|
OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET -
|
|
1]);
|
|
}
|
|
#endif
|
|
|
|
strcpy(vars.tmp.fullAddress, tmp);
|
|
|
|
// Prepare amount
|
|
|
|
os_memmove(vars.tmp.fullAmount,
|
|
btchip_context_D.shortCoinId,
|
|
btchip_context_D.shortCoinIdLength);
|
|
vars.tmp.fullAmount[btchip_context_D.shortCoinIdLength] =
|
|
' ';
|
|
btchip_context_D.tmp =
|
|
(unsigned char *)(vars.tmp.fullAmount +
|
|
btchip_context_D.shortCoinIdLength +
|
|
1);
|
|
textSize = btchip_convert_hex_amount_to_displayable(amount);
|
|
vars.tmp
|
|
.fullAmount[textSize +
|
|
btchip_context_D.shortCoinIdLength + 1] =
|
|
'\0';
|
|
|
|
// prepare fee display
|
|
os_memmove(vars.tmp.feesAmount,
|
|
btchip_context_D.shortCoinId,
|
|
btchip_context_D.shortCoinIdLength);
|
|
vars.tmp.feesAmount[btchip_context_D.shortCoinIdLength] =
|
|
' ';
|
|
btchip_context_D.tmp =
|
|
(unsigned char *)(vars.tmp.feesAmount +
|
|
btchip_context_D.shortCoinIdLength +
|
|
1);
|
|
textSize = btchip_convert_hex_amount_to_displayable(fees);
|
|
vars.tmp
|
|
.feesAmount[textSize +
|
|
btchip_context_D.shortCoinIdLength + 1] =
|
|
'\0';
|
|
break;
|
|
}
|
|
} else {
|
|
offset += 8;
|
|
}
|
|
offset += 1 + btchip_context_D.currentOutput[offset];
|
|
currentPos++;
|
|
}
|
|
}
|
|
return 1;
|
|
error:
|
|
return 0;
|
|
}
|
|
|
|
#define HASH_LENGTH 4
|
|
uint8_t prepare_message_signature() {
|
|
cx_hash(&btchip_context_D.transactionHashAuthorization.header, CX_LAST,
|
|
vars.tmp.fullAmount, 0, vars.tmp.fullAmount);
|
|
snprintf(vars.tmp.fullAddress, sizeof(vars.tmp.fullAddress), "%.*H...%.*H",
|
|
8, vars.tmp.fullAmount, 8, vars.tmp.fullAmount + 32 - 8);
|
|
return 1;
|
|
}
|
|
|
|
unsigned int btchip_bagl_confirm_full_output() {
|
|
if (!prepare_full_output(0)) {
|
|
return 0;
|
|
}
|
|
|
|
#if defined(TARGET_BLUE)
|
|
ui_transaction_full_blue_init();
|
|
#elif defined(TARGET_NANOS)
|
|
ux_step = 0;
|
|
ux_step_count = 4;
|
|
UX_DISPLAY(ui_verify_nanos, ui_verify_prepro);
|
|
#endif // #if TARGET_ID
|
|
return 1;
|
|
}
|
|
|
|
unsigned int btchip_bagl_confirm_single_output() {
|
|
// TODO : remove when supporting multi output
|
|
#if defined(TARGET_BLUE)
|
|
if (btchip_context_D.transactionContext.consumeP2SH) {
|
|
ui_transaction_p2sh_blue_init();
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
if (!prepare_single_output()) {
|
|
return 0;
|
|
}
|
|
|
|
snprintf(vars.tmp.feesAmount, sizeof(vars.tmp.feesAmount), "output #%d",
|
|
btchip_context_D.totalOutputs - btchip_context_D.remainingOutputs +
|
|
1);
|
|
|
|
#if defined(TARGET_BLUE)
|
|
ui_transaction_output_blue_init();
|
|
#elif defined(TARGET_NANOS)
|
|
ux_step = 0;
|
|
ux_step_count = 3;
|
|
UX_DISPLAY(ui_verify_output_nanos, ui_verify_output_prepro);
|
|
#endif // #if TARGET_ID
|
|
return 1;
|
|
}
|
|
|
|
unsigned int btchip_bagl_finalize_tx() {
|
|
if (!prepare_fees()) {
|
|
return 0;
|
|
}
|
|
|
|
#if defined(TARGET_BLUE)
|
|
ui_transaction_finalize_blue_init();
|
|
#elif defined(TARGET_NANOS)
|
|
ux_step = 0;
|
|
ux_step_count = 2;
|
|
UX_DISPLAY(ui_finalize_nanos, ui_finalize_prepro);
|
|
#endif // #if TARGET_ID
|
|
return 1;
|
|
}
|
|
|
|
void btchip_bagl_confirm_message_signature() {
|
|
if (!prepare_message_signature()) {
|
|
return;
|
|
}
|
|
|
|
#if defined(TARGET_BLUE)
|
|
ui_message_signature_blue_init();
|
|
#elif defined(TARGET_NANOS)
|
|
ux_step = 0;
|
|
ux_step_count = 2;
|
|
UX_DISPLAY(ui_verify_message_signature_nanos, ui_verify_message_prepro);
|
|
#endif // #if TARGET_ID
|
|
}
|
|
|
|
unsigned int btchip_bagl_display_public_key() {
|
|
// setup qrcode of the address in the apdu buffer
|
|
strcat(G_io_apdu_buffer + 200, " ");
|
|
|
|
#if defined(TARGET_BLUE)
|
|
// must assert spi buffer is longer than the requested qrcode len.
|
|
// sizeof(data and temp buffer) >=
|
|
// qrcodegen_BUFFER_LEN_FOR_VERSION(guessed_qrcode_version)
|
|
|
|
// encode the address as a QRcode
|
|
os_memset(&vars.tmpqr, 0, sizeof(vars.tmpqr));
|
|
// use G_io_seproxyhal_spi_buffer as
|
|
if (qrcodegen_encodeBinary(
|
|
G_io_apdu_buffer + 200, strlen(G_io_apdu_buffer + 200),
|
|
G_io_seproxyhal_spi_buffer, sizeof(G_io_seproxyhal_spi_buffer),
|
|
// the edge qrcode size will be discarded when drawing
|
|
&vars.tmpqr.qrcode, sizeof(vars.tmpqr.qrcode), qrcodegen_Ecc_LOW,
|
|
qrcodegen_VERSION_MIN,
|
|
3, // buffer is not designed to handle more than version 3
|
|
qrcodegen_Mask_AUTO, 0)) {
|
|
vars.tmpqr.icon_details.width = vars.tmpqr.qrcode[0];
|
|
vars.tmpqr.icon_details.height = vars.tmpqr.qrcode[0];
|
|
vars.tmpqr.icon_details.bpp = 1;
|
|
#if defined(TARGET_BLUE)
|
|
// mgnify on the fly without consuming RAM
|
|
vars.tmpqr.colors[0] = -1;
|
|
#else
|
|
vars.tmpqr.colors[1] = -1;
|
|
#endif
|
|
vars.tmpqr.icon_details.colors = &vars.tmpqr.colors[0];
|
|
vars.tmpqr.icon_details.bitmap = &vars.tmpqr.qrcode[1];
|
|
// os_memmove(&vars.tmpqr.icon, &C_qrcode_icon_initializer,
|
|
// sizeof(C_qrcode_icon_initializer));
|
|
}
|
|
|
|
UX_DISPLAY(ui_display_address_blue, ui_display_address_blue_prepro);
|
|
#elif defined(TARGET_NANOS)
|
|
// append and prepend a white space to the address
|
|
G_io_apdu_buffer[199] = ' ';
|
|
ux_step = 0;
|
|
ux_step_count = 2;
|
|
UX_DISPLAY(ui_display_address_nanos, ui_display_address_nanos_prepro);
|
|
#endif // #if TARGET_ID
|
|
return 1;
|
|
}
|
|
|
|
void app_exit(void) {
|
|
BEGIN_TRY_L(exit) {
|
|
TRY_L(exit) {
|
|
os_sched_exit(-1);
|
|
}
|
|
FINALLY_L(exit) {
|
|
}
|
|
}
|
|
END_TRY_L(exit);
|
|
}
|
|
|
|
__attribute__((section(".boot"))) int main(void) {
|
|
// exit critical section
|
|
__asm volatile("cpsie i");
|
|
|
|
// ensure exception will work as planned
|
|
os_boot();
|
|
|
|
for (;;) {
|
|
UX_INIT();
|
|
BEGIN_TRY {
|
|
TRY {
|
|
io_seproxyhal_init();
|
|
|
|
btchip_context_init();
|
|
|
|
// deactivate usb before activating
|
|
USB_power_U2F(0, 0);
|
|
|
|
#ifdef HAVE_U2F
|
|
os_memset((unsigned char *)&u2fService, 0, sizeof(u2fService));
|
|
u2fService.inputBuffer = G_io_apdu_buffer;
|
|
u2fService.outputBuffer = G_io_apdu_buffer;
|
|
u2fService.messageBuffer = (uint8_t *)u2fMessageBuffer;
|
|
u2fService.messageBufferSize = U2F_MAX_MESSAGE_SIZE;
|
|
u2f_initialize_service((u2f_service_t *)&u2fService);
|
|
|
|
USB_power_U2F(1, N_btchip.fidoTransport);
|
|
#else
|
|
USB_power_U2F(1, 0);
|
|
#endif
|
|
|
|
#ifdef HAVE_BLE
|
|
BLE_power(0, NULL);
|
|
BLE_power(1, "Ledger Wallet");
|
|
#endif // HAVE_BLE
|
|
|
|
#if defined(TARGET_BLUE)
|
|
// setup the status bar colors (remembered after wards, even
|
|
// more if another app does not resetup after app switch)
|
|
UX_SET_STATUS_BAR_COLOR(0xFFFFFF, COLOR_APP);
|
|
#endif // TARGET_ID
|
|
|
|
ui_idle();
|
|
|
|
app_main();
|
|
}
|
|
CATCH(EXCEPTION_IO_RESET) {
|
|
// reset IO and UX
|
|
continue;
|
|
}
|
|
CATCH_ALL {
|
|
break;
|
|
}
|
|
FINALLY {
|
|
}
|
|
}
|
|
END_TRY;
|
|
}
|
|
app_exit();
|
|
|
|
return 0;
|
|
}
|