1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-04 13:38:28 +00:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Vít Obrusník
6c84fdf56f
Merge 5c2bacd39a into ef02c4de5d 2024-12-03 11:02:20 +01:00
matejcik
ef02c4de5d feat(core): introduce storage insecure mode
reduces the number of PIN iterations and avoids erasing the other
storage bank -- if a test ever overruns, it will probably RSOD out, but
that's unlikely to happen
2024-12-03 11:02:17 +01:00
matejcik
835f7087c6 build(core): correctly propagate DISABLE_OPTIGA to kernel 2024-12-03 11:02:17 +01:00
matejcik
331e07b1e0 feat(core): disable animations in debug firmware by default
This makes it possible to run HW tests on the T3T1 where animations mess
things up. It also speeds up HW tests on other models slightly.

export TREZOR_DISABLE_ANIMATION=0 to build a debug firmware with
animations enabled
2024-12-03 11:02:17 +01:00
obrusvit
5c2bacd39a fixup! refactor(core): model_r confirm_summary 2024-12-02 19:20:42 +01:00
obrusvit
acee4b605e fixup! refactor(core): model_t confirm_summary 2024-12-02 19:20:33 +01:00
obrusvit
0dcc125c4d fixup! refactor(core): mercury confirm_summary 2024-12-02 19:20:09 +01:00
obrusvit
61448aad34 fixup! refactor(core): model_r confirm_summary 2024-12-02 18:28:00 +01:00
obrusvit
b1850b904a fixup! refactor(core): mercury confirm_summary 2024-12-02 14:55:59 +01:00
obrusvit
f9c897d2df fixup! refactor(core): model_r confirm_summary 2024-12-02 14:55:59 +01:00
obrusvit
e7c5bc2632 fixup! refactor(core): model_t confirm_summary 2024-12-02 14:46:23 +01:00
obrusvit
29cf6d3557 fixup! refactor(core): mercury confirm_summary 2024-12-02 13:48:12 +01:00
obrusvit
ff7f6ea446 refactor(core): mercury confirm_summary
- old confirm_total removed
- flow_confirm_summary refactored to confirm_summary

[no changelog]
2024-11-30 23:50:53 +01:00
obrusvit
2b559fc22f refactor(core): model_r confirm_summary
- altcoin_tx_summary removed and replaced with confirm_summary

[no changelog]
2024-11-30 23:50:53 +01:00
obrusvit
c08e495c9b refactor(core): model_t confirm_summary
- model_t confirm_total refactored to confirm_summary
- parameter set changed to pave the way for unification across models

[no changelog]
2024-11-30 23:50:53 +01:00
24 changed files with 593 additions and 465 deletions

View File

@ -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)" \

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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};

View File

@ -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(),
}
}

View File

@ -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)
}

View File

@ -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."""

View File

@ -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(

View File

@ -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."""

View File

@ -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
}

View File

@ -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(
/// *,

View File

@ -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,

View File

@ -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

View File

@ -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."""

View File

@ -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`

View File

@ -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,

View File

@ -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:

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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()