mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-04 13:38:28 +00:00
Compare commits
15 Commits
894623a039
...
6c84fdf56f
Author | SHA1 | Date | |
---|---|---|---|
|
6c84fdf56f | ||
|
ef02c4de5d | ||
|
835f7087c6 | ||
|
331e07b1e0 | ||
|
5c2bacd39a | ||
|
acee4b605e | ||
|
0dcc125c4d | ||
|
61448aad34 | ||
|
b1850b904a | ||
|
f9c897d2df | ||
|
e7c5bc2632 | ||
|
29cf6d3557 | ||
|
ff7f6ea446 | ||
|
2b559fc22f | ||
|
c08e495c9b |
@ -31,6 +31,7 @@ PYOPT ?= 1
|
||||
BITCOIN_ONLY ?= 0
|
||||
BOOTLOADER_QA ?= 0
|
||||
BOOTLOADER_DEVEL ?= 0
|
||||
DISABLE_OPTIGA ?= 0
|
||||
TREZOR_MODEL ?= T
|
||||
TREZOR_MEMPERF ?= 0
|
||||
ADDRESS_SANITIZER ?= 0
|
||||
@ -41,6 +42,8 @@ THP ?= 0
|
||||
BENCHMARK ?= 0
|
||||
TREZOR_EMULATOR_DEBUGGABLE ?= 0
|
||||
QUIET_MODE ?= 0
|
||||
TREZOR_DISABLE_ANIMATION ?= $(if $(filter 0,$(PYOPT)),1,0)
|
||||
STORAGE_INSECURE_TESTING_MODE ?= 0
|
||||
|
||||
# OpenOCD interface default. Alternative: ftdi/olimex-arm-usb-tiny-h
|
||||
OPENOCD_INTERFACE ?= stlink
|
||||
@ -142,7 +145,9 @@ SCONS_VARS = \
|
||||
PRODUCTION="$(PRODUCTION)" \
|
||||
PYOPT="$(PYOPT)" \
|
||||
QUIET_MODE="$(QUIET_MODE)" \
|
||||
STORAGE_INSECURE_TESTING_MODE="$(STORAGE_INSECURE_TESTING_MODE)" \
|
||||
THP="$(THP)" \
|
||||
TREZOR_DISABLE_ANIMATION="$(TREZOR_DISABLE_ANIMATION)" \
|
||||
TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" \
|
||||
TREZOR_EMULATOR_DEBUGGABLE=$(TREZOR_EMULATOR_DEBUGGABLE) \
|
||||
TREZOR_MEMPERF="$(TREZOR_MEMPERF)" \
|
||||
|
@ -18,6 +18,14 @@ HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
|
||||
THP = ARGUMENTS.get('THP', '0') == '1' # Trezor-Host Protocol
|
||||
MODEL_IDENTIFIER = models.get_model_identifier(TREZOR_MODEL)
|
||||
BENCHMARK = ARGUMENTS.get('BENCHMARK', '0') == '1'
|
||||
DISABLE_ANIMATION = ARGUMENTS.get('TREZOR_DISABLE_ANIMATION', '0') == '1'
|
||||
|
||||
STORAGE_INSECURE_TESTING_MODE = ARGUMENTS.get('STORAGE_INSECURE_TESTING_MODE', '0') == '1'
|
||||
if STORAGE_INSECURE_TESTING_MODE and PRODUCTION:
|
||||
raise RuntimeError("STORAGE_INSECURE_TESTING_MODE cannot be used in production")
|
||||
if STORAGE_INSECURE_TESTING_MODE:
|
||||
DISABLE_OPTIGA = True
|
||||
PYOPT = "0"
|
||||
|
||||
if BENCHMARK and PYOPT != '0':
|
||||
print("BENCHMARK=1 works only with PYOPT=0.")
|
||||
@ -30,7 +38,9 @@ FEATURE_FLAGS = {
|
||||
}
|
||||
|
||||
FEATURES_WANTED = ["input", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic"]
|
||||
if DISABLE_OPTIGA and PYOPT == '0':
|
||||
if DISABLE_OPTIGA:
|
||||
if PYOPT != '0':
|
||||
raise RuntimeError("DISABLE_OPTIGA requires PYOPT=0")
|
||||
FEATURES_WANTED.remove("optiga")
|
||||
|
||||
CCFLAGS_MOD = ''
|
||||
@ -69,6 +79,7 @@ CPPDEFINES_MOD += [
|
||||
('USE_CARDANO', '1' if EVERYTHING else '0'),
|
||||
('USE_NEM', '1' if (EVERYTHING and TREZOR_MODEL == "T") else '0'),
|
||||
('USE_EOS', '1' if (EVERYTHING and TREZOR_MODEL == "T") else '0'),
|
||||
('DISABLE_ANIMATION', '1' if DISABLE_ANIMATION else '0'),
|
||||
]
|
||||
SOURCE_MOD += [
|
||||
'embed/upymod/trezorobj.c',
|
||||
@ -367,6 +378,9 @@ if THP:
|
||||
'vendor/trezor-crypto/elligator2.c',
|
||||
]
|
||||
|
||||
if STORAGE_INSECURE_TESTING_MODE:
|
||||
CPPDEFINES_MOD += ['STORAGE_INSECURE_TESTING_MODE']
|
||||
|
||||
ui.init_ui(TREZOR_MODEL, "firmware", CPPDEFINES_MOD, SOURCE_MOD, RUST_UI_FEATURES)
|
||||
|
||||
SOURCE_QSTR = SOURCE_MOD + SOURCE_MICROPYTHON + SOURCE_MICROPYTHON_SPEED
|
||||
@ -873,6 +887,14 @@ elif 'STM32U5G9xx' in CPPDEFINES_HAL or 'STM32U585xx' in CPPDEFINES_HAL:
|
||||
else:
|
||||
raise Exception("Unknown MCU")
|
||||
|
||||
if STORAGE_INSECURE_TESTING_MODE:
|
||||
INSECURE_TESTING_MODE_STR = """
|
||||
#########################################################
|
||||
# STORAGE_INSECURE_TESTING_MODE enabled, DO NOT USE #
|
||||
#########################################################
|
||||
"""
|
||||
action_bin.append(INSECURE_TESTING_MODE_STR)
|
||||
|
||||
program_bin = env.Command(
|
||||
target='firmware.bin',
|
||||
source=program_elf,
|
||||
|
@ -16,6 +16,13 @@ DISABLE_OPTIGA = ARGUMENTS.get('DISABLE_OPTIGA', '0') == '1'
|
||||
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
|
||||
THP = ARGUMENTS.get('THP', '0') == '1' # Trezor-Host Protocol
|
||||
|
||||
STORAGE_INSECURE_TESTING_MODE = ARGUMENTS.get('STORAGE_INSECURE_TESTING_MODE', '0') == '1'
|
||||
if STORAGE_INSECURE_TESTING_MODE and PRODUCTION:
|
||||
raise RuntimeError("STORAGE_INSECURE_TESTING_MODE cannot be used in production")
|
||||
if STORAGE_INSECURE_TESTING_MODE:
|
||||
DISABLE_OPTIGA = True
|
||||
PYOPT = "0"
|
||||
|
||||
FEATURE_FLAGS = {
|
||||
"RDI": True,
|
||||
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
|
||||
@ -24,7 +31,10 @@ FEATURE_FLAGS = {
|
||||
}
|
||||
|
||||
FEATURES_WANTED = ["input", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic"]
|
||||
if DISABLE_OPTIGA and PYOPT == '0':
|
||||
if DISABLE_OPTIGA:
|
||||
# TODO use PYOPT instead of PRODUCTION, same as in firmware, blocked on #4253
|
||||
if PRODUCTION:
|
||||
raise RuntimeError("DISABLE_OPTIGA requires non-production build")
|
||||
FEATURES_WANTED.remove("optiga")
|
||||
|
||||
CCFLAGS_MOD = ''
|
||||
@ -235,6 +245,8 @@ if THP:
|
||||
'vendor/trezor-crypto/elligator2.c',
|
||||
]
|
||||
|
||||
if STORAGE_INSECURE_TESTING_MODE:
|
||||
CPPDEFINES_MOD += ['STORAGE_INSECURE_TESTING_MODE']
|
||||
|
||||
env = Environment(
|
||||
ENV=os.environ,
|
||||
@ -411,6 +423,14 @@ action_bin=[
|
||||
'$CP $TARGET ' + BINARY_NAME,
|
||||
]
|
||||
|
||||
if STORAGE_INSECURE_TESTING_MODE:
|
||||
INSECURE_TESTING_MODE_STR = """
|
||||
#########################################################
|
||||
# STORAGE_INSECURE_TESTING_MODE enabled, DO NOT USE #
|
||||
#########################################################
|
||||
"""
|
||||
action_bin.append(INSECURE_TESTING_MODE_STR)
|
||||
|
||||
program_bin = env.Command(
|
||||
target='kernel.bin',
|
||||
source=program_elf,
|
||||
|
@ -38,8 +38,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR___name__;
|
||||
MP_QSTR_account;
|
||||
MP_QSTR_account_items;
|
||||
MP_QSTR_account_items_title;
|
||||
MP_QSTR_account_label;
|
||||
MP_QSTR_account_path;
|
||||
MP_QSTR_accounts;
|
||||
MP_QSTR_action;
|
||||
@ -69,12 +67,11 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_address_qr;
|
||||
MP_QSTR_address_title;
|
||||
MP_QSTR_allow_cancel;
|
||||
MP_QSTR_altcoin_tx_summary;
|
||||
MP_QSTR_amount;
|
||||
MP_QSTR_amount_change;
|
||||
MP_QSTR_amount_label;
|
||||
MP_QSTR_amount_new;
|
||||
MP_QSTR_amount_title;
|
||||
MP_QSTR_amount_value;
|
||||
MP_QSTR_app_name;
|
||||
MP_QSTR_area_bytesize;
|
||||
MP_QSTR_attach_timer_fn;
|
||||
@ -175,8 +172,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_buttons__view_all_data;
|
||||
MP_QSTR_can_go_back;
|
||||
MP_QSTR_cancel;
|
||||
MP_QSTR_cancel_arrow;
|
||||
MP_QSTR_cancel_cross;
|
||||
MP_QSTR_cancel_text;
|
||||
MP_QSTR_case_sensitive;
|
||||
MP_QSTR_check_homescreen_format;
|
||||
@ -209,7 +204,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_confirm_properties;
|
||||
MP_QSTR_confirm_recovery;
|
||||
MP_QSTR_confirm_reset_device;
|
||||
MP_QSTR_confirm_total;
|
||||
MP_QSTR_confirm_summary;
|
||||
MP_QSTR_confirm_total__fee_rate;
|
||||
MP_QSTR_confirm_total__fee_rate_colon;
|
||||
MP_QSTR_confirm_total__sending_from_account;
|
||||
@ -242,12 +237,11 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_experimental_mode__only_for_dev;
|
||||
MP_QSTR_experimental_mode__title;
|
||||
MP_QSTR_extra;
|
||||
MP_QSTR_fee_amount;
|
||||
MP_QSTR_extra_items;
|
||||
MP_QSTR_fee;
|
||||
MP_QSTR_fee_items;
|
||||
MP_QSTR_fee_label;
|
||||
MP_QSTR_fee_rate_amount;
|
||||
MP_QSTR_fee_title;
|
||||
MP_QSTR_fee_value;
|
||||
MP_QSTR_fingerprint;
|
||||
MP_QSTR_firmware_update__title;
|
||||
MP_QSTR_firmware_update__title_fingerprint;
|
||||
@ -255,7 +249,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_flow_confirm_output;
|
||||
MP_QSTR_flow_confirm_reset;
|
||||
MP_QSTR_flow_confirm_set_new_pin;
|
||||
MP_QSTR_flow_confirm_summary;
|
||||
MP_QSTR_flow_continue_recovery;
|
||||
MP_QSTR_flow_get_address;
|
||||
MP_QSTR_flow_prompt_backup;
|
||||
@ -317,7 +310,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_instructions__view_all_data;
|
||||
MP_QSTR_is_type_of;
|
||||
MP_QSTR_items;
|
||||
MP_QSTR_items_title;
|
||||
MP_QSTR_joint__title;
|
||||
MP_QSTR_joint__to_the_total_amount;
|
||||
MP_QSTR_joint__you_are_contributing;
|
||||
@ -697,7 +689,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_title;
|
||||
MP_QSTR_total_amount;
|
||||
MP_QSTR_total_fee_new;
|
||||
MP_QSTR_total_label;
|
||||
MP_QSTR_total_len;
|
||||
MP_QSTR_touch_event;
|
||||
MP_QSTR_trace;
|
||||
|
@ -85,7 +85,7 @@ pub use swipe_up_screen::{SwipeUpScreen, SwipeUpScreenMsg};
|
||||
#[cfg(feature = "translations")]
|
||||
pub use tap_to_confirm::TapToConfirm;
|
||||
pub use updatable_more_info::UpdatableMoreInfo;
|
||||
pub use vertical_menu::{PagedVerticalMenu, VerticalMenu, VerticalMenuChoiceMsg};
|
||||
pub use vertical_menu::{PagedVerticalMenu, VerticalMenu, VerticalMenuChoiceMsg, MENU_MAX_ITEMS};
|
||||
pub use welcome_screen::WelcomeScreen;
|
||||
|
||||
use super::{constant, theme};
|
||||
|
@ -25,7 +25,7 @@ pub enum VerticalMenuChoiceMsg {
|
||||
|
||||
/// Number of buttons.
|
||||
/// Presently, VerticalMenu holds only fixed number of buttons.
|
||||
const MAX_ITEMS: usize = 3;
|
||||
pub const MENU_MAX_ITEMS: usize = 3;
|
||||
|
||||
/// Fixed height of each menu button.
|
||||
const MENU_BUTTON_HEIGHT: i16 = 64;
|
||||
@ -33,7 +33,7 @@ const MENU_BUTTON_HEIGHT: i16 = 64;
|
||||
/// Fixed height of a separator.
|
||||
const MENU_SEP_HEIGHT: i16 = 2;
|
||||
|
||||
type VerticalMenuButtons = Vec<Button, MAX_ITEMS>;
|
||||
type VerticalMenuButtons = Vec<Button, MENU_MAX_ITEMS>;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct AttachAnimation {
|
||||
@ -180,7 +180,7 @@ impl VerticalMenu {
|
||||
fn new(buttons: VerticalMenuButtons) -> Self {
|
||||
Self {
|
||||
buttons,
|
||||
n_items: MAX_ITEMS,
|
||||
n_items: MENU_MAX_ITEMS,
|
||||
attach_animation: AttachAnimation::default(),
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
use heapless::Vec;
|
||||
|
||||
use crate::{
|
||||
error,
|
||||
error::{self},
|
||||
maybe_trace::MaybeTrace,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
button_request::ButtonRequest,
|
||||
component::{swipe_detect::SwipeSettings, ButtonRequestExt, ComponentExt},
|
||||
component::{swipe_detect::SwipeSettings, Component, ComponentExt},
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
FlowController, FlowMsg, SwipeFlow,
|
||||
FlowController, FlowMsg, Swipable, SwipeFlow,
|
||||
},
|
||||
geometry::Direction,
|
||||
},
|
||||
@ -19,7 +19,7 @@ use super::{
|
||||
super::{
|
||||
component::{
|
||||
Frame, FrameMsg, PromptMsg, PromptScreen, SwipeContent, VerticalMenu,
|
||||
VerticalMenuChoiceMsg,
|
||||
VerticalMenuChoiceMsg, MENU_MAX_ITEMS,
|
||||
},
|
||||
theme,
|
||||
},
|
||||
@ -29,6 +29,7 @@ use super::{
|
||||
const MENU_ITEM_CANCEL: usize = 0;
|
||||
const MENU_ITEM_FEE_INFO: usize = 1;
|
||||
const MENU_ITEM_ACCOUNT_INFO: usize = 2;
|
||||
const MENU_ITEM_EXTRA_INFO: usize = 3;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ConfirmSummary {
|
||||
@ -37,6 +38,7 @@ pub enum ConfirmSummary {
|
||||
Menu,
|
||||
FeeInfo,
|
||||
AccountInfo,
|
||||
ExtraInfo,
|
||||
CancelTap,
|
||||
}
|
||||
|
||||
@ -52,9 +54,10 @@ impl FlowController for ConfirmSummary {
|
||||
(Self::Summary, Direction::Up) => Self::Hold.swipe(direction),
|
||||
(Self::Hold, Direction::Down) => Self::Summary.swipe(direction),
|
||||
(Self::Menu, Direction::Right) => Self::Summary.swipe(direction),
|
||||
(Self::AccountInfo | Self::FeeInfo | Self::CancelTap, Direction::Right) => {
|
||||
Self::Menu.swipe(direction)
|
||||
}
|
||||
(
|
||||
Self::AccountInfo | Self::FeeInfo | Self::ExtraInfo | Self::CancelTap,
|
||||
Direction::Right,
|
||||
) => Self::Menu.swipe(direction),
|
||||
_ => self.do_nothing(),
|
||||
}
|
||||
}
|
||||
@ -66,6 +69,7 @@ impl FlowController for ConfirmSummary {
|
||||
(Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::CancelTap.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Choice(MENU_ITEM_FEE_INFO)) => Self::FeeInfo.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => Self::AccountInfo.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Choice(MENU_ITEM_EXTRA_INFO)) => Self::ExtraInfo.swipe_left(),
|
||||
(Self::Menu, FlowMsg::Cancelled) => Self::Summary.swipe_right(),
|
||||
(Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
|
||||
(_, FlowMsg::Cancelled) => Self::Menu.goto(),
|
||||
@ -74,18 +78,20 @@ impl FlowController for ConfirmSummary {
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy_page() -> impl Component<Msg = FlowMsg> + Swipable + MaybeTrace {
|
||||
Frame::left_aligned(TString::empty(), VerticalMenu::empty()).map(|_| Some(FlowMsg::Cancelled))
|
||||
}
|
||||
|
||||
pub fn new_confirm_summary(
|
||||
summary_params: ShowInfoParams,
|
||||
account_params: ShowInfoParams,
|
||||
fee_params: ShowInfoParams,
|
||||
br_name: TString<'static>,
|
||||
br_code: u16,
|
||||
cancel_text: Option<TString<'static>>,
|
||||
fee_params: Option<ShowInfoParams>,
|
||||
account_params: Option<ShowInfoParams>,
|
||||
extra_params: Option<ShowInfoParams>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
) -> Result<SwipeFlow, error::Error> {
|
||||
// Summary
|
||||
let content_summary = summary_params
|
||||
.into_layout()?
|
||||
.one_button_request(ButtonRequest::from_num(br_code, br_name))
|
||||
// Summary(1) + Hold(1)
|
||||
.with_pages(|summary_pages| summary_pages + 1);
|
||||
|
||||
@ -105,33 +111,44 @@ pub fn new_confirm_summary(
|
||||
});
|
||||
|
||||
// FeeInfo
|
||||
let has_fee_info = !fee_params.is_empty();
|
||||
let content_fee = fee_params.into_layout()?;
|
||||
|
||||
let content_fee = fee_params.map(|params| params.into_layout()).transpose()?;
|
||||
// AccountInfo
|
||||
let has_account_info = !account_params.is_empty();
|
||||
let content_account = account_params.into_layout()?;
|
||||
let content_account = account_params
|
||||
.map(|params| params.into_layout())
|
||||
.transpose()?;
|
||||
// ExtraInfo
|
||||
let content_extra = extra_params
|
||||
.map(|params| params.into_layout())
|
||||
.transpose()?;
|
||||
|
||||
// Menu
|
||||
// Menu with provided info and cancel
|
||||
let mut menu = VerticalMenu::empty();
|
||||
let mut menu_items = Vec::<usize, 3>::new();
|
||||
if has_fee_info {
|
||||
if content_fee.is_some() {
|
||||
menu = menu.item(
|
||||
theme::ICON_CHEVRON_RIGHT,
|
||||
TR::confirm_total__title_fee.into(),
|
||||
);
|
||||
unwrap!(menu_items.push(MENU_ITEM_FEE_INFO));
|
||||
}
|
||||
if has_account_info {
|
||||
if content_account.is_some() {
|
||||
menu = menu.item(
|
||||
theme::ICON_CHEVRON_RIGHT,
|
||||
TR::address_details__account_info.into(),
|
||||
);
|
||||
unwrap!(menu_items.push(MENU_ITEM_ACCOUNT_INFO));
|
||||
}
|
||||
if content_extra.is_some() && menu_items.len() < MENU_MAX_ITEMS - 1 {
|
||||
// NOTE: extra is shown only if VerticalMenu has space considering mandatory "Cancel"
|
||||
menu = menu.item(
|
||||
theme::ICON_CHEVRON_RIGHT,
|
||||
TR::words__title_information.into(),
|
||||
);
|
||||
unwrap!(menu_items.push(MENU_ITEM_EXTRA_INFO));
|
||||
}
|
||||
menu = menu.danger(
|
||||
theme::ICON_CANCEL,
|
||||
cancel_text.unwrap_or(TR::send__cancel_sign.into()),
|
||||
verb_cancel.unwrap_or(TR::send__cancel_sign.into()),
|
||||
);
|
||||
unwrap!(menu_items.push(MENU_ITEM_CANCEL));
|
||||
let content_menu = Frame::left_aligned(TString::empty(), menu)
|
||||
@ -159,13 +176,26 @@ pub fn new_confirm_summary(
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let res = SwipeFlow::new(&ConfirmSummary::Summary)?
|
||||
let mut res = SwipeFlow::new(&ConfirmSummary::Summary)?
|
||||
.with_page(&ConfirmSummary::Summary, content_summary)?
|
||||
.with_page(&ConfirmSummary::Hold, content_hold)?
|
||||
.with_page(&ConfirmSummary::Menu, content_menu)?
|
||||
.with_page(&ConfirmSummary::FeeInfo, content_fee)?
|
||||
.with_page(&ConfirmSummary::AccountInfo, content_account)?
|
||||
.with_page(&ConfirmSummary::CancelTap, content_cancel_tap)?;
|
||||
.with_page(&ConfirmSummary::Menu, content_menu)?;
|
||||
if let Some(content_fee) = content_fee {
|
||||
res = res.with_page(&ConfirmSummary::FeeInfo, content_fee)?;
|
||||
} else {
|
||||
res = res.with_page(&ConfirmSummary::FeeInfo, dummy_page())?;
|
||||
};
|
||||
if let Some(content_account) = content_account {
|
||||
res = res.with_page(&ConfirmSummary::AccountInfo, content_account)?
|
||||
} else {
|
||||
res = res.with_page(&ConfirmSummary::AccountInfo, dummy_page())?
|
||||
};
|
||||
if let Some(content_extra) = content_extra {
|
||||
res = res.with_page(&ConfirmSummary::ExtraInfo, content_extra)?
|
||||
} else {
|
||||
res = res.with_page(&ConfirmSummary::ExtraInfo, dummy_page())?
|
||||
};
|
||||
res = res.with_page(&ConfirmSummary::CancelTap, content_cancel_tap)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
@ -624,50 +624,79 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
|
||||
extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let account_items: Obj = kwargs.get(Qstr::MP_QSTR_account_items)?;
|
||||
let account_items_title: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_account_items_title)
|
||||
.unwrap_or(Obj::const_none())
|
||||
let amount: TString = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
||||
let amount_label: TString = kwargs.get(Qstr::MP_QSTR_amount_label)?.try_into()?;
|
||||
let fee: TString = kwargs.get(Qstr::MP_QSTR_fee)?.try_into()?;
|
||||
let fee_label: TString = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?;
|
||||
let title: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_title)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let fee_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_fee_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let account_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_account_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let extra_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_extra_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb_cancel: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let fee_items: Obj = kwargs.get(Qstr::MP_QSTR_fee_items)?;
|
||||
let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?;
|
||||
let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
|
||||
let cancel_text: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_cancel_text)?.try_into_option()?;
|
||||
|
||||
let mut summary_params = ShowInfoParams::new(title)
|
||||
let mut summary_params = ShowInfoParams::new(title.unwrap_or(TString::empty()))
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_swipe_up();
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
summary_params = unwrap!(summary_params.add(label, value));
|
||||
}
|
||||
summary_params = unwrap!(summary_params.add(amount_label, amount));
|
||||
summary_params = unwrap!(summary_params.add(fee_label, fee));
|
||||
|
||||
let mut account_params =
|
||||
ShowInfoParams::new(account_items_title.unwrap_or(TR::send__send_from.into()))
|
||||
.with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(account_items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
account_params = unwrap!(account_params.add(label, value));
|
||||
}
|
||||
|
||||
let mut fee_params =
|
||||
ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(fee_items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
fee_params = unwrap!(fee_params.add(label, value));
|
||||
}
|
||||
// collect available info
|
||||
let fee_params = if let Some(items) = fee_items {
|
||||
let mut fee_params =
|
||||
ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
fee_params = unwrap!(fee_params.add(label, value));
|
||||
}
|
||||
Some(fee_params)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let account_params = if let Some(items) = account_items {
|
||||
let mut account_params =
|
||||
ShowInfoParams::new(TR::send__send_from.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
account_params = unwrap!(account_params.add(label, value));
|
||||
}
|
||||
Some(account_params)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let extra_params = if let Some(items) = extra_items {
|
||||
let mut extra_params =
|
||||
ShowInfoParams::new(TR::words__title_information.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
extra_params = unwrap!(extra_params.add(label, value));
|
||||
}
|
||||
Some(extra_params)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let flow = flow::new_confirm_summary(
|
||||
summary_params,
|
||||
account_params,
|
||||
fee_params,
|
||||
br_name,
|
||||
br_code,
|
||||
cancel_text,
|
||||
account_params,
|
||||
extra_params,
|
||||
verb_cancel,
|
||||
)?;
|
||||
Ok(LayoutObj::new_root(flow)?.into())
|
||||
};
|
||||
@ -761,34 +790,6 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break());
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
|
||||
}
|
||||
|
||||
new_confirm_action_simple(
|
||||
paragraphs.into_paragraphs(),
|
||||
ConfirmActionExtra::Menu(ConfirmActionMenuStrings::new()),
|
||||
ConfirmActionStrings::new(title, None, None, Some(title)),
|
||||
true,
|
||||
None,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.and_then(LayoutObj::new_root)
|
||||
.map(Into::into)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let sign: i32 = kwargs.get(Qstr::MP_QSTR_sign)?.try_into()?;
|
||||
@ -1649,16 +1650,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm value. Merge of confirm_total and confirm_output."""
|
||||
Qstr::MP_QSTR_confirm_value => obj_fn_kw!(0, new_confirm_value).as_obj(),
|
||||
|
||||
/// def confirm_total(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// items: Iterable[tuple[str, str]],
|
||||
/// info_button: bool = False,
|
||||
/// cancel_arrow: bool = False,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Transaction summary. Always hold to confirm."""
|
||||
Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(),
|
||||
|
||||
/// def confirm_modify_output(
|
||||
/// *,
|
||||
/// sign: int,
|
||||
@ -2004,19 +1995,20 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm the recipient, (optionally) confirm the amount and (optionally) confirm the summary and present a Hold to Sign page."""
|
||||
Qstr::MP_QSTR_flow_confirm_output => obj_fn_kw!(0, new_confirm_output).as_obj(),
|
||||
|
||||
/// def flow_confirm_summary(
|
||||
/// def confirm_summary(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// items: Iterable[tuple[str, str]],
|
||||
/// account_items: Iterable[tuple[str, str]],
|
||||
/// account_items_title: str | None,
|
||||
/// fee_items: Iterable[tuple[str, str]],
|
||||
/// br_code: ButtonRequestType,
|
||||
/// br_name: str,
|
||||
/// cancel_text: str | None = None,
|
||||
/// amount: str,
|
||||
/// amount_label: str,
|
||||
/// fee: str,
|
||||
/// fee_label: str,
|
||||
/// title: str | None = None,
|
||||
/// fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// account_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// verb_cancel: str | None = None,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Total summary and hold to confirm."""
|
||||
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(),
|
||||
/// """Confirm summary of a transaction."""
|
||||
Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(),
|
||||
|
||||
/// class BacklightLevels:
|
||||
/// """Backlight levels. Values dynamically update based on user settings."""
|
||||
|
@ -915,6 +915,15 @@ impl ButtonActions {
|
||||
)
|
||||
}
|
||||
|
||||
/// Cancelling with left and confirming with middle
|
||||
pub fn cancel_confirm_none() -> Self {
|
||||
Self::new(
|
||||
Some(ButtonAction::Cancel),
|
||||
Some(ButtonAction::Confirm),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Cancelling with left, confirming with middle and info with right
|
||||
pub fn cancel_confirm_info() -> Self {
|
||||
Self::new(
|
||||
|
@ -45,6 +45,7 @@ use crate::{
|
||||
},
|
||||
ComponentExt, FormattedText, Label, LineBreaking, Never, Timeout,
|
||||
},
|
||||
display::Font,
|
||||
geometry,
|
||||
layout::{
|
||||
base::LAYOUT_STATE,
|
||||
@ -638,131 +639,110 @@ extern "C" fn new_confirm_output_amount(n_args: usize, args: *const Obj, kwargs:
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = |_args: &[Obj], kwargs: &Map| {
|
||||
let total_amount: TString = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?;
|
||||
let fee_amount: TString = kwargs.get(Qstr::MP_QSTR_fee_amount)?.try_into()?;
|
||||
let fee_rate_amount: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_fee_rate_amount)?
|
||||
.try_into_option()?;
|
||||
let account_label: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_account_label)?.try_into_option()?;
|
||||
let total_label: TString = kwargs.get(Qstr::MP_QSTR_total_label)?.try_into()?;
|
||||
let amount: TString = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
||||
let amount_label: TString = kwargs.get(Qstr::MP_QSTR_amount_label)?.try_into()?;
|
||||
let fee: TString = kwargs.get(Qstr::MP_QSTR_fee)?.try_into()?;
|
||||
let fee_label: TString = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?;
|
||||
let _title: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_title)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let fee_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_fee_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let account_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_account_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let extra_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_extra_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb_cancel: Option<TString<'static>> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
|
||||
// collect available info pages
|
||||
let mut info_pages: Vec<(TString, Obj), 3> = Vec::new();
|
||||
if let Some(info) = fee_items {
|
||||
unwrap!(info_pages.push((TR::confirm_total__title_fee.into(), info)));
|
||||
}
|
||||
if let Some(info) = account_items {
|
||||
unwrap!(info_pages.push((TR::confirm_total__title_sending_from.into(), info)));
|
||||
}
|
||||
if let Some(info) = extra_items {
|
||||
unwrap!(info_pages.push((TR::words__title_information.into(), info)));
|
||||
}
|
||||
|
||||
// button layouts and actions
|
||||
let verb_cancel: TString = verb_cancel.unwrap_or(TString::empty());
|
||||
let btns_summary_page = move |has_pages_after: bool| -> (ButtonLayout, ButtonActions) {
|
||||
// if there are no info pages, the right button is not needed
|
||||
// if verb_cancel is "^", the left button is an arrow pointing up
|
||||
let left_btn = Some(ButtonDetails::from_text_possible_icon(verb_cancel));
|
||||
let right_btn = has_pages_after.then(|| {
|
||||
ButtonDetails::text("i".into())
|
||||
.with_fixed_width(theme::BUTTON_ICON_WIDTH)
|
||||
.with_font(Font::NORMAL)
|
||||
});
|
||||
let middle_btn = Some(ButtonDetails::armed_text(TR::buttons__confirm.into()));
|
||||
|
||||
(
|
||||
ButtonLayout::new(left_btn, middle_btn, right_btn),
|
||||
if has_pages_after {
|
||||
ButtonActions::cancel_confirm_next()
|
||||
} else {
|
||||
ButtonActions::cancel_confirm_none()
|
||||
},
|
||||
)
|
||||
};
|
||||
let btns_info_page = |is_last: bool| -> (ButtonLayout, ButtonActions) {
|
||||
// on the last info page, the right button is not needed
|
||||
if is_last {
|
||||
(
|
||||
ButtonLayout::arrow_none_none(),
|
||||
ButtonActions::prev_none_none(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
ButtonLayout::arrow_none_arrow(),
|
||||
ButtonActions::prev_none_next(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let total_pages = 1 + info_pages.len();
|
||||
let get_page = move |page_index| {
|
||||
match page_index {
|
||||
0 => {
|
||||
// Total amount + fee
|
||||
let btn_layout = ButtonLayout::cancel_armed_info(TR::buttons__confirm.into());
|
||||
let btn_actions = ButtonActions::cancel_confirm_next();
|
||||
let (btn_layout, btn_actions) = btns_summary_page(!info_pages.is_empty());
|
||||
|
||||
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
||||
.text_bold(total_label)
|
||||
.text_bold(amount_label)
|
||||
.newline()
|
||||
.text_mono(total_amount)
|
||||
.text_mono(amount)
|
||||
.newline()
|
||||
.newline()
|
||||
.text_bold(fee_label)
|
||||
.newline()
|
||||
.text_mono(fee_amount);
|
||||
.text_mono(fee);
|
||||
|
||||
let formatted = FormattedText::new(ops);
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
}
|
||||
1 => {
|
||||
// Fee rate info
|
||||
let btn_layout = ButtonLayout::arrow_none_arrow();
|
||||
let btn_actions = ButtonActions::prev_none_next();
|
||||
|
||||
let fee_rate_amount = fee_rate_amount.unwrap_or("".into());
|
||||
|
||||
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
||||
.text_bold_upper(TR::confirm_total__title_fee)
|
||||
.newline()
|
||||
.newline()
|
||||
.newline_half()
|
||||
.text_bold(TR::confirm_total__fee_rate_colon)
|
||||
.newline()
|
||||
.text_mono(fee_rate_amount);
|
||||
|
||||
let formatted = FormattedText::new(ops);
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
}
|
||||
2 => {
|
||||
// Wallet and account info
|
||||
let btn_layout = ButtonLayout::arrow_none_none();
|
||||
let btn_actions = ButtonActions::prev_none_none();
|
||||
|
||||
let account_label = account_label.unwrap_or("".into());
|
||||
|
||||
// TODO: include wallet info when available
|
||||
|
||||
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
||||
.text_bold_upper(TR::confirm_total__title_sending_from)
|
||||
.newline()
|
||||
.newline()
|
||||
.newline_half()
|
||||
.text_bold(TR::words__account_colon)
|
||||
.newline()
|
||||
.text_mono(account_label);
|
||||
|
||||
let formatted = FormattedText::new(ops);
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 3);
|
||||
|
||||
let obj = LayoutObj::new(Flow::new(pages))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = |_args: &[Obj], kwargs: &Map| {
|
||||
let amount_title: TString = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?;
|
||||
let amount_value: TString = kwargs.get(Qstr::MP_QSTR_amount_value)?.try_into()?;
|
||||
let fee_title: TString = kwargs.get(Qstr::MP_QSTR_fee_title)?.try_into()?;
|
||||
let fee_value: TString = kwargs.get(Qstr::MP_QSTR_fee_value)?.try_into()?;
|
||||
let items_title: TString = kwargs.get(Qstr::MP_QSTR_items_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let cancel_cross: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_cross, false)?;
|
||||
|
||||
let get_page = move |page_index| {
|
||||
match page_index {
|
||||
0 => {
|
||||
// Amount + fee
|
||||
let btn_layout = if cancel_cross {
|
||||
ButtonLayout::cancel_armed_info(TR::buttons__confirm.into())
|
||||
} else {
|
||||
ButtonLayout::up_arrow_armed_info(TR::buttons__confirm.into())
|
||||
};
|
||||
let btn_actions = ButtonActions::cancel_confirm_next();
|
||||
|
||||
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
||||
.text_bold(amount_title)
|
||||
.newline()
|
||||
.text_mono(amount_value)
|
||||
.newline()
|
||||
.newline_half()
|
||||
.text_bold(fee_title)
|
||||
.newline()
|
||||
.text_mono(fee_value);
|
||||
|
||||
let formatted = FormattedText::new(ops);
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
}
|
||||
1 => {
|
||||
// Other information
|
||||
let btn_layout = ButtonLayout::arrow_none_none();
|
||||
let btn_actions = ButtonActions::prev_none_none();
|
||||
i => {
|
||||
// Other info pages as provided
|
||||
let (title, info_obj) = &info_pages[i - 1];
|
||||
let is_last = i == total_pages - 1;
|
||||
let (btn_layout, btn_actions) = btns_info_page(is_last);
|
||||
|
||||
let mut ops = OpTextLayout::new(theme::TEXT_MONO);
|
||||
|
||||
for item in unwrap!(IterBuf::new().try_iterate(items)) {
|
||||
for item in unwrap!(IterBuf::new().try_iterate(*info_obj)) {
|
||||
let [key, value]: [Obj; 2] = unwrap!(util::iter_into_array(item));
|
||||
if !ops.is_empty() {
|
||||
// Each key-value pair is on its own page
|
||||
@ -776,13 +756,12 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
|
||||
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
.with_title(items_title)
|
||||
.with_slim_arrows()
|
||||
.with_title(*title)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 2);
|
||||
let pages = FlowPages::new(get_page, total_pages);
|
||||
|
||||
let obj = LayoutObj::new(Flow::new(pages).with_scrollbar(false))?;
|
||||
Ok(obj.into())
|
||||
@ -1810,30 +1789,20 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm output amount."""
|
||||
Qstr::MP_QSTR_confirm_output_amount => obj_fn_kw!(0, new_confirm_output_amount).as_obj(),
|
||||
|
||||
/// def confirm_total(
|
||||
/// def confirm_summary(
|
||||
/// *,
|
||||
/// total_amount: str,
|
||||
/// fee_amount: str,
|
||||
/// fee_rate_amount: str | None,
|
||||
/// account_label: str | None,
|
||||
/// total_label: str,
|
||||
/// amount: str,
|
||||
/// amount_label: str,
|
||||
/// fee: str,
|
||||
/// fee_label: str,
|
||||
/// title: str | None = None,
|
||||
/// fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// account_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// verb_cancel: str | None = None,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Confirm summary of a transaction."""
|
||||
Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(),
|
||||
|
||||
/// def altcoin_tx_summary(
|
||||
/// *,
|
||||
/// amount_title: str,
|
||||
/// amount_value: str,
|
||||
/// fee_title: str,
|
||||
/// fee_value: str,
|
||||
/// items_title: str,
|
||||
/// items: Iterable[Tuple[str, str]],
|
||||
/// cancel_cross: bool = False,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Confirm details about altcoin transaction."""
|
||||
Qstr::MP_QSTR_altcoin_tx_summary => obj_fn_kw!(0, new_altcoin_tx_summary).as_obj(),
|
||||
Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(),
|
||||
|
||||
/// def tutorial() -> LayoutObj[UiResult]:
|
||||
/// """Show user how to interact with the device."""
|
||||
|
@ -93,20 +93,12 @@ where
|
||||
left: Option<TString<'static>>,
|
||||
right: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
let cancel = match left {
|
||||
Some(verb) => verb.map(|s| match s {
|
||||
"^" => Button::with_icon(theme::ICON_UP),
|
||||
"<" => Button::with_icon(theme::ICON_BACK),
|
||||
_ => Button::with_text(verb),
|
||||
}),
|
||||
_ => Button::with_icon(theme::ICON_CANCEL),
|
||||
};
|
||||
let confirm = match right {
|
||||
Some(verb) => Button::with_text(verb).styled(theme::button_confirm()),
|
||||
_ => Button::with_icon(theme::ICON_CONFIRM).styled(theme::button_confirm()),
|
||||
};
|
||||
self.button_cancel = Some(cancel);
|
||||
self.button_confirm = confirm;
|
||||
self = self.with_cancel_button(left);
|
||||
self
|
||||
}
|
||||
|
||||
@ -117,8 +109,16 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cancel_arrow(mut self) -> Self {
|
||||
self.button_cancel = Some(Button::with_icon(theme::ICON_UP));
|
||||
pub fn with_cancel_button(mut self, left: Option<TString<'static>>) -> Self {
|
||||
let cancel = match left {
|
||||
Some(verb) => verb.map(|s| match s {
|
||||
"^" => Button::with_icon(theme::ICON_UP),
|
||||
"<" => Button::with_icon(theme::ICON_BACK),
|
||||
_ => Button::with_text(verb),
|
||||
}),
|
||||
_ => Button::with_icon(theme::ICON_CANCEL),
|
||||
};
|
||||
self.button_cancel = Some(cancel);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -763,28 +763,53 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
|
||||
let cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?;
|
||||
let amount: TString = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
||||
let amount_label: TString = kwargs.get(Qstr::MP_QSTR_amount_label)?.try_into()?;
|
||||
let fee: TString = kwargs.get(Qstr::MP_QSTR_fee)?.try_into()?;
|
||||
let fee_label: TString = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?;
|
||||
let title: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_title)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let fee_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_fee_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let account_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_account_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let extra_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_extra_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb_cancel: Option<TString<'static>> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
let info_button: bool =
|
||||
fee_items.is_some() || account_items.is_some() || extra_items.is_some();
|
||||
let paragraphs = ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_NORMAL, amount_label).no_break(),
|
||||
Paragraph::new(&theme::TEXT_MONO, amount),
|
||||
Paragraph::new(&theme::TEXT_NORMAL, fee_label).no_break(),
|
||||
Paragraph::new(&theme::TEXT_MONO, fee),
|
||||
]);
|
||||
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break());
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
|
||||
}
|
||||
let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?;
|
||||
if cancel_arrow {
|
||||
page = page.with_cancel_arrow()
|
||||
}
|
||||
let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_hold()?
|
||||
.with_cancel_button(verb_cancel);
|
||||
if info_button {
|
||||
page = page.with_swipe_left();
|
||||
}
|
||||
let mut frame = Frame::left_aligned(theme::label_title(), title, page);
|
||||
let mut frame = Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title.unwrap_or(TString::empty()),
|
||||
page,
|
||||
);
|
||||
if info_button {
|
||||
frame = frame.with_info_button();
|
||||
}
|
||||
@ -1868,15 +1893,20 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm value. Merge of confirm_total and confirm_output."""
|
||||
Qstr::MP_QSTR_confirm_value => obj_fn_kw!(0, new_confirm_value).as_obj(),
|
||||
|
||||
/// def confirm_total(
|
||||
/// def confirm_summary(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// items: Iterable[tuple[str, str]],
|
||||
/// info_button: bool = False,
|
||||
/// cancel_arrow: bool = False,
|
||||
/// amount: str,
|
||||
/// amount_label: str,
|
||||
/// fee: str,
|
||||
/// fee_label: str,
|
||||
/// title: str | None = None,
|
||||
/// fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// account_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// verb_cancel: str | None = None,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Transaction summary. Always hold to confirm."""
|
||||
Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(),
|
||||
/// """Confirm summary of a transaction."""
|
||||
Qstr::MP_QSTR_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(),
|
||||
|
||||
/// def confirm_modify_output(
|
||||
/// *,
|
||||
|
@ -410,6 +410,9 @@ STATIC mp_obj_tuple_t mod_trezorutils_version_obj = {
|
||||
/// """UI layout identifier ("tt" for model T, "tr" for models One and R)."""
|
||||
/// USE_THP: bool
|
||||
/// """Whether the firmware supports Trezor-Host Protocol (version 2)."""
|
||||
/// if __debug__:
|
||||
/// DISABLE_ANIMATION: bool
|
||||
/// """Whether the firmware should disable animations."""
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorutils)},
|
||||
@ -502,6 +505,13 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
|
||||
#else
|
||||
#error Unknown layout
|
||||
#endif
|
||||
#if !PYOPT
|
||||
#if DISABLE_ANIMATION
|
||||
{MP_ROM_QSTR(MP_QSTR_DISABLE_ANIMATION), mp_const_true},
|
||||
#else
|
||||
{MP_ROM_QSTR(MP_QSTR_DISABLE_ANIMATION), mp_const_false},
|
||||
#endif // TREZOR_DISABLE_ANIMATION
|
||||
#endif // PYOPT
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorutils_globals,
|
||||
|
@ -144,17 +144,6 @@ def confirm_value(
|
||||
"""Confirm value. Merge of confirm_total and confirm_output."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def confirm_total(
|
||||
*,
|
||||
title: str,
|
||||
items: Iterable[tuple[str, str]],
|
||||
info_button: bool = False,
|
||||
cancel_arrow: bool = False,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Transaction summary. Always hold to confirm."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def confirm_modify_output(
|
||||
*,
|
||||
@ -534,18 +523,19 @@ def flow_confirm_output(
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def flow_confirm_summary(
|
||||
def confirm_summary(
|
||||
*,
|
||||
title: str,
|
||||
items: Iterable[tuple[str, str]],
|
||||
account_items: Iterable[tuple[str, str]],
|
||||
account_items_title: str | None,
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
br_code: ButtonRequestType,
|
||||
br_name: str,
|
||||
cancel_text: str | None = None,
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
account_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Total summary and hold to confirm."""
|
||||
"""Confirm summary of a transaction."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
@ -743,32 +733,21 @@ def confirm_output_amount(
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def confirm_total(
|
||||
def confirm_summary(
|
||||
*,
|
||||
total_amount: str,
|
||||
fee_amount: str,
|
||||
fee_rate_amount: str | None,
|
||||
account_label: str | None,
|
||||
total_label: str,
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
account_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Confirm summary of a transaction."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def altcoin_tx_summary(
|
||||
*,
|
||||
amount_title: str,
|
||||
amount_value: str,
|
||||
fee_title: str,
|
||||
fee_value: str,
|
||||
items_title: str,
|
||||
items: Iterable[Tuple[str, str]],
|
||||
cancel_cross: bool = False,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Confirm details about altcoin transaction."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def tutorial() -> LayoutObj[UiResult]:
|
||||
"""Show user how to interact with the device."""
|
||||
@ -1290,14 +1269,19 @@ def confirm_value(
|
||||
|
||||
|
||||
# rust/src/ui/model_tt/layout.rs
|
||||
def confirm_total(
|
||||
def confirm_summary(
|
||||
*,
|
||||
title: str,
|
||||
items: Iterable[tuple[str, str]],
|
||||
info_button: bool = False,
|
||||
cancel_arrow: bool = False,
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
account_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Transaction summary. Always hold to confirm."""
|
||||
"""Confirm summary of a transaction."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tt/layout.rs
|
||||
|
@ -152,3 +152,6 @@ UI_LAYOUT: str
|
||||
"""UI layout identifier ("tt" for model T, "tr" for models One and R)."""
|
||||
USE_THP: bool
|
||||
"""Whether the firmware supports Trezor-Host Protocol (version 2)."""
|
||||
if __debug__:
|
||||
DISABLE_ANIMATION: bool
|
||||
"""Whether the firmware should disable animations."""
|
||||
|
@ -24,7 +24,7 @@ else:
|
||||
|
||||
|
||||
if __debug__:
|
||||
trezorui2.disable_animation(bool(utils.DISABLE_ANIMATION))
|
||||
trezorui2.disable_animation(utils.DISABLE_ANIMATION)
|
||||
|
||||
|
||||
# all rendering is done through a singleton of `Display`
|
||||
|
@ -686,10 +686,6 @@ def confirm_total(
|
||||
total_label = total_label or TR.send__total_amount # def_arg
|
||||
fee_label = fee_label or TR.send__incl_transaction_fee # def_arg
|
||||
|
||||
items = [
|
||||
(total_label, total_amount),
|
||||
(fee_label, fee_amount),
|
||||
]
|
||||
fee_items = []
|
||||
account_items = []
|
||||
if source_account:
|
||||
@ -700,44 +696,48 @@ def confirm_total(
|
||||
fee_items.append((TR.confirm_total__fee_rate, fee_rate_amount))
|
||||
|
||||
return raise_if_not_confirmed(
|
||||
trezorui2.flow_confirm_summary(
|
||||
trezorui2.confirm_summary(
|
||||
amount=total_amount,
|
||||
amount_label=total_label,
|
||||
fee=fee_amount,
|
||||
fee_label=fee_label,
|
||||
title=title,
|
||||
items=items,
|
||||
fee_items=fee_items,
|
||||
account_items=account_items,
|
||||
account_items_title=None,
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
cancel_text=TR.send__cancel_sign,
|
||||
fee_items=fee_items or None,
|
||||
account_items=account_items or None,
|
||||
),
|
||||
None,
|
||||
br_name,
|
||||
br_code,
|
||||
)
|
||||
|
||||
|
||||
def _confirm_summary(
|
||||
items: Iterable[tuple[str, str]] | None = None,
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
info_items: Iterable[tuple[str, str]] | None = None,
|
||||
info_title: str | None = None,
|
||||
fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
account_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_title: str | None = None,
|
||||
br_name: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
cancel_text: str | None = None,
|
||||
) -> Awaitable[None]:
|
||||
title = title or TR.words__title_summary # def_arg
|
||||
|
||||
return raise_if_not_confirmed(
|
||||
trezorui2.flow_confirm_summary(
|
||||
trezorui2.confirm_summary(
|
||||
amount=amount,
|
||||
amount_label=amount_label,
|
||||
fee=fee,
|
||||
fee_label=fee_label,
|
||||
title=title,
|
||||
items=items or (),
|
||||
fee_items=fee_items or (),
|
||||
account_items=info_items or (),
|
||||
account_items_title=info_title,
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
cancel_text=cancel_text,
|
||||
fee_items=fee_items or None,
|
||||
account_items=account_items or None,
|
||||
extra_items=extra_items or None,
|
||||
),
|
||||
None,
|
||||
br_name,
|
||||
br_code,
|
||||
)
|
||||
|
||||
|
||||
@ -853,8 +853,11 @@ if not utils.BITCOIN_ONLY:
|
||||
) # def_arg
|
||||
fee_title = fee_title or TR.words__fee # def_arg
|
||||
return _confirm_summary(
|
||||
items=((amount_title, amount), (fee_title, fee)),
|
||||
info_items=items,
|
||||
amount,
|
||||
amount_title,
|
||||
fee,
|
||||
fee_title,
|
||||
extra_items=items,
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
)
|
||||
@ -866,13 +869,13 @@ if not utils.BITCOIN_ONLY:
|
||||
) -> Awaitable[None]:
|
||||
amount_title = TR.send__total_amount
|
||||
fee_title = TR.send__incl_transaction_fee
|
||||
more_info_title = TR.buttons__more_info
|
||||
|
||||
return _confirm_summary(
|
||||
items=((amount_title, amount), (fee_title, fee)),
|
||||
info_items=items,
|
||||
info_title=more_info_title,
|
||||
fee_items=None,
|
||||
amount,
|
||||
amount_title,
|
||||
fee,
|
||||
fee_title,
|
||||
extra_items=items,
|
||||
br_name="confirm_cardano_tx",
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
)
|
||||
@ -880,10 +883,10 @@ if not utils.BITCOIN_ONLY:
|
||||
|
||||
def confirm_joint_total(spending_amount: str, total_amount: str) -> Awaitable[None]:
|
||||
return _confirm_summary(
|
||||
items=(
|
||||
(TR.send__you_are_contributing, spending_amount),
|
||||
(TR.send__to_the_total_amount, total_amount),
|
||||
),
|
||||
spending_amount,
|
||||
TR.send__you_are_contributing,
|
||||
total_amount,
|
||||
TR.send__to_the_total_amount,
|
||||
title=TR.send__title_joint_transaction,
|
||||
br_name="confirm_joint_total",
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
|
@ -814,15 +814,22 @@ def confirm_total(
|
||||
) -> Awaitable[None]:
|
||||
total_label = total_label or f"{TR.send__total_amount}:" # def_arg
|
||||
fee_label = fee_label or TR.send__including_fee # def_arg
|
||||
return interact(
|
||||
# TODO: resolve these differences in TT's and TR's confirm_total
|
||||
trezorui2.confirm_total( # type: ignore [Arguments missing]
|
||||
total_amount=total_amount, # type: ignore [No parameter named]
|
||||
fee_amount=fee_amount, # type: ignore [No parameter named]
|
||||
fee_rate_amount=fee_rate_amount, # type: ignore [No parameter named]
|
||||
account_label=source_account, # type: ignore [No parameter named]
|
||||
total_label=total_label, # type: ignore [No parameter named]
|
||||
fee_label=fee_label, # type: ignore [No parameter named]
|
||||
|
||||
fee_info_items = []
|
||||
if fee_rate_amount:
|
||||
fee_info_items.append((TR.confirm_total__fee_rate_colon, fee_rate_amount))
|
||||
account_info_items = []
|
||||
if source_account:
|
||||
account_info_items.append((TR.words__account_colon, source_account))
|
||||
|
||||
return raise_if_not_confirmed(
|
||||
trezorui2.confirm_summary(
|
||||
amount=total_amount,
|
||||
amount_label=total_label,
|
||||
fee=fee_amount,
|
||||
fee_label=fee_label,
|
||||
fee_items=fee_info_items or None,
|
||||
account_items=account_info_items or None,
|
||||
),
|
||||
br_name,
|
||||
br_code,
|
||||
@ -871,14 +878,12 @@ if not utils.BITCOIN_ONLY:
|
||||
amount_title = f"{TR.words__amount}:"
|
||||
amount_value = total_amount
|
||||
await raise_if_not_confirmed(
|
||||
trezorui2.altcoin_tx_summary(
|
||||
amount_title=amount_title,
|
||||
amount_value=amount_value,
|
||||
fee_title=f"{TR.send__maximum_fee}:",
|
||||
fee_value=maximum_fee,
|
||||
items_title=TR.confirm_total__title_fee,
|
||||
items=[(f"{k}:", v) for (k, v) in info_items],
|
||||
cancel_cross=True,
|
||||
trezorui2.confirm_summary(
|
||||
amount=amount_value,
|
||||
amount_label=amount_title,
|
||||
fee=maximum_fee,
|
||||
fee_label=f"{TR.send__maximum_fee}:",
|
||||
fee_items=[(f"{k}:", v) for (k, v) in info_items],
|
||||
),
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
@ -898,14 +903,12 @@ if not utils.BITCOIN_ONLY:
|
||||
) # def_arg
|
||||
fee_title = fee_title or TR.words__fee # def_arg
|
||||
return raise_if_not_confirmed(
|
||||
trezorui2.altcoin_tx_summary(
|
||||
amount_title=amount_title,
|
||||
amount_value=amount,
|
||||
fee_title=fee_title,
|
||||
fee_value=fee,
|
||||
items_title=TR.confirm_total__title_fee,
|
||||
items=items,
|
||||
cancel_cross=True,
|
||||
trezorui2.confirm_summary(
|
||||
amount=amount,
|
||||
amount_label=amount_title,
|
||||
fee=fee,
|
||||
fee_label=fee_title,
|
||||
fee_items=items,
|
||||
),
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
@ -922,14 +925,12 @@ if not utils.BITCOIN_ONLY:
|
||||
fee_title = TR.send__including_fee
|
||||
|
||||
return raise_if_not_confirmed(
|
||||
trezorui2.altcoin_tx_summary(
|
||||
amount_title=amount_title,
|
||||
amount_value=amount,
|
||||
fee_title=fee_title,
|
||||
fee_value=fee,
|
||||
items_title=TR.words__title_information,
|
||||
items=items,
|
||||
cancel_cross=True,
|
||||
trezorui2.confirm_summary(
|
||||
amount=amount,
|
||||
amount_label=amount_title,
|
||||
fee=fee,
|
||||
fee_label=fee_title,
|
||||
extra_items=items,
|
||||
),
|
||||
br_name="confirm_cardano_tx",
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
@ -947,13 +948,12 @@ if not utils.BITCOIN_ONLY:
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
chunkify: bool = False,
|
||||
) -> None:
|
||||
summary_layout = trezorui2.altcoin_tx_summary(
|
||||
amount_title=f"{TR.words__amount}:",
|
||||
amount_value=total_amount,
|
||||
fee_title=f"{TR.send__maximum_fee}:",
|
||||
fee_value=maximum_fee,
|
||||
items_title=TR.confirm_total__title_fee,
|
||||
items=[(f"{k}:", v) for (k, v) in fee_info_items],
|
||||
summary_layout = trezorui2.confirm_summary(
|
||||
amount=total_amount,
|
||||
amount_label=f"{TR.words__amount}:",
|
||||
fee=maximum_fee,
|
||||
fee_label=f"{TR.send__maximum_fee}:",
|
||||
fee_items=[(f"{k}:", v) for (k, v) in fee_info_items],
|
||||
)
|
||||
|
||||
if not is_contract_interaction:
|
||||
|
@ -750,43 +750,66 @@ def confirm_total(
|
||||
total_label = total_label or f"{TR.send__total_amount}:" # def_arg
|
||||
fee_label = fee_label or TR.send__including_fee # def_arg
|
||||
|
||||
items = [
|
||||
(total_label, total_amount),
|
||||
(fee_label, fee_amount),
|
||||
]
|
||||
info_items = []
|
||||
account_info_items = []
|
||||
fee_info_items = []
|
||||
extra_info_items = []
|
||||
if source_account:
|
||||
info_items.append((TR.confirm_total__sending_from_account, source_account))
|
||||
account_info_items.append(
|
||||
(TR.confirm_total__sending_from_account, source_account)
|
||||
)
|
||||
if fee_rate_amount:
|
||||
info_items.append((f"{TR.confirm_total__fee_rate}:", fee_rate_amount))
|
||||
fee_info_items.append((f"{TR.confirm_total__fee_rate}:", fee_rate_amount))
|
||||
|
||||
return _confirm_summary(
|
||||
items,
|
||||
TR.words__title_summary,
|
||||
info_items=info_items,
|
||||
total_amount,
|
||||
total_label,
|
||||
fee_amount,
|
||||
fee_label,
|
||||
title=title,
|
||||
fee_items=fee_info_items,
|
||||
account_items=account_info_items,
|
||||
extra_items=extra_info_items,
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
)
|
||||
|
||||
|
||||
def _confirm_summary(
|
||||
items: Iterable[tuple[str, str]],
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
info_items: Iterable[tuple[str, str]] | None = None,
|
||||
info_title: str | None = None,
|
||||
fee_items: Iterable[tuple[str, str]] | None = None,
|
||||
account_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_title: str | None = None,
|
||||
br_name: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> Awaitable[None]:
|
||||
title = title or TR.words__title_summary # def_arg
|
||||
|
||||
total_layout = trezorui2.confirm_total(
|
||||
total_layout = trezorui2.confirm_summary(
|
||||
amount=amount,
|
||||
amount_label=amount_label,
|
||||
fee=fee,
|
||||
fee_label=fee_label,
|
||||
title=title,
|
||||
items=items,
|
||||
info_button=bool(info_items),
|
||||
fee_items=fee_items or None,
|
||||
account_items=account_items or None,
|
||||
extra_items=extra_items or None,
|
||||
)
|
||||
info_items = info_items or []
|
||||
|
||||
# TODO: use `_info` params directly in this^ layout instead of using `with_info`
|
||||
info_items = []
|
||||
if fee_items:
|
||||
info_items.extend(fee_items)
|
||||
if account_items:
|
||||
info_items.extend(account_items)
|
||||
if extra_items:
|
||||
info_items.extend(extra_items)
|
||||
info_layout = trezorui2.show_info_with_cancel(
|
||||
title=info_title if info_title else TR.words__title_information,
|
||||
title=extra_title if extra_title else TR.words__title_information,
|
||||
items=info_items,
|
||||
)
|
||||
return with_info(total_layout, info_layout, br_name, br_code)
|
||||
@ -811,14 +834,15 @@ if not utils.BITCOIN_ONLY:
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
chunkify: bool = False,
|
||||
) -> None:
|
||||
total_layout = trezorui2.confirm_total(
|
||||
# NOTE: fee_info used so that info button is shown
|
||||
total_layout = trezorui2.confirm_summary(
|
||||
amount=total_amount,
|
||||
amount_label=f"{TR.words__amount}:",
|
||||
fee=maximum_fee,
|
||||
fee_label=f"{TR.send__maximum_fee}:",
|
||||
title=TR.words__title_summary,
|
||||
items=[
|
||||
(f"{TR.words__amount}:", total_amount),
|
||||
(f"{TR.send__maximum_fee}:", maximum_fee),
|
||||
],
|
||||
info_button=True,
|
||||
cancel_arrow=True,
|
||||
fee_items=fee_info_items,
|
||||
verb_cancel="^",
|
||||
)
|
||||
info_layout = trezorui2.show_info_with_cancel(
|
||||
title=TR.confirm_total__title_fee,
|
||||
@ -879,17 +903,23 @@ if not utils.BITCOIN_ONLY:
|
||||
|
||||
# confirmation
|
||||
if verb == TR.ethereum__staking_claim:
|
||||
items = ((f"{TR.send__maximum_fee}:", maximum_fee),)
|
||||
amount = ""
|
||||
amount_label = ""
|
||||
fee_label = f"{TR.send__maximum_fee}:"
|
||||
fee = maximum_fee
|
||||
else:
|
||||
items = (
|
||||
(f"{TR.words__amount}:", total_amount),
|
||||
(f"{TR.send__maximum_fee}:", maximum_fee),
|
||||
)
|
||||
amount_label = f"{TR.words__amount}:"
|
||||
amount = total_amount
|
||||
fee_label = f"{TR.send__maximum_fee}:"
|
||||
fee = maximum_fee
|
||||
await _confirm_summary(
|
||||
items, # items
|
||||
amount,
|
||||
amount_label,
|
||||
fee,
|
||||
fee_label,
|
||||
title=title,
|
||||
info_title=TR.confirm_total__title_fee,
|
||||
info_items=[(f"{k}:", v) for (k, v) in info_items],
|
||||
extra_items=[(f"{k}:", v) for (k, v) in info_items],
|
||||
extra_title=TR.confirm_total__title_fee,
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
)
|
||||
@ -908,8 +938,11 @@ if not utils.BITCOIN_ONLY:
|
||||
) # def_arg
|
||||
fee_title = fee_title or TR.words__fee # def_arg
|
||||
return _confirm_summary(
|
||||
((amount_title, amount), (fee_title, fee)),
|
||||
info_items=items,
|
||||
amount,
|
||||
amount_title,
|
||||
fee,
|
||||
fee_title,
|
||||
extra_items=items,
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
)
|
||||
@ -922,8 +955,11 @@ if not utils.BITCOIN_ONLY:
|
||||
amount_title = f"{TR.send__total_amount}:"
|
||||
fee_title = TR.send__including_fee
|
||||
return _confirm_summary(
|
||||
((amount_title, amount), (fee_title, fee)),
|
||||
info_items=items,
|
||||
amount,
|
||||
amount_title,
|
||||
fee,
|
||||
fee_title,
|
||||
extra_items=items,
|
||||
br_name="confirm_cardano_tx",
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
)
|
||||
@ -931,12 +967,13 @@ if not utils.BITCOIN_ONLY:
|
||||
|
||||
def confirm_joint_total(spending_amount: str, total_amount: str) -> Awaitable[None]:
|
||||
return raise_if_not_confirmed(
|
||||
trezorui2.confirm_total(
|
||||
# FIXME: arguments for amount/fee are misused here
|
||||
trezorui2.confirm_summary(
|
||||
amount=spending_amount,
|
||||
amount_label=TR.send__you_are_contributing,
|
||||
fee=total_amount,
|
||||
fee_label=TR.send__to_the_total_amount,
|
||||
title=TR.send__title_joint_transaction,
|
||||
items=[
|
||||
(TR.send__you_are_contributing, spending_amount),
|
||||
(TR.send__to_the_total_amount, total_amount),
|
||||
],
|
||||
),
|
||||
"confirm_joint_total",
|
||||
ButtonRequestType.SignTx,
|
||||
|
@ -33,17 +33,20 @@ from trezorutils import ( # noqa: F401
|
||||
)
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
DISABLE_ANIMATION = 0
|
||||
|
||||
if __debug__:
|
||||
if EMULATOR:
|
||||
import uos
|
||||
|
||||
DISABLE_ANIMATION = int(uos.getenv("TREZOR_DISABLE_ANIMATION") or "0")
|
||||
LOG_MEMORY = int(uos.getenv("TREZOR_LOG_MEMORY") or "0")
|
||||
DISABLE_ANIMATION = uos.getenv("TREZOR_DISABLE_ANIMATION") == "1"
|
||||
LOG_MEMORY = uos.getenv("TREZOR_LOG_MEMORY") == "1"
|
||||
else:
|
||||
from trezorutils import DISABLE_ANIMATION # noqa: F401
|
||||
|
||||
LOG_MEMORY = 0
|
||||
|
||||
else:
|
||||
DISABLE_ANIMATION = False
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Iterator, Protocol, Sequence, TypeVar
|
||||
|
||||
|
@ -284,11 +284,15 @@ void norcow_wipe(void) {
|
||||
// Erase the active sector first, because it contains sensitive data.
|
||||
erase_sector(norcow_active_sector, sectrue);
|
||||
|
||||
#if STORAGE_INSECURE_TESTING_MODE && !PRODUCTION
|
||||
// skip erasing inactive sectors
|
||||
#else
|
||||
for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) {
|
||||
if (i != norcow_active_sector) {
|
||||
erase_sector(i, secfalse);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
norcow_active_version = NORCOW_VERSION;
|
||||
norcow_write_sector = norcow_active_sector;
|
||||
norcow_free_offset = NORCOW_STORAGE_START;
|
||||
|
@ -86,8 +86,12 @@ const uint32_t V0_PIN_EMPTY = 1;
|
||||
// up constant storage space.
|
||||
#define MAX_WIPE_CODE_LEN 50
|
||||
|
||||
#if STORAGE_INSECURE_TESTING_MODE && !PRODUCTION
|
||||
#define PIN_ITER_COUNT 1
|
||||
#else
|
||||
// The total number of iterations to use in PBKDF2.
|
||||
#define PIN_ITER_COUNT 20000
|
||||
#endif
|
||||
|
||||
// The minimum number of milliseconds between progress updates.
|
||||
#define MIN_PROGRESS_UPDATE_MS 100
|
||||
|
@ -2,3 +2,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t hamming_weight(uint32_t value);
|
||||
|
||||
#ifndef STORAGE_INSECURE_TESTING_MODE
|
||||
#define STORAGE_INSECURE_TESTING_MODE 0
|
||||
#endif
|
||||
|
||||
#if STORAGE_INSECURE_TESTING_MODE
|
||||
#if PRODUCTION
|
||||
#error "STORAGE_INSECURE_TESTING_MODE can't be used in production"
|
||||
#else
|
||||
#pragma message("STORAGE IS INSECURE DO NOT USE THIS IN PRODUCTION")
|
||||
#endif
|
||||
#endif
|
||||
|
@ -835,10 +835,10 @@ def sign_tx_go_to_info_tr(
|
||||
yield
|
||||
|
||||
layout = client.debug.press_right()
|
||||
screen_texts.append(layout.text_content())
|
||||
screen_texts.append(layout.visible_screen())
|
||||
|
||||
layout = client.debug.press_right()
|
||||
screen_texts.append(layout.text_content())
|
||||
screen_texts.append(layout.visible_screen())
|
||||
|
||||
client.debug.press_left()
|
||||
client.debug.press_left()
|
||||
|
Loading…
Reference in New Issue
Block a user