1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-24 13:22:05 +00:00

feat(core): support extra info item in summary menu on Delizia

It will allow confirming the blockhash for Solana transactions.

Also, simplify arguments passing into `new_confirm_output()`.

[no changelog]
This commit is contained in:
Roman Zeyde 2025-02-20 23:55:01 +02:00
parent 97b8409cf4
commit 4efc51d3b4
12 changed files with 85 additions and 50 deletions

View File

@ -63,9 +63,9 @@ static void _librust_qstrs(void) {
MP_QSTR_address_details__derivation_path_colon; MP_QSTR_address_details__derivation_path_colon;
MP_QSTR_address_details__title_receive_address; MP_QSTR_address_details__title_receive_address;
MP_QSTR_address_details__title_receiving_to; MP_QSTR_address_details__title_receiving_to;
MP_QSTR_address_item;
MP_QSTR_address_label; MP_QSTR_address_label;
MP_QSTR_address_qr; MP_QSTR_address_qr;
MP_QSTR_address_title;
MP_QSTR_allow_cancel; MP_QSTR_allow_cancel;
MP_QSTR_amount; MP_QSTR_amount;
MP_QSTR_amount_change; MP_QSTR_amount_change;
@ -232,6 +232,7 @@ static void _librust_qstrs(void) {
MP_QSTR_experimental_mode__only_for_dev; MP_QSTR_experimental_mode__only_for_dev;
MP_QSTR_experimental_mode__title; MP_QSTR_experimental_mode__title;
MP_QSTR_extra; MP_QSTR_extra;
MP_QSTR_extra_item;
MP_QSTR_extra_items; MP_QSTR_extra_items;
MP_QSTR_extra_title; MP_QSTR_extra_title;
MP_QSTR_fee; MP_QSTR_fee;

View File

@ -435,9 +435,22 @@ extern "C" fn new_flow_confirm_output(n_args: usize, args: *const Obj, kwargs: *
let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?; let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?; let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?;
let address: Option<Obj> = kwargs.get(Qstr::MP_QSTR_address)?.try_into_option()?; let address_item = kwargs
let address_title: Option<TString> = .get(Qstr::MP_QSTR_address_item)?
kwargs.get(Qstr::MP_QSTR_address_title)?.try_into_option()?; .try_into_option()?
.map(|item| -> Result<(TString, Obj), crate::error::Error> {
let pair: [Obj; 2] = util::iter_into_array(item)?;
Ok((pair[0].try_into()?, pair[1]))
})
.transpose()?;
let extra_item = kwargs
.get(Qstr::MP_QSTR_extra_item)?
.try_into_option()?
.map(|item| -> Result<(TString, Obj), crate::error::Error> {
let pair: [Obj; 2] = util::iter_into_array(item)?;
Ok((pair[0].try_into()?, pair[1]))
})
.transpose()?;
let summary_items: Option<Obj> = let summary_items: Option<Obj> =
kwargs.get(Qstr::MP_QSTR_summary_items)?.try_into_option()?; kwargs.get(Qstr::MP_QSTR_summary_items)?.try_into_option()?;
let fee_items: Option<Obj> = kwargs.get(Qstr::MP_QSTR_fee_items)?.try_into_option()?; let fee_items: Option<Obj> = kwargs.get(Qstr::MP_QSTR_fee_items)?.try_into_option()?;
@ -464,8 +477,8 @@ extern "C" fn new_flow_confirm_output(n_args: usize, args: *const Obj, kwargs: *
account_path, account_path,
br_code, br_code,
br_name, br_name,
address, address_item,
address_title, extra_item,
summary_items, summary_items,
fee_items, fee_items,
summary_title, summary_title,
@ -1333,8 +1346,8 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// account_path: str | None, /// account_path: str | None,
/// br_code: ButtonRequestType, /// br_code: ButtonRequestType,
/// br_name: str, /// br_name: str,
/// address: str | None, /// address_item: (str, str) | None,
/// address_title: str | None, /// extra_item: (str, str) | None,
/// summary_items: Iterable[tuple[str, str]] | None = None, /// summary_items: Iterable[tuple[str, str]] | None = None,
/// fee_items: Iterable[tuple[str, str]] | None = None, /// fee_items: Iterable[tuple[str, str]] | None = None,
/// summary_title: str | None = None, /// summary_title: str | None = None,

View File

@ -98,7 +98,7 @@ pub struct SwipeFlow {
/// Current state of the flow. /// Current state of the flow.
state: FlowState, state: FlowState,
/// Store of all screens which are part of the flow. /// Store of all screens which are part of the flow.
store: Vec<GcBox<dyn FlowComponentDynTrait>, 12>, store: Vec<GcBox<dyn FlowComponentDynTrait>, 16>,
/// Swipe detector. /// Swipe detector.
swipe: SwipeDetect, swipe: SwipeDetect,
/// Swipe allowed /// Swipe allowed

View File

@ -548,8 +548,8 @@ impl FirmwareUI for UIBolt {
_account_path: Option<TString<'static>>, _account_path: Option<TString<'static>>,
_br_code: u16, _br_code: u16,
_br_name: TString<'static>, _br_name: TString<'static>,
_address: Option<Obj>, _address_item: Option<(TString<'static>, Obj)>,
_address_title: Option<TString<'static>>, _extra_item: Option<(TString<'static>, Obj)>,
_summary_items: Option<Obj>, _summary_items: Option<Obj>,
_fee_items: Option<Obj>, _fee_items: Option<Obj>,
_summary_title: Option<TString<'static>>, _summary_title: Option<TString<'static>>,

View File

@ -687,8 +687,8 @@ impl FirmwareUI for UICaesar {
_account_path: Option<TString<'static>>, _account_path: Option<TString<'static>>,
_br_code: u16, _br_code: u16,
_br_name: TString<'static>, _br_name: TString<'static>,
_address: Option<Obj>, _address_item: Option<(TString<'static>, Obj)>,
_address_title: Option<TString<'static>>, _extra_item: Option<(TString<'static>, Obj)>,
_summary_items: Option<Obj>, _summary_items: Option<Obj>,
_fee_items: Option<Obj>, _fee_items: Option<Obj>,
_summary_title: Option<TString<'static>>, _summary_title: Option<TString<'static>>,

View File

@ -30,6 +30,7 @@ const MENU_ITEM_CANCEL: usize = 0;
const MENU_ITEM_FEE_INFO: usize = 1; const MENU_ITEM_FEE_INFO: usize = 1;
const MENU_ITEM_ADDRESS_INFO: usize = 2; const MENU_ITEM_ADDRESS_INFO: usize = 2;
const MENU_ITEM_ACCOUNT_INFO: usize = 3; const MENU_ITEM_ACCOUNT_INFO: usize = 3;
const MENU_ITEM_EXTRA_INFO: usize = 4;
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum ConfirmOutput { pub enum ConfirmOutput {
@ -123,6 +124,7 @@ pub enum ConfirmOutputWithSummary {
HoldMenu, HoldMenu,
HoldMenuCancel, HoldMenuCancel,
AccountInfo, AccountInfo,
ExtraInfo,
} }
impl FlowController for ConfirmOutputWithSummary { impl FlowController for ConfirmOutputWithSummary {
@ -144,6 +146,7 @@ impl FlowController for ConfirmOutputWithSummary {
(Self::Summary, Direction::Down) => Self::Main.swipe(direction), (Self::Summary, Direction::Down) => Self::Main.swipe(direction),
(Self::SummaryMenu, Direction::Right) => Self::Summary.swipe(direction), (Self::SummaryMenu, Direction::Right) => Self::Summary.swipe(direction),
(Self::SummaryMenuCancel, Direction::Right) => Self::SummaryMenu.swipe(direction), (Self::SummaryMenuCancel, Direction::Right) => Self::SummaryMenu.swipe(direction),
(Self::ExtraInfo, Direction::Right) => Self::SummaryMenu.swipe(direction),
(Self::FeeInfo, Direction::Right) => Self::SummaryMenu.swipe(direction), (Self::FeeInfo, Direction::Right) => Self::SummaryMenu.swipe(direction),
(Self::Hold, Direction::Left) => Self::HoldMenu.swipe(direction), (Self::Hold, Direction::Left) => Self::HoldMenu.swipe(direction),
(Self::Hold, Direction::Down) => Self::Summary.swipe(direction), (Self::Hold, Direction::Down) => Self::Summary.swipe(direction),
@ -162,6 +165,7 @@ impl FlowController for ConfirmOutputWithSummary {
(Self::AccountInfo, FlowMsg::Cancelled) => Self::MainMenu.goto(), (Self::AccountInfo, FlowMsg::Cancelled) => Self::MainMenu.goto(),
(Self::MainMenuCancel, FlowMsg::Cancelled) => Self::MainMenu.goto(), (Self::MainMenuCancel, FlowMsg::Cancelled) => Self::MainMenu.goto(),
(Self::AddressInfo, FlowMsg::Info) => Self::MainMenu.goto(), (Self::AddressInfo, FlowMsg::Info) => Self::MainMenu.goto(),
(Self::ExtraInfo, FlowMsg::Info) => Self::SummaryMenu.goto(),
(Self::Summary, FlowMsg::Info) => Self::SummaryMenu.goto(), (Self::Summary, FlowMsg::Info) => Self::SummaryMenu.goto(),
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => { (Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => {
Self::SummaryMenuCancel.swipe_left() Self::SummaryMenuCancel.swipe_left()
@ -173,6 +177,9 @@ impl FlowController for ConfirmOutputWithSummary {
} }
(Self::HoldMenuCancel, FlowMsg::Cancelled) => Self::HoldMenu.goto(), (Self::HoldMenuCancel, FlowMsg::Cancelled) => Self::HoldMenu.goto(),
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_FEE_INFO)) => Self::FeeInfo.swipe_left(), (Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_FEE_INFO)) => Self::FeeInfo.swipe_left(),
(Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_EXTRA_INFO)) => {
Self::ExtraInfo.swipe_left()
}
(Self::MainMenu, FlowMsg::Choice(MENU_ITEM_ADDRESS_INFO)) => { (Self::MainMenu, FlowMsg::Choice(MENU_ITEM_ADDRESS_INFO)) => {
Self::AddressInfo.swipe_left() Self::AddressInfo.swipe_left()
} }
@ -221,7 +228,7 @@ pub fn new_confirm_output(
br_code: u16, br_code: u16,
confirm_amount: Option<ConfirmValue>, confirm_amount: Option<ConfirmValue>,
confirm_address: Option<ConfirmValue>, confirm_address: Option<ConfirmValue>,
address_title: TString<'static>, confirm_extra: Option<ConfirmValue>,
summary_items_params: Option<ShowInfoParams>, summary_items_params: Option<ShowInfoParams>,
fee_items_params: ShowInfoParams, fee_items_params: ShowInfoParams,
summary_br_name: Option<TString<'static>>, summary_br_name: Option<TString<'static>>,
@ -236,8 +243,8 @@ pub fn new_confirm_output(
// MainMenu // MainMenu
let mut main_menu = VerticalMenu::empty(); let mut main_menu = VerticalMenu::empty();
let mut main_menu_items = Vec::<usize, 3>::new(); let mut main_menu_items = Vec::<usize, 3>::new();
if confirm_address.is_some() { if let Some(ref confirm_address) = confirm_address {
main_menu = main_menu.item(theme::ICON_CHEVRON_RIGHT, address_title); main_menu = main_menu.item(theme::ICON_CHEVRON_RIGHT, confirm_address.title());
unwrap!(main_menu_items.push(MENU_ITEM_ADDRESS_INFO)); unwrap!(main_menu_items.push(MENU_ITEM_ADDRESS_INFO));
} }
if account.is_some() && account_path.is_some() { if account.is_some() && account_path.is_some() {
@ -309,7 +316,11 @@ pub fn new_confirm_output(
// SummaryMenu // SummaryMenu
let mut summary_menu = VerticalMenu::empty(); let mut summary_menu = VerticalMenu::empty();
let mut summary_menu_items = Vec::<usize, 2>::new(); let mut summary_menu_items = Vec::<usize, 3>::new();
if let Some(ref confirm_extra) = confirm_extra {
summary_menu = summary_menu.item(theme::ICON_CHEVRON_RIGHT, confirm_extra.title());
unwrap!(summary_menu_items.push(MENU_ITEM_EXTRA_INFO));
}
if has_fee_info { if has_fee_info {
summary_menu = summary_menu.item( summary_menu = summary_menu.item(
theme::ICON_CHEVRON_RIGHT, theme::ICON_CHEVRON_RIGHT,
@ -364,7 +375,8 @@ pub fn new_confirm_output(
.map(|_| Some(FlowMsg::Cancelled)), .map(|_| Some(FlowMsg::Cancelled)),
)?; )?;
} }
flow.with_page(&ConfirmOutputWithSummary::Summary, content_summary)? flow = flow
.with_page(&ConfirmOutputWithSummary::Summary, content_summary)?
.with_page(&ConfirmOutputWithSummary::SummaryMenu, content_summary_menu)? .with_page(&ConfirmOutputWithSummary::SummaryMenu, content_summary_menu)?
.with_page( .with_page(
&ConfirmOutputWithSummary::SummaryMenuCancel, &ConfirmOutputWithSummary::SummaryMenuCancel,
@ -374,7 +386,12 @@ pub fn new_confirm_output(
.with_page(&ConfirmOutputWithSummary::Hold, content_hold)? .with_page(&ConfirmOutputWithSummary::Hold, content_hold)?
.with_page(&ConfirmOutputWithSummary::HoldMenu, content_hold_menu)? .with_page(&ConfirmOutputWithSummary::HoldMenu, content_hold_menu)?
.with_page(&ConfirmOutputWithSummary::HoldMenuCancel, get_cancel_page())? .with_page(&ConfirmOutputWithSummary::HoldMenuCancel, get_cancel_page())?
.with_page(&ConfirmOutputWithSummary::AccountInfo, account_content)? .with_page(&ConfirmOutputWithSummary::AccountInfo, account_content)?;
if let Some(confirm_extra) = confirm_extra {
let extra_content = confirm_extra.into_layout()?;
flow = flow.with_page(&ConfirmOutputWithSummary::ExtraInfo, extra_content)?
}
flow
} else { } else {
SwipeFlow::new(&ConfirmOutput::Address)? SwipeFlow::new(&ConfirmOutput::Address)?
.with_page(&ConfirmOutput::Address, main_content)? .with_page(&ConfirmOutput::Address, main_content)?

View File

@ -300,6 +300,10 @@ impl ConfirmValue {
self.page_counter, self.page_counter,
) )
} }
pub fn title(&self) -> TString<'static> {
self.title
}
} }
pub struct ShowInfoParams { pub struct ShowInfoParams {

View File

@ -519,8 +519,8 @@ impl FirmwareUI for UIDelizia {
account_path: Option<TString<'static>>, account_path: Option<TString<'static>>,
br_code: u16, br_code: u16,
br_name: TString<'static>, br_name: TString<'static>,
address: Option<Obj>, address_item: Option<(TString<'static>, Obj)>,
address_title: Option<TString<'static>>, extra_item: Option<(TString<'static>, Obj)>,
summary_items: Option<Obj>, summary_items: Option<Obj>,
fee_items: Option<Obj>, fee_items: Option<Obj>,
summary_title: Option<TString<'static>>, summary_title: Option<TString<'static>>,
@ -528,8 +528,6 @@ impl FirmwareUI for UIDelizia {
summary_br_name: Option<TString<'static>>, summary_br_name: Option<TString<'static>>,
cancel_text: Option<TString<'static>>, cancel_text: Option<TString<'static>>,
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let address_title = address_title.unwrap_or(TR::words__address.into());
let confirm_main = let confirm_main =
ConfirmValue::new(title.unwrap_or(TString::empty()), message, description) ConfirmValue::new(title.unwrap_or(TString::empty()), message, description)
.with_subtitle(subtitle) .with_subtitle(subtitle)
@ -549,7 +547,7 @@ impl FirmwareUI for UIDelizia {
.with_swipe_down() .with_swipe_down()
}); });
let confirm_value = address.map(|address| { let confirm_address = address_item.map(|(address_title, address)| {
ConfirmValue::new(address_title, address, None) ConfirmValue::new(address_title, address, None)
.with_cancel_button() .with_cancel_button()
.with_chunkify(true) .with_chunkify(true)
@ -557,6 +555,14 @@ impl FirmwareUI for UIDelizia {
.with_swipe_right() .with_swipe_right()
}); });
let confirm_extra = extra_item.map(|(extra_title, extra)| {
ConfirmValue::new(extra_title, extra, None)
.with_cancel_button()
.with_chunkify(true)
.with_text_mono(true)
.with_swipe_right()
});
let mut fee_items_params = let mut fee_items_params =
ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button(); ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button();
if fee_items.is_some() { if fee_items.is_some() {
@ -589,8 +595,8 @@ impl FirmwareUI for UIDelizia {
br_name, br_name,
br_code, br_code,
confirm_amount, confirm_amount,
confirm_value, confirm_address,
address_title, confirm_extra,
summary_items_params, summary_items_params,
fee_items_params, fee_items_params,
summary_br_name, summary_br_name,

View File

@ -169,8 +169,8 @@ pub trait FirmwareUI {
account_path: Option<TString<'static>>, account_path: Option<TString<'static>>,
br_code: u16, br_code: u16,
br_name: TString<'static>, br_name: TString<'static>,
address: Option<Obj>, // TODO: replace Obj address_item: Option<(TString<'static>, Obj)>,
address_title: Option<TString<'static>>, extra_item: Option<(TString<'static>, Obj)>,
summary_items: Option<Obj>, // TODO: replace Obj summary_items: Option<Obj>, // TODO: replace Obj
fee_items: Option<Obj>, // TODO: replace Obj fee_items: Option<Obj>, // TODO: replace Obj
summary_title: Option<TString<'static>>, summary_title: Option<TString<'static>>,

View File

@ -319,8 +319,8 @@ def flow_confirm_output(
account_path: str | None, account_path: str | None,
br_code: ButtonRequestType, br_code: ButtonRequestType,
br_name: str, br_name: str,
address: str | None, address_item: (str, str) | None,
address_title: str | None, extra_item: (str, str) | None,
summary_items: Iterable[tuple[str, str]] | None = None, summary_items: Iterable[tuple[str, str]] | None = None,
fee_items: Iterable[tuple[str, str]] | None = None, fee_items: Iterable[tuple[str, str]] | None = None,
summary_title: str | None = None, summary_title: str | None = None,

View File

@ -49,10 +49,6 @@ def _get_address_reference_props(
) )
def _blockhash_item(blockhash: bytes) -> tuple[str, str]:
return (f"{TR.words__blockhash}:", base58.encode(blockhash))
async def confirm_instruction( async def confirm_instruction(
instruction: Instruction, instruction: Instruction,
instructions_count: int, instructions_count: int,
@ -352,7 +348,7 @@ async def confirm_custom_transaction(
fee_title=f"{TR.solana__expected_fee}:", fee_title=f"{TR.solana__expected_fee}:",
items=( items=(
(f"{TR.words__account}:", _format_path(signer_path)), (f"{TR.words__account}:", _format_path(signer_path)),
_blockhash_item(blockhash), (f"{TR.words__blockhash}:", base58.encode(blockhash)),
), ),
) )
@ -426,7 +422,7 @@ async def confirm_stake_transaction(
), ),
fee_item=_fee_summary(fee), fee_item=_fee_summary(fee),
fee_details=_fee_details(fee), fee_details=_fee_details(fee),
blockhash_item=_blockhash_item(blockhash), blockhash_item=(TR.words__blockhash, base58.encode(blockhash)),
br_name="confirm_stake_transaction", br_name="confirm_stake_transaction",
) )
@ -452,7 +448,7 @@ async def confirm_unstake_transaction(
amount_item=("", ""), amount_item=("", ""),
fee_item=_fee_summary(fee), fee_item=_fee_summary(fee),
fee_details=_fee_details(fee), fee_details=_fee_details(fee),
blockhash_item=_blockhash_item(blockhash), blockhash_item=(TR.words__blockhash, base58.encode(blockhash)),
br_name="confirm_unstake_transaction", br_name="confirm_unstake_transaction",
) )
@ -481,7 +477,7 @@ async def confirm_claim_transaction(
), ),
fee_item=_fee_summary(fee), fee_item=_fee_summary(fee),
fee_details=_fee_details(fee), fee_details=_fee_details(fee),
blockhash_item=_blockhash_item(blockhash), blockhash_item=(TR.words__blockhash, base58.encode(blockhash)),
br_name="confirm_claim_transaction", br_name="confirm_claim_transaction",
) )
@ -497,6 +493,6 @@ async def confirm_transaction(
fee_title=fee_title, fee_title=fee_title,
items=( items=(
(f"{TR.words__account}:", _format_path(signer_path)), (f"{TR.words__account}:", _format_path(signer_path)),
_blockhash_item(blockhash), (f"{TR.words__blockhash}:", base58.encode(blockhash)),
), ),
) )

View File

@ -398,8 +398,8 @@ async def confirm_output(
text_mono=True, text_mono=True,
account=source_account, account=source_account,
account_path=source_account_path, account_path=source_account_path,
address=None, address_item=None,
address_title=None, extra_item=None,
br_code=br_code, br_code=br_code,
br_name="confirm_output", br_name="confirm_output",
summary_items=None, summary_items=None,
@ -778,8 +778,8 @@ if not utils.BITCOIN_ONLY:
text_mono=True, text_mono=True,
account=account, account=account,
account_path=account_path, account_path=account_path,
address=None, address_item=None,
address_title=None, extra_item=None,
br_code=ButtonRequestType.SignTx, br_code=ButtonRequestType.SignTx,
br_name="confirm_output", br_name="confirm_output",
summary_items=( summary_items=(
@ -830,8 +830,8 @@ if not utils.BITCOIN_ONLY:
account_path=account_path, account_path=account_path,
br_code=br_code, br_code=br_code,
br_name=br_name, br_name=br_name,
address=address, address_item=(address_title, address),
address_title=address_title, extra_item=None,
summary_items=summary_items, summary_items=summary_items,
fee_items=info_items, fee_items=info_items,
summary_title=verb, summary_title=verb,
@ -879,8 +879,6 @@ if not utils.BITCOIN_ONLY:
blockhash_item: tuple[str, str], blockhash_item: tuple[str, str],
br_name: str, br_name: str,
) -> None: ) -> None:
(address_title, address) = stake_item
summary_items = (amount_item, fee_item)
description = message description = message
if vote_account: if vote_account:
description = f"{message}\n{TR.solana__stake_provider}:" description = f"{message}\n{TR.solana__stake_provider}:"
@ -898,11 +896,11 @@ if not utils.BITCOIN_ONLY:
account_path=account_path, account_path=account_path,
br_code=ButtonRequestType.SignTx, br_code=ButtonRequestType.SignTx,
br_name=br_name, br_name=br_name,
address=address, address_item=stake_item,
address_title=address_title, extra_item=blockhash_item,
fee_items=fee_details, fee_items=fee_details,
summary_title=title, summary_title=title,
summary_items=summary_items, summary_items=(amount_item, fee_item),
summary_br_name="confirm_solana_staking_tx_total", summary_br_name="confirm_solana_staking_tx_total",
summary_br_code=ButtonRequestType.SignTx, summary_br_code=ButtonRequestType.SignTx,
cancel_text=TR.buttons__cancel, cancel_text=TR.buttons__cancel,