1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-26 01:18:28 +00:00

refactor(core): move confirm_fido to UiFeatures

- still uses Gc<List> for now
This commit is contained in:
obrusvit 2024-11-07 23:52:07 +01:00
parent 0bc5dfac6a
commit a7a12cf898
14 changed files with 273 additions and 310 deletions

View File

@ -1,6 +1,8 @@
use crate::{
io::BinaryData,
micropython::{
gc::Gc,
list::List,
macros::{obj_fn_1, obj_fn_kw, obj_module},
map::Map,
module::Module,
@ -92,6 +94,19 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_fido(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 app_name: TString = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?;
let icon: Option<TString> = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?;
let accounts: Gc<List> = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?;
let layout = ModelUI::confirm_fido(title, app_name, icon, accounts)?;
Ok(LayoutObj::new_root(layout)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
// TODO: there was `no_mangle` attribute in TT, should we apply it?
extern "C" fn new_confirm_firmware_update(
n_args: usize,
@ -676,6 +691,19 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """Confirm coinjoin authorization."""
Qstr::MP_QSTR_confirm_coinjoin => obj_fn_kw!(0, new_confirm_coinjoin).as_obj(),
/// def confirm_fido(
/// *,
/// title: str,
/// app_name: str,
/// icon_name: str | None,
/// accounts: list[str | None],
/// ) -> LayoutObj[int | UiResult]:
/// """FIDO confirmation.
///
/// Returns page index in case of confirmation and CANCELLED otherwise.
/// """
Qstr::MP_QSTR_confirm_fido => obj_fn_kw!(0, new_confirm_fido).as_obj(),
/// def confirm_firmware_update(
/// *,
/// description: str,

View File

@ -1,6 +1,6 @@
use crate::{
error,
micropython::{gc::Gc, list::List, map::Map, obj::Obj, qstr::Qstr, util},
micropython::{gc::Gc, list::List},
strutil::TString,
translations::TR,
ui::{
@ -14,7 +14,6 @@ use crate::{
FlowController, FlowMsg, SwipeFlow, SwipePage,
},
geometry::Direction,
layout::obj::LayoutObj,
},
};
@ -39,6 +38,7 @@ pub enum ConfirmFido {
static CRED_SELECTED: AtomicUsize = AtomicUsize::new(0);
static SINGLE_CRED: AtomicBool = AtomicBool::new(false);
const EXTRA_PADDING: i16 = 6;
impl FlowController for ConfirmFido {
#[inline]
@ -50,7 +50,7 @@ impl FlowController for ConfirmFido {
match (self, direction) {
(Self::Intro, Direction::Left) => Self::Menu.swipe(direction),
(Self::Menu, Direction::Right) => {
if Self::single_cred() {
if single_cred() {
Self::Details.swipe_right()
} else {
Self::Intro.swipe_right()
@ -69,7 +69,7 @@ impl FlowController for ConfirmFido {
(_, FlowMsg::Info) => Self::Menu.goto(),
(Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled),
(Self::Menu, FlowMsg::Cancelled) => {
if Self::single_cred() {
if single_cred() {
Self::Details.swipe_right()
} else {
Self::Intro.swipe_right()
@ -88,11 +88,6 @@ impl FlowController for ConfirmFido {
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmFido::new_obj) }
}
fn footer_update_fn(
content: &SwipeContent<SwipePage<PagedVerticalMenu<impl Fn(usize) -> TString<'static>>>>,
ctx: &mut EventCtx,
@ -103,18 +98,16 @@ fn footer_update_fn(
footer.update_page_counter(ctx, current_page, Some(total_pages));
}
impl ConfirmFido {
const EXTRA_PADDING: i16 = 6;
fn single_cred() -> bool {
SINGLE_CRED.load(Ordering::Relaxed)
}
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let app_name: TString = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?;
let icon_name: Option<TString> = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?;
let accounts: Gc<List> = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?;
pub fn new_confirm_fido(
title: TString<'static>,
app_name: TString<'static>,
icon_name: Option<TString<'static>>,
accounts: Gc<List>,
) -> Result<SwipeFlow, error::Error> {
let num_accounts = accounts.len();
SINGLE_CRED.store(num_accounts <= 1, Ordering::Relaxed);
CRED_SELECTED.store(0, Ordering::Relaxed);
@ -178,7 +171,7 @@ impl ConfirmFido {
.with_footer(TR::instructions__swipe_up.into(), Some(title))
.with_swipe(Direction::Up, SwipeSettings::default())
.with_swipe(Direction::Right, SwipeSettings::immediate());
let content_details = if Self::single_cred() {
let content_details = if single_cred() {
content_details.with_menu_button()
} else {
content_details.with_cancel_button()
@ -210,17 +203,15 @@ impl ConfirmFido {
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
});
let initial_page = if Self::single_cred() {
let initial_page = if single_cred() {
&ConfirmFido::Details
} else {
&ConfirmFido::Intro
};
let res = SwipeFlow::new(initial_page)?
SwipeFlow::new(initial_page)?
.with_page(&ConfirmFido::Intro, content_intro)?
.with_page(&ConfirmFido::ChooseCredential, content_choose_credential)?
.with_page(&ConfirmFido::Details, content_details)?
.with_page(&ConfirmFido::Tap, content_tap)?
.with_page(&ConfirmFido::Menu, content_menu)?;
Ok(LayoutObj::new_root(res)?.into())
}
.with_page(&ConfirmFido::Menu, content_menu)
}

View File

@ -785,13 +785,6 @@ extern "C" fn new_prompt_backup() -> Obj {
unsafe { util::try_or_raise(block) }
}
extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
#[cfg(feature = "universal_fw")]
return flow::confirm_fido::new_confirm_fido(n_args, args, kwargs);
#[cfg(not(feature = "universal_fw"))]
panic!();
}
extern "C" fn new_warning_hi_prio(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()?;
@ -914,19 +907,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Transaction summary. Always hold to confirm."""
Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(),
/// def confirm_fido(
/// *,
/// title: str,
/// app_name: str,
/// icon_name: str | None,
/// accounts: list[str | None],
/// ) -> LayoutObj[int | UiResult]:
/// """FIDO confirmation.
///
/// Returns page index in case of confirmation and CANCELLED otherwise.
/// """
Qstr::MP_QSTR_confirm_fido => obj_fn_kw!(0, new_confirm_fido).as_obj(),
/// def confirm_with_info(
/// *,
/// title: str,

View File

@ -3,7 +3,7 @@ use core::cmp::Ordering;
use crate::{
error::{value_error, Error},
io::BinaryData,
micropython::{gc::Gc, util},
micropython::{gc::Gc, list::List, util},
strutil::TString,
translations::TR,
ui::{
@ -125,6 +125,22 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
Ok(flow)
}
fn confirm_fido(
title: TString<'static>,
app_name: TString<'static>,
icon: Option<TString<'static>>,
accounts: Gc<List>,
) -> Result<impl LayoutMaybeTrace, Error> {
#[cfg(feature = "universal_fw")]
return Ok(flow::confirm_fido::new_confirm_fido(
title, app_name, icon, accounts,
)?);
#[cfg(not(feature = "universal_fw"))]
Err::<RootComponent<Empty, ModelMercuryFeatures>, Error>(Error::ValueError(
c"confirm_fido not used in bitcoin-only firmware",
))
}
fn confirm_firmware_update(
description: TString<'static>,
fingerprint: TString<'static>,

View File

@ -778,70 +778,6 @@ extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs:
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_fido(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 app_name: TString = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?;
let accounts: Gc<List> = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?;
// Cache the page count so that we can move `accounts` into the closure.
let page_count = accounts.len();
// Closure to lazy-load the information on given page index.
// Done like this to allow arbitrarily many pages without
// the need of any allocation here in Rust.
let get_page = move |page_index| {
let account_obj = unwrap!(accounts.get(page_index));
let account = TString::try_from(account_obj).unwrap_or_else(|_| TString::empty());
let (btn_layout, btn_actions) = if page_count == 1 {
// There is only one page
(
ButtonLayout::cancel_none_text(TR::buttons__confirm.into()),
ButtonActions::cancel_none_confirm(),
)
} else if page_index == 0 {
// First page
(
ButtonLayout::cancel_armed_arrow(TR::buttons__select.into()),
ButtonActions::cancel_confirm_next(),
)
} else if page_index == page_count - 1 {
// Last page
(
ButtonLayout::arrow_armed_none(TR::buttons__select.into()),
ButtonActions::prev_confirm_none(),
)
} else {
// Page in the middle
(
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
ButtonActions::prev_confirm_next(),
)
};
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
.newline()
.text_normal(app_name)
.newline()
.text_bold(account);
let formatted = FormattedText::new(ops);
Page::new(btn_layout, btn_actions, formatted)
};
let pages = FlowPages::new(get_page, page_count);
// Returning the page index in case of confirmation.
let obj = LayoutObj::new(
Flow::new(pages)
.with_common_title(title)
.with_return_confirmed_index(),
)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_with_info(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()?;
@ -1050,19 +986,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Confirm details about altcoin transaction."""
Qstr::MP_QSTR_altcoin_tx_summary => obj_fn_kw!(0, new_altcoin_tx_summary).as_obj(),
/// def confirm_fido(
/// *,
/// title: str,
/// app_name: str,
/// icon_name: str | None, # unused on TR
/// accounts: list[str | None],
/// ) -> LayoutObj[int | UiResult]:
/// """FIDO confirmation.
///
/// Returns page index in case of confirmation and CANCELLED otherwise.
/// """
Qstr::MP_QSTR_confirm_fido => obj_fn_kw!(0, new_confirm_fido).as_obj(),
/// def multiple_pages_texts(
/// *,
/// title: str,

View File

@ -4,7 +4,7 @@ use crate::{
error::Error,
io::BinaryData,
maybe_trace::MaybeTrace,
micropython::gc::Gc,
micropython::{gc::Gc, list::List},
strutil::TString,
translations::TR,
ui::{
@ -113,6 +113,68 @@ impl UIFeaturesFirmware for ModelTRFeatures {
)
}
fn confirm_fido(
title: TString<'static>,
app_name: TString<'static>,
icon: Option<TString<'static>>,
accounts: Gc<List>,
) -> Result<impl LayoutMaybeTrace, Error> {
// Cache the page count so that we can move `accounts` into the closure.
let page_count = accounts.len();
// Closure to lazy-load the information on given page index.
// Done like this to allow arbitrarily many pages without
// the need of any allocation here in Rust.
let get_page = move |page_index| {
let account_obj = unwrap!(accounts.get(page_index));
let account = TString::try_from(account_obj).unwrap_or_else(|_| TString::empty());
let (btn_layout, btn_actions) = if page_count == 1 {
// There is only one page
(
ButtonLayout::cancel_none_text(TR::buttons__confirm.into()),
ButtonActions::cancel_none_confirm(),
)
} else if page_index == 0 {
// First page
(
ButtonLayout::cancel_armed_arrow(TR::buttons__select.into()),
ButtonActions::cancel_confirm_next(),
)
} else if page_index == page_count - 1 {
// Last page
(
ButtonLayout::arrow_armed_none(TR::buttons__select.into()),
ButtonActions::prev_confirm_none(),
)
} else {
// Page in the middle
(
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
ButtonActions::prev_confirm_next(),
)
};
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
.newline()
.text_normal(app_name)
.newline()
.text_bold(account);
let formatted = FormattedText::new(ops);
Page::new(btn_layout, btn_actions, formatted)
};
let pages = FlowPages::new(get_page, page_count);
// Returning the page index in case of confirmation.
let obj = RootComponent::new(
Flow::new(pages)
.with_common_title(title)
.with_return_confirmed_index(),
);
Ok(obj)
}
fn confirm_firmware_update(
description: TString<'static>,
fingerprint: TString<'static>,

View File

@ -702,37 +702,6 @@ extern "C" fn new_confirm_total(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_fido(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 app_name: TString = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?;
let icon: Option<TString> = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?;
let accounts: Gc<List> = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?;
// Cache the page count so that we can move `accounts` into the closure.
let page_count = accounts.len();
// Closure to lazy-load the information on given page index.
// Done like this to allow arbitrarily many pages without
// the need of any allocation here in Rust.
let get_page = move |page_index| {
let account = unwrap!(accounts.get(page_index));
account.try_into().unwrap_or_else(|_| "".into())
};
let controls = Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL),
Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()),
true,
);
let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls);
let obj = LayoutObj::new(Frame::centered(theme::label_title(), title, fido_page))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_with_info(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()?;
@ -929,19 +898,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Transaction summary. Always hold to confirm."""
Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(),
/// def confirm_fido(
/// *,
/// title: str,
/// app_name: str,
/// icon_name: str | None,
/// accounts: list[str | None],
/// ) -> LayoutObj[int | UiResult]:
/// """FIDO confirmation.
///
/// Returns page index in case of confirmation and CANCELLED otherwise.
/// """
Qstr::MP_QSTR_confirm_fido => obj_fn_kw!(0, new_confirm_fido).as_obj(),
/// def confirm_with_info(
/// *,
/// title: str,

View File

@ -3,7 +3,7 @@ use core::cmp::Ordering;
use crate::{
error::{value_error, Error},
io::BinaryData,
micropython::gc::Gc,
micropython::{gc::Gc, list::List},
strutil::TString,
translations::TR,
ui::{
@ -27,8 +27,8 @@ use crate::{
use super::{
component::{
check_homescreen_format, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet,
CancelConfirmMsg, CoinJoinProgress, Dialog, Frame, Homescreen, IconDialog, Lockscreen,
MnemonicKeyboard, NumberInputDialog, PassphraseKeyboard, PinKeyboard, Progress,
CancelConfirmMsg, CoinJoinProgress, Dialog, FidoConfirm, Frame, Homescreen, IconDialog,
Lockscreen, MnemonicKeyboard, NumberInputDialog, PassphraseKeyboard, PinKeyboard, Progress,
SelectWordCount, SetBrightnessDialog, Slip39Input,
},
theme, ModelTTFeatures,
@ -118,6 +118,34 @@ impl UIFeaturesFirmware for ModelTTFeatures {
Ok(layout)
}
fn confirm_fido(
title: TString<'static>,
app_name: TString<'static>,
icon: Option<TString<'static>>,
accounts: Gc<List>,
) -> Result<impl LayoutMaybeTrace, Error> {
// Cache the page count so that we can move `accounts` into the closure.
let page_count = accounts.len();
// Closure to lazy-load the information on given page index.
// Done like this to allow arbitrarily many pages without
// the need of any allocation here in Rust.
let get_page = move |page_index| {
let account = unwrap!(accounts.get(page_index));
account.try_into().unwrap_or_else(|_| "".into())
};
let controls = Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL),
Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()),
true,
);
let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls);
let layout = RootComponent::new(Frame::centered(theme::label_title(), title, fido_page));
Ok(layout)
}
fn confirm_firmware_update(
description: TString<'static>,
fingerprint: TString<'static>,

View File

@ -1,7 +1,7 @@
use crate::{
error::Error,
io::BinaryData,
micropython::{gc::Gc, obj::Obj},
micropython::{gc::Gc, list::List, obj::Obj},
strutil::TString,
};
@ -35,6 +35,13 @@ pub trait UIFeaturesFirmware {
max_feerate: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error>;
fn confirm_fido(
title: TString<'static>,
app_name: TString<'static>,
icon: Option<TString<'static>>,
accounts: Gc<List>, // TODO: replace Gc<List>
) -> Result<impl LayoutMaybeTrace, Error>;
fn confirm_firmware_update(
description: TString<'static>,
fingerprint: TString<'static>,

View File

@ -108,19 +108,6 @@ def confirm_total(
"""Transaction summary. Always hold to confirm."""
# rust/src/ui/model_mercury/layout.rs
def confirm_fido(
*,
title: str,
app_name: str,
icon_name: str | None,
accounts: list[str | None],
) -> LayoutObj[int | UiResult]:
"""FIDO confirmation.
Returns page index in case of confirmation and CANCELLED otherwise.
"""
# rust/src/ui/model_mercury/layout.rs
def confirm_with_info(
*,
@ -367,19 +354,6 @@ def altcoin_tx_summary(
"""Confirm details about altcoin transaction."""
# rust/src/ui/model_tr/layout.rs
def confirm_fido(
*,
title: str,
app_name: str,
icon_name: str | None, # unused on TR
accounts: list[str | None],
) -> LayoutObj[int | UiResult]:
"""FIDO confirmation.
Returns page index in case of confirmation and CANCELLED otherwise.
"""
# rust/src/ui/model_tr/layout.rs
def multiple_pages_texts(
*,
@ -534,19 +508,6 @@ def confirm_total(
"""Transaction summary. Always hold to confirm."""
# rust/src/ui/model_tt/layout.rs
def confirm_fido(
*,
title: str,
app_name: str,
icon_name: str | None,
accounts: list[str | None],
) -> LayoutObj[int | UiResult]:
"""FIDO confirmation.
Returns page index in case of confirmation and CANCELLED otherwise.
"""
# rust/src/ui/model_tt/layout.rs
def confirm_with_info(
*,

View File

@ -105,6 +105,19 @@ def confirm_coinjoin(
"""Confirm coinjoin authorization."""
# rust/src/ui/api/firmware_upy.rs
def confirm_fido(
*,
title: str,
app_name: str,
icon_name: str | None,
accounts: list[str | None],
) -> LayoutObj[int | UiResult]:
"""FIDO confirmation.
Returns page index in case of confirmation and CANCELLED otherwise.
"""
# rust/src/ui/api/firmware_upy.rs
def confirm_firmware_update(
*,

View File

@ -1,4 +1,3 @@
import trezorui2
import trezorui_api
from trezor import ui
from trezor.enums import ButtonRequestType
@ -13,7 +12,7 @@ async def confirm_fido(
accounts: list[str | None],
) -> int:
"""Webauthn confirmation for one or more credentials."""
confirm = trezorui2.confirm_fido(
confirm = trezorui_api.confirm_fido(
title=header,
app_name=app_name,
icon_name=icon_name,

View File

@ -1,4 +1,3 @@
import trezorui2
import trezorui_api
from trezor import ui
from trezor.enums import ButtonRequestType
@ -9,13 +8,14 @@ from ..common import interact
async def confirm_fido(
header: str,
app_name: str,
icon_name: str | None,
_icon_name: str | None, # unused on TR
accounts: list[str | None],
) -> int:
"""Webauthn confirmation for one or more credentials."""
confirm = trezorui2.confirm_fido( # type: ignore [Argument missing for parameter "icon_name"]
confirm = trezorui_api.confirm_fido( # type: ignore [Argument missing for parameter "icon_name"]
title=header,
app_name=app_name,
icon_name=None,
accounts=accounts,
)
result = await interact(confirm, "confirm_fido", ButtonRequestType.Other)

View File

@ -1,4 +1,3 @@
import trezorui2
import trezorui_api
from trezor import ui
from trezor.enums import ButtonRequestType
@ -13,7 +12,7 @@ async def confirm_fido(
accounts: list[str | None],
) -> int:
"""Webauthn confirmation for one or more credentials."""
confirm = trezorui2.confirm_fido(
confirm = trezorui_api.confirm_fido(
title=header,
app_name=app_name,
icon_name=icon_name,