1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-05 05:15:27 +00:00

WIP - FIDO and various fixes

This commit is contained in:
grdddj 2023-01-03 13:16:22 +01:00
parent 416f76a033
commit d4dce3d67f
39 changed files with 528 additions and 465 deletions

View File

@ -133,22 +133,6 @@ core fw btconly production build:
- firmware-T2T1-btconly-production-*.*.*-$CI_COMMIT_SHORT_SHA.bin - firmware-T2T1-btconly-production-*.*.*-$CI_COMMIT_SHORT_SHA.bin
expire_in: 1 week expire_in: 1 week
core fw btconly t1 build:
stage: build
<<: *gitlab_caching
needs: []
variables:
BITCOIN_ONLY: "1"
TREZOR_MODEL: "1"
script:
- nix-shell --run "poetry run make -C core build_firmware"
- cp core/build/firmware/firmware.bin firmware-T1B1-btconly-t1-$CORE_VERSION-$CI_COMMIT_SHORT_SHA.bin
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA"
paths:
- firmware-T1B1-btconly-t1-*.*.*-$CI_COMMIT_SHORT_SHA.bin
expire_in: 1 week
core fw R btconly debug build: core fw R btconly debug build:
stage: build stage: build
<<: *gitlab_caching <<: *gitlab_caching
@ -370,22 +354,6 @@ core unix frozen debug build arm:
tags: tags:
- docker_darwin_arm - docker_darwin_arm
core unix frozen btconly debug t1 build:
stage: build
<<: *gitlab_caching
needs: []
variables:
BITCOIN_ONLY: "1"
TREZOR_MODEL: "1"
script:
- nix-shell --run "poetry run make -C core build_unix_frozen"
- mv core/build/unix/trezor-emu-core core/build/unix/trezor-emu-core-bitcoinonly
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA"
paths:
- core/build/unix # most of it needed by test_rust
expire_in: 1 week
core macos frozen regular build: core macos frozen regular build:
stage: build stage: build
<<: *gitlab_caching <<: *gitlab_caching

View File

@ -120,6 +120,7 @@ static void _librust_qstrs(void) {
MP_QSTR_time_ms; MP_QSTR_time_ms;
MP_QSTR_app_name; MP_QSTR_app_name;
MP_QSTR_icon_name; MP_QSTR_icon_name;
MP_QSTR_account;
MP_QSTR_accounts; MP_QSTR_accounts;
MP_QSTR_indeterminate; MP_QSTR_indeterminate;
MP_QSTR_notification; MP_QSTR_notification;

View File

@ -9,8 +9,6 @@ use crate::{
}, },
}; };
use heapless::String;
use super::theme; use super::theme;
const HALF_SCREEN_BUTTON_WIDTH: i16 = constant::WIDTH / 2 - 1; const HALF_SCREEN_BUTTON_WIDTH: i16 = constant::WIDTH / 2 - 1;
@ -262,18 +260,6 @@ impl Component for Button {
} }
} }
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Button {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Button");
match &self.content {
ButtonContent::Text(text) => t.field("text", text),
ButtonContent::Icon(icon) => t.field("icon", icon),
}
t.close();
}
}
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
enum State { enum State {
Released, Released,
@ -453,7 +439,7 @@ impl ButtonDetails {
self self
} }
/// Default duration of the hold-to-confirm. /// Default duration of the hold-to-confirm - 1 second.
pub fn with_default_duration(mut self) -> Self { pub fn with_default_duration(mut self) -> Self {
self.duration = Some(Duration::from_millis(1000)); self.duration = Some(Duration::from_millis(1000));
self self
@ -511,11 +497,11 @@ impl ButtonLayout {
/// Default button layout for all three buttons - icons. /// Default button layout for all three buttons - icons.
pub fn default_three_icons() -> Self { pub fn default_three_icons() -> Self {
Self::three_icons_middle_text("SELECT".into()) Self::arrow_armed_icon("SELECT".into())
} }
/// Special middle text for default icon layout. /// Special middle text for default icon layout.
pub fn three_icons_middle_text(middle_text: StrBuffer) -> Self { pub fn arrow_armed_icon(middle_text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::left_arrow_icon()), Some(ButtonDetails::left_arrow_icon()),
Some(ButtonDetails::armed_text(middle_text)), Some(ButtonDetails::armed_text(middle_text)),
@ -523,8 +509,35 @@ impl ButtonLayout {
) )
} }
/// Left cancel, armed text and next right arrow.
pub fn cancel_armed_arrow(middle_text: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text(middle_text)),
Some(ButtonDetails::right_arrow_icon()),
)
}
/// Left cancel, armed text and right text.
pub fn cancel_armed_text(middle_text: StrBuffer, right_text: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text(middle_text)),
Some(ButtonDetails::text(right_text)),
)
}
/// Left back arrow and middle armed text.
pub fn arrow_armed_none(middle_text: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
Some(ButtonDetails::armed_text(middle_text)),
None,
)
}
/// Left and right texts. /// Left and right texts.
pub fn left_right_text(text_left: StrBuffer, text_right: StrBuffer) -> Self { pub fn text_none_text(text_left: StrBuffer, text_right: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::text(text_left)), Some(ButtonDetails::text(text_left)),
None, None,
@ -533,12 +546,12 @@ impl ButtonLayout {
} }
/// Only right text. /// Only right text.
pub fn only_right_text(text_right: StrBuffer) -> Self { pub fn none_none_text(text_right: StrBuffer) -> Self {
Self::new(None, None, Some(ButtonDetails::text(text_right))) Self::new(None, None, Some(ButtonDetails::text(text_right)))
} }
/// Left and right arrow icons for navigation. /// Left and right arrow icons for navigation.
pub fn left_right_arrows() -> Self { pub fn arrow_none_arrow() -> Self {
Self::new( Self::new(
Some(ButtonDetails::left_arrow_icon()), Some(ButtonDetails::left_arrow_icon()),
None, None,
@ -546,8 +559,26 @@ impl ButtonLayout {
) )
} }
/// Left arrow and right text.
pub fn arrow_none_text(text_right: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::text(text_right)),
)
}
/// Up arrow left and right text.
pub fn up_arrow_none_text(text_right: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::up_arrow_icon()),
None,
Some(ButtonDetails::text(text_right)),
)
}
/// Cancel cross on left and right arrow. /// Cancel cross on left and right arrow.
pub fn cancel_and_arrow() -> Self { pub fn cancel_none_arrow() -> Self {
Self::new( Self::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
@ -556,7 +587,7 @@ impl ButtonLayout {
} }
/// Cancel cross on left and right arrow facing down. /// Cancel cross on left and right arrow facing down.
pub fn cancel_and_arrow_down() -> Self { pub fn cancel_none_arrow_wide() -> Self {
Self::new( Self::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
@ -565,7 +596,7 @@ impl ButtonLayout {
} }
/// Cancel cross on left and text on the right. /// Cancel cross on left and text on the right.
pub fn cancel_and_text(text: StrBuffer) -> Self { pub fn cancel_none_text(text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
@ -574,39 +605,30 @@ impl ButtonLayout {
} }
/// Cancel cross on left and hold-to-confirm text on the right. /// Cancel cross on left and hold-to-confirm text on the right.
pub fn cancel_and_htc_text(text: StrBuffer, duration: Duration) -> Self { pub fn cancel_none_htc(text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
Some(ButtonDetails::text(text).with_duration(duration)), Some(ButtonDetails::text(text).with_default_duration()),
) )
} }
/// Arrow back on left and hold-to-confirm text on the right. /// Arrow back on left and hold-to-confirm text on the right.
pub fn back_and_htc_text(text: StrBuffer, duration: Duration) -> Self { pub fn arrow_none_htc(text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::left_arrow_icon()), Some(ButtonDetails::left_arrow_icon()),
None, None,
Some(ButtonDetails::text(text).with_duration(duration)), Some(ButtonDetails::text(text).with_default_duration()),
)
}
/// Arrow back on left and text on the right.
pub fn back_and_text(text: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::text(text)),
) )
} }
/// Only armed text in the middle. /// Only armed text in the middle.
pub fn middle_armed_text(text: StrBuffer) -> Self { pub fn none_armed_none(text: StrBuffer) -> Self {
Self::new(None, Some(ButtonDetails::armed_text(text)), None) Self::new(None, Some(ButtonDetails::armed_text(text)), None)
} }
/// Only hold-to-confirm with text on the right. /// Only hold-to-confirm with text on the right.
pub fn htc_only(text: StrBuffer, duration: Duration) -> Self { pub fn none_none_htc(text: StrBuffer, duration: Duration) -> Self {
Self::new( Self::new(
None, None,
None, None,
@ -614,30 +636,14 @@ impl ButtonLayout {
) )
} }
/// Only right arrow facing down. /// Only left arrow.
pub fn only_arrow_down() -> Self { pub fn arrow_none_none() -> Self {
Self::new(None, None, Some(ButtonDetails::down_arrow_icon_wide())) Self::new(Some(ButtonDetails::left_arrow_icon()), None, None)
} }
}
#[cfg(feature = "ui_debug")] /// Only right arrow facing down.
impl crate::trace::Trace for ButtonDetails { pub fn none_none_arrow_wide() -> Self {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { Self::new(None, None, Some(ButtonDetails::down_arrow_icon_wide()))
t.open("ButtonDetails");
let mut btn_text: String<30> = String::new();
if let Some(text) = &self.text {
btn_text.push_str(text.as_ref()).unwrap();
} else if let Some(icon) = &self.icon {
btn_text.push_str("Icon:").unwrap();
btn_text.push_str(icon.text.as_ref()).unwrap();
}
if let Some(duration) = &self.duration {
btn_text.push_str(" (HTC:").unwrap();
btn_text.push_str(inttostr!(duration.to_millis())).unwrap();
btn_text.push_str(")").unwrap();
}
t.button(btn_text.as_ref());
t.close();
} }
} }
@ -667,6 +673,195 @@ pub enum ButtonAction {
Action(&'static str), Action(&'static str),
} }
/// Storing actions for all three possible buttons.
#[derive(Clone, Copy)]
pub struct ButtonActions {
pub left: Option<ButtonAction>,
pub middle: Option<ButtonAction>,
pub right: Option<ButtonAction>,
}
impl ButtonActions {
pub fn new(
left: Option<ButtonAction>,
middle: Option<ButtonAction>,
right: Option<ButtonAction>,
) -> Self {
Self {
left,
middle,
right,
}
}
/// Going back with left, going further with right
pub fn prev_none_next() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
None,
Some(ButtonAction::NextPage),
)
}
/// Going back with left, going further with middle
pub fn prev_next_none() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
Some(ButtonAction::NextPage),
None,
)
}
/// Previous with left, confirming with right
pub fn prev_none_confirm() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
None,
Some(ButtonAction::Confirm),
)
}
/// Previous with left, confirming with middle
pub fn prev_confirm_none() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
Some(ButtonAction::Confirm),
None,
)
}
/// Going to last page with left, to the next page with right
pub fn last_none_next() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(-1)),
None,
Some(ButtonAction::NextPage),
)
}
/// Going to last page with left, to the next page with right and confirm
/// with middle
pub fn last_confirm_next() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(-1)),
Some(ButtonAction::Confirm),
Some(ButtonAction::NextPage),
)
}
/// Going to previous page with left, to the next page with right and
/// confirm with middle
pub fn prev_confirm_next() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
Some(ButtonAction::Confirm),
Some(ButtonAction::NextPage),
)
}
/// Cancelling with left, going to the next page with right
pub fn cancel_none_next() -> Self {
Self::new(
Some(ButtonAction::Cancel),
None,
Some(ButtonAction::NextPage),
)
}
/// Only going to the next page with right
pub fn none_none_next() -> Self {
Self::new(None, None, Some(ButtonAction::NextPage))
}
/// Only going to the prev page with left
pub fn prev_none_none() -> Self {
Self::new(Some(ButtonAction::PrevPage), None, None)
}
/// Cancelling with left, confirming with right
pub fn cancel_none_confirm() -> Self {
Self::new(
Some(ButtonAction::Cancel),
None,
Some(ButtonAction::Confirm),
)
}
/// Cancelling with left, confirming with middle and next with right
pub fn cancel_confirm_next() -> Self {
Self::new(
Some(ButtonAction::Cancel),
Some(ButtonAction::Confirm),
Some(ButtonAction::NextPage),
)
}
/// Going to the beginning with left, confirming with right
pub fn beginning_none_confirm() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(0)),
None,
Some(ButtonAction::Confirm),
)
}
/// Going to the beginning with left, cancelling with right
pub fn beginning_none_cancel() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(0)),
None,
Some(ButtonAction::Cancel),
)
}
/// Having access to appropriate action based on the `ButtonPos`
pub fn get_action(&self, pos: ButtonPos) -> Option<ButtonAction> {
match pos {
ButtonPos::Left => self.left,
ButtonPos::Middle => self.middle,
ButtonPos::Right => self.right,
}
}
}
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Button {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Button");
match &self.content {
ButtonContent::Text(text) => t.field("text", text),
ButtonContent::Icon(icon) => t.field("icon", icon),
}
t.close();
}
}
#[cfg(feature = "ui_debug")]
use heapless::String;
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ButtonDetails {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("ButtonDetails");
let mut btn_text: String<30> = String::new();
if let Some(text) = &self.text {
btn_text.push_str(text.as_ref()).unwrap();
} else if let Some(icon) = &self.icon {
btn_text.push_str("Icon:").unwrap();
btn_text.push_str(icon.text.as_ref()).unwrap();
}
if let Some(duration) = &self.duration {
btn_text.push_str(" (HTC:").unwrap();
btn_text.push_str(inttostr!(duration.to_millis())).unwrap();
btn_text.push_str(")").unwrap();
}
t.button(btn_text.as_ref());
t.close();
}
}
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl ButtonAction { impl ButtonAction {
/// Describing the action as a string. Debug-only. /// Describing the action as a string. Debug-only.
@ -697,126 +892,3 @@ impl ButtonAction {
"None".into() "None".into()
} }
} }
/// Storing actions for all three possible buttons.
#[derive(Clone, Copy)]
pub struct ButtonActions {
pub left: Option<ButtonAction>,
pub middle: Option<ButtonAction>,
pub right: Option<ButtonAction>,
}
impl ButtonActions {
pub fn new(
left: Option<ButtonAction>,
middle: Option<ButtonAction>,
right: Option<ButtonAction>,
) -> Self {
Self {
left,
middle,
right,
}
}
/// Going back with left, going further with right
pub fn prev_next() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
None,
Some(ButtonAction::NextPage),
)
}
/// Going back with left, going further with middle
pub fn prev_next_with_middle() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
Some(ButtonAction::NextPage),
None,
)
}
/// Previous with left, confirming with right
pub fn prev_confirm() -> Self {
Self::new(
Some(ButtonAction::PrevPage),
None,
Some(ButtonAction::Confirm),
)
}
/// Going to last page with left, to the next page with right
pub fn last_next() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(-1)),
None,
Some(ButtonAction::NextPage),
)
}
/// Going to last page with left, to the next page with right and confirm
/// with middle
pub fn last_confirm_next() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(-1)),
Some(ButtonAction::Confirm),
Some(ButtonAction::NextPage),
)
}
/// Cancelling with left, going to the next page with right
pub fn cancel_next() -> Self {
Self::new(
Some(ButtonAction::Cancel),
None,
Some(ButtonAction::NextPage),
)
}
/// Only going to the next page with right
pub fn only_next() -> Self {
Self::new(None, None, Some(ButtonAction::NextPage))
}
/// Only going to the prev page with left
pub fn only_prev() -> Self {
Self::new(Some(ButtonAction::PrevPage), None, None)
}
/// Cancelling with left, confirming with right
pub fn cancel_confirm() -> Self {
Self::new(
Some(ButtonAction::Cancel),
None,
Some(ButtonAction::Confirm),
)
}
/// Going to the beginning with left, confirming with right
pub fn beginning_confirm() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(0)),
None,
Some(ButtonAction::Confirm),
)
}
/// Going to the beginning with left, cancelling with right
pub fn beginning_cancel() -> Self {
Self::new(
Some(ButtonAction::GoToIndex(0)),
None,
Some(ButtonAction::Cancel),
)
}
/// Having access to appropriate action based on the `ButtonPos`
pub fn get_action(&self, pos: ButtonPos) -> Option<ButtonAction> {
match pos {
ButtonPos::Left => self.left,
ButtonPos::Middle => self.middle,
ButtonPos::Right => self.right,
}
}
}

View File

@ -11,8 +11,6 @@ use crate::{
}, },
}; };
use heapless::String;
/// All possible states buttons (left and right) can be at. /// All possible states buttons (left and right) can be at.
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
enum ButtonState { enum ButtonState {
@ -435,8 +433,12 @@ impl Component for ButtonController {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
use super::ButtonContent; use super::ButtonContent;
#[cfg(feature = "ui_debug")]
use heapless::String;
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ButtonContainer { impl crate::trace::Trace for ButtonContainer {

View File

@ -14,6 +14,7 @@ use super::{
/// To be returned directly from Flow. /// To be returned directly from Flow.
pub enum FlowMsg { pub enum FlowMsg {
Confirmed, Confirmed,
ConfirmedIndex(u8),
Cancelled, Cancelled,
} }
@ -30,6 +31,7 @@ pub struct Flow<F, const M: usize> {
pad: Pad, pad: Pad,
buttons: Child<ButtonController>, buttons: Child<ButtonController>,
page_counter: u8, page_counter: u8,
return_confirmed_index: bool,
} }
impl<F, const M: usize> Flow<F, M> impl<F, const M: usize> Flow<F, M>
@ -51,6 +53,7 @@ where
// `content.page_count()`. // `content.page_count()`.
buttons: Child::new(ButtonController::new(ButtonLayout::empty())), buttons: Child::new(ButtonController::new(ButtonLayout::empty())),
page_counter: 0, page_counter: 0,
return_confirmed_index: false,
} }
} }
@ -61,6 +64,12 @@ where
self self
} }
/// Causing the Flow to return the index of the page that was confirmed.
pub fn with_return_confirmed_index(mut self) -> Self {
self.return_confirmed_index = true;
self
}
/// Getting new current page according to page counter. /// Getting new current page according to page counter.
/// Also updating the possible title and moving the scrollbar to correct /// Also updating the possible title and moving the scrollbar to correct
/// position. /// position.
@ -222,7 +231,13 @@ where
return None; return None;
} }
ButtonAction::Cancel => return Some(FlowMsg::Cancelled), ButtonAction::Cancel => return Some(FlowMsg::Cancelled),
ButtonAction::Confirm => return Some(FlowMsg::Confirmed), ButtonAction::Confirm => {
if self.return_confirmed_index {
return Some(FlowMsg::ConfirmedIndex(self.page_counter));
} else {
return Some(FlowMsg::Confirmed);
}
}
ButtonAction::Select => {} ButtonAction::Select => {}
ButtonAction::Action(_) => {} ButtonAction::Action(_) => {}
} }
@ -247,6 +262,8 @@ where
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
use heapless::String; use heapless::String;

View File

@ -332,6 +332,8 @@ impl<const M: usize> Paginate for Page<M> {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
pub mod trace { pub mod trace {
use crate::ui::model_tr::component::flow_pages_poc_helpers::TraceSink; use crate::ui::model_tr::component::flow_pages_poc_helpers::TraceSink;

View File

@ -96,6 +96,8 @@ where
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Frame<T> impl<T> crate::trace::Trace for Frame<T>
where where

View File

@ -108,6 +108,8 @@ impl Component for HoldToConfirm {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for HoldToConfirm { impl crate::trace::Trace for HoldToConfirm {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

@ -85,17 +85,6 @@ impl Component for Homescreen {
} }
} }
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Homescreen {
fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("Homescreen");
d.kw_pair("active_page", "0");
d.kw_pair("page_count", "1");
d.field("label", &self.label.as_ref());
d.close();
}
}
pub struct Lockscreen { pub struct Lockscreen {
label: StrBuffer, label: StrBuffer,
bootscreen: bool, bootscreen: bool,
@ -137,6 +126,19 @@ impl Component for Lockscreen {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Homescreen {
fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("Homescreen");
d.kw_pair("active_page", "0");
d.kw_pair("page_count", "1");
d.field("label", &self.label.as_ref());
d.close();
}
}
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Lockscreen { impl crate::trace::Trace for Lockscreen {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

@ -58,11 +58,8 @@ impl ChoiceFactory for ChoiceFactoryBIP39 {
match self { match self {
Self::Letters(letter_choices) => { Self::Letters(letter_choices) => {
if choice_index >= letter_choices.len() as u8 { if choice_index >= letter_choices.len() as u8 {
ChoiceItem::new( ChoiceItem::new("DELETE", ButtonLayout::arrow_armed_icon("CONFIRM".into()))
"DELETE", .with_icon(Icon::new(theme::ICON_DELETE))
ButtonLayout::three_icons_middle_text("CONFIRM".into()),
)
.with_icon(Icon::new(theme::ICON_DELETE))
} else { } else {
let letter = letter_choices[choice_index as usize]; let letter = letter_choices[choice_index as usize];
ChoiceItem::new( ChoiceItem::new(
@ -73,11 +70,9 @@ impl ChoiceFactory for ChoiceFactoryBIP39 {
} }
Self::Words(word_choices) => { Self::Words(word_choices) => {
if choice_index >= word_choices.len() as u8 { if choice_index >= word_choices.len() as u8 {
let mut item = ChoiceItem::new( let mut item =
"DELETE", ChoiceItem::new("DELETE", ButtonLayout::arrow_armed_icon("CONFIRM".into()))
ButtonLayout::three_icons_middle_text("CONFIRM".into()), .with_icon(Icon::new(theme::ICON_DELETE));
)
.with_icon(Icon::new(theme::ICON_DELETE));
item.set_right_btn(None); item.set_right_btn(None);
item item
} else { } else {
@ -223,6 +218,8 @@ impl Component for Bip39Entry {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
use super::super::{ButtonAction, ButtonPos}; use super::super::{ButtonAction, ButtonPos};
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]

View File

@ -421,6 +421,8 @@ where
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<F> crate::trace::Trace for ChoicePage<F> impl<F> crate::trace::Trace for ChoicePage<F>
where where

View File

@ -198,6 +198,8 @@ impl Choice for ChoiceItem {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ChoiceItem { impl crate::trace::Trace for ChoiceItem {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {

View File

@ -135,10 +135,7 @@ impl ChoiceFactoryPassphrase {
/// return back /// return back
fn get_character_item(&self, choice_index: u8) -> ChoiceItem { fn get_character_item(&self, choice_index: u8) -> ChoiceItem {
if is_menu_choice(&self.current_category, choice_index) { if is_menu_choice(&self.current_category, choice_index) {
ChoiceItem::new( ChoiceItem::new("MENU", ButtonLayout::arrow_armed_icon("RETURN".into()))
"MENU",
ButtonLayout::three_icons_middle_text("RETURN".into()),
)
} else { } else {
let ch = get_char(&self.current_category, choice_index); let ch = get_char(&self.current_category, choice_index);
ChoiceItem::new(char_to_string::<1>(ch), ButtonLayout::default_three_icons()) ChoiceItem::new(char_to_string::<1>(ch), ButtonLayout::default_three_icons())
@ -304,6 +301,8 @@ impl Component for PassphraseEntry {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
use super::super::{ButtonAction, ButtonPos}; use super::super::{ButtonAction, ButtonPos};
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]

View File

@ -215,6 +215,8 @@ impl Component for PinEntry {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
use super::super::{ButtonAction, ButtonPos}; use super::super::{ButtonAction, ButtonPos};

View File

@ -9,9 +9,6 @@ use crate::{
use super::super::{ButtonLayout, ChoiceFactory, ChoiceItem, ChoicePage, ChoicePageMsg}; use super::super::{ButtonLayout, ChoiceFactory, ChoiceItem, ChoicePage, ChoicePageMsg};
use heapless::{String, Vec}; use heapless::{String, Vec};
#[cfg(feature = "ui_debug")]
use super::super::{ButtonAction, ButtonPos};
pub enum SimpleChoiceMsg { pub enum SimpleChoiceMsg {
Result(String<50>), Result(String<50>),
} }
@ -95,6 +92,11 @@ impl<const N: usize> Component for SimpleChoice<N> {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
use super::super::{ButtonAction, ButtonPos};
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<const N: usize> crate::trace::Trace for SimpleChoice<N> { impl<const N: usize> crate::trace::Trace for SimpleChoice<N> {
fn get_btn_action(&self, pos: ButtonPos) -> String<25> { fn get_btn_action(&self, pos: ButtonPos) -> String<25> {

View File

@ -241,6 +241,8 @@ impl LoaderStyleSheet {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Loader { impl crate::trace::Trace for Loader {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

@ -13,7 +13,6 @@ mod loader;
mod no_btn_dialog; mod no_btn_dialog;
mod page; mod page;
mod progress; mod progress;
mod qr_code;
mod result_anim; mod result_anim;
mod result_popup; mod result_popup;
mod scrollbar; mod scrollbar;
@ -45,7 +44,6 @@ pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
pub use no_btn_dialog::{NoBtnDialog, NoBtnDialogMsg}; pub use no_btn_dialog::{NoBtnDialog, NoBtnDialogMsg};
pub use page::ButtonPage; pub use page::ButtonPage;
pub use progress::Progress; pub use progress::Progress;
pub use qr_code::{QRCodePage, QRCodePageMessage};
pub use result_anim::{ResultAnim, ResultAnimMsg}; pub use result_anim::{ResultAnim, ResultAnimMsg};
pub use result_popup::{ResultPopup, ResultPopupMsg}; pub use result_popup::{ResultPopup, ResultPopupMsg};
pub use scrollbar::ScrollBar; pub use scrollbar::ScrollBar;

View File

@ -55,6 +55,8 @@ where
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for NoBtnDialog<T, U> impl<T, U> crate::trace::Trace for NoBtnDialog<T, U>
where where

View File

@ -217,6 +217,8 @@ where
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
use super::ButtonAction; use super::ButtonAction;
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]

View File

@ -120,6 +120,8 @@ impl Component for Progress {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Progress { impl crate::trace::Trace for Progress {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {

View File

@ -1,109 +0,0 @@
use crate::{
micropython::buffer::StrBuffer,
ui::{
component::{Child, Component, Event, EventCtx},
display::{self, Font},
geometry::Rect,
},
};
use super::{theme, ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos};
pub enum QRCodePageMessage {
Confirmed,
Cancelled,
}
pub struct QRCodePage<T> {
title: StrBuffer,
title_area: Rect,
qr_code: T,
buttons: Child<ButtonController>,
}
impl<T> QRCodePage<T> {
pub fn new(title: StrBuffer, qr_code: T, btn_layout: ButtonLayout) -> Self {
Self {
title,
title_area: Rect::zero(),
qr_code,
buttons: Child::new(ButtonController::new(btn_layout)),
}
}
}
impl<T> Component for QRCodePage<T>
where
T: Component,
{
type Msg = QRCodePageMessage;
fn place(&mut self, bounds: Rect) -> Rect {
let (content_area, button_area) = bounds.split_bottom(theme::BUTTON_HEIGHT);
let (qr_code_area, title_area) = content_area.split_left(theme::QR_SIDE_MAX);
self.title_area = title_area;
self.qr_code.place(qr_code_area);
self.buttons.place(button_area);
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
let button_event = self.buttons.event(ctx, event);
if let Some(ButtonControllerMsg::Triggered(pos)) = button_event {
match pos {
ButtonPos::Left => {
return Some(QRCodePageMessage::Cancelled);
}
ButtonPos::Right => {
return Some(QRCodePageMessage::Confirmed);
}
_ => {}
}
}
None
}
fn paint(&mut self) {
self.qr_code.paint();
// TODO: add the Label from Suite
display::text_multiline(
self.title_area,
self.title.as_ref(),
Font::MONO,
theme::FG,
theme::BG,
);
self.buttons.paint();
}
}
#[cfg(feature = "ui_debug")]
use super::ButtonAction;
#[cfg(feature = "ui_debug")]
use heapless::String;
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for QRCodePage<T> {
fn get_btn_action(&self, pos: ButtonPos) -> String<25> {
match pos {
ButtonPos::Left => ButtonAction::Cancel.string(),
ButtonPos::Right => ButtonAction::Confirm.string(),
ButtonPos::Middle => ButtonAction::empty(),
}
}
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("QRCodePage");
t.kw_pair("active_page", "0");
t.kw_pair("page_count", "1");
self.report_btn_actions(t);
t.content_flag();
t.string("QR CODE");
t.string(self.title.as_ref());
t.content_flag();
t.field("buttons", &self.buttons);
t.close();
}
}

View File

@ -140,6 +140,8 @@ impl Component for ResultAnim {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ResultAnim { impl crate::trace::Trace for ResultAnim {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

@ -49,7 +49,7 @@ impl ResultPopup {
.with_placement(LinearPlacement::vertical().align_at_center()); .with_placement(LinearPlacement::vertical().align_at_center());
let buttons = button_text.map(|text| { let buttons = button_text.map(|text| {
let btn_layout = ButtonLayout::only_right_text(text.into()); let btn_layout = ButtonLayout::none_none_text(text.into());
Child::new(ButtonController::new(btn_layout)) Child::new(ButtonController::new(btn_layout))
}); });
@ -155,6 +155,8 @@ impl Component for ResultPopup {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ResultPopup { impl crate::trace::Trace for ResultPopup {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

@ -142,6 +142,8 @@ impl<const N: usize> Paginate for ShareWords<N> {
} }
} }
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<const N: usize> crate::trace::Trace for ShareWords<N> { impl<const N: usize> crate::trace::Trace for ShareWords<N> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {

View File

@ -6,14 +6,15 @@ use crate::{
error::Error, error::Error,
micropython::{ micropython::{
buffer::StrBuffer, buffer::StrBuffer,
gc::Gc,
iter::{Iter, IterBuf}, iter::{Iter, IterBuf},
list::List,
map::Map, map::Map,
module::Module, module::Module,
obj::Obj, obj::Obj,
qstr::Qstr, qstr::Qstr,
util, util,
}, },
time::Duration,
ui::{ ui::{
component::{ component::{
base::Component, base::Component,
@ -38,7 +39,7 @@ use super::{
Bip39Entry, Bip39EntryMsg, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, Flow, Bip39Entry, Bip39EntryMsg, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, Flow,
FlowMsg, FlowPages, Frame, Homescreen, HomescreenMsg, Lockscreen, NoBtnDialog, FlowMsg, FlowPages, Frame, Homescreen, HomescreenMsg, Lockscreen, NoBtnDialog,
NoBtnDialogMsg, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry, PinEntryMsg, Progress, NoBtnDialogMsg, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry, PinEntryMsg, Progress,
QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg, ShareWords, SimpleChoice, SimpleChoiceMsg,
}, },
constant, theme, constant, theme,
}; };
@ -94,18 +95,7 @@ where
match msg { match msg {
FlowMsg::Confirmed => Ok(CONFIRMED.as_obj()), FlowMsg::Confirmed => Ok(CONFIRMED.as_obj()),
FlowMsg::Cancelled => Ok(CANCELLED.as_obj()), FlowMsg::Cancelled => Ok(CANCELLED.as_obj()),
} FlowMsg::ConfirmedIndex(page) => Ok(page.into()),
}
}
impl<T> ComponentMsgObj for QRCodePage<T>
where
T: Component,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
QRCodePageMessage::Confirmed => Ok(CONFIRMED.as_obj()),
QRCodePageMessage::Cancelled => Ok(CANCELLED.as_obj()),
} }
} }
} }
@ -181,11 +171,13 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
let action: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?; let action: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<StrBuffer> = let description: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let verb: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?; let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?;
let verb_cancel: Option<StrBuffer> = let verb_cancel: Option<StrBuffer> = kwargs
kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into_option()?; .get(Qstr::MP_QSTR_verb_cancel)
let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?; .unwrap_or_else(|_| Obj::const_none())
let hold: bool = kwargs.get(Qstr::MP_QSTR_hold)?.try_into()?; .try_into_option()?;
let reverse: bool = kwargs.get_or(Qstr::MP_QSTR_reverse, false)?;
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
let paragraphs = { let paragraphs = {
let action = action.unwrap_or_default(); let action = action.unwrap_or_default();
@ -215,7 +207,6 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
}; };
// Right button - text or nothing. // Right button - text or nothing.
let verb = verb.unwrap_or_default();
let mut confirm_btn = if verb.len() > 0 { let mut confirm_btn = if verb.len() > 0 {
Some(ButtonDetails::text(verb)) Some(ButtonDetails::text(verb))
} else { } else {
@ -293,24 +284,16 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
match page_index { match page_index {
0 => { 0 => {
// RECIPIENT + address // RECIPIENT + address
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::cancel_none_text("CONFIRM".into());
Some(ButtonDetails::cancel_icon()), let btn_actions = ButtonActions::cancel_none_next();
None,
Some(ButtonDetails::text("CONFIRM".into())),
);
let btn_actions = ButtonActions::cancel_next();
Page::<10>::new(btn_layout, btn_actions, Font::MONO) Page::<10>::new(btn_layout, btn_actions, Font::MONO)
.with_title(address_title) .with_title(address_title)
.text_mono(address) .text_mono(address)
} }
1 => { 1 => {
// AMOUNT + amount // AMOUNT + amount
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::up_arrow_none_text("CONFIRM".into());
Some(ButtonDetails::up_arrow_icon()), let btn_actions = ButtonActions::prev_none_confirm();
None,
Some(ButtonDetails::text("CONFIRM".into())),
);
let btn_actions = ButtonActions::cancel_confirm();
Page::<10>::new(btn_layout, btn_actions, Font::MONO) Page::<10>::new(btn_layout, btn_actions, Font::MONO)
.with_title(amount_title) .with_title(amount_title)
.newline() .newline()
@ -341,12 +324,8 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
// Total amount + fee // Total amount + fee
assert!(page_index == 0); assert!(page_index == 0);
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::cancel_none_htc("HOLD TO CONFIRM".into());
Some(ButtonDetails::cancel_icon()), let btn_actions = ButtonActions::cancel_none_confirm();
None,
Some(ButtonDetails::text("HOLD TO CONFIRM".into()).with_default_duration()),
);
let btn_actions = ButtonActions::cancel_confirm();
let mut flow_page = Page::<15>::new(btn_layout, btn_actions, Font::MONO) let mut flow_page = Page::<15>::new(btn_layout, btn_actions, Font::MONO)
.text_bold(total_label) .text_bold(total_label)
@ -386,11 +365,7 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
match page_index { match page_index {
0 => { 0 => {
// RECEIVE ADDRESS // RECEIVE ADDRESS
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::cancel_armed_text("CONFIRM".into(), "i".into());
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text("CONFIRM".into())),
Some(ButtonDetails::text("i".into())),
);
let btn_actions = ButtonActions::last_confirm_next(); let btn_actions = ButtonActions::last_confirm_next();
Page::<15>::new(btn_layout, btn_actions, Font::BOLD) Page::<15>::new(btn_layout, btn_actions, Font::BOLD)
.text_bold(title) .text_bold(title)
@ -400,12 +375,8 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
} }
1 => { 1 => {
// QR CODE // QR CODE
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::arrow_none_arrow();
Some(ButtonDetails::left_arrow_icon()), let btn_actions = ButtonActions::prev_none_next();
None,
Some(ButtonDetails::right_arrow_icon()),
);
let btn_actions = ButtonActions::prev_next();
Page::<15>::new(btn_layout, btn_actions, Font::MONO).qr_code( Page::<15>::new(btn_layout, btn_actions, Font::MONO).qr_code(
address_qr, address_qr,
theme::QR_SIDE_MAX, theme::QR_SIDE_MAX,
@ -415,9 +386,8 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
} }
2 => { 2 => {
// ADDRESS INFO // ADDRESS INFO
let btn_layout = let btn_layout = ButtonLayout::arrow_none_none();
ButtonLayout::new(Some(ButtonDetails::left_arrow_icon()), None, None); let btn_actions = ButtonActions::prev_none_none();
let btn_actions = ButtonActions::only_prev();
Page::<15>::new(btn_layout, btn_actions, Font::MONO) Page::<15>::new(btn_layout, btn_actions, Font::MONO)
.text_bold("Account:".into()) .text_bold("Account:".into())
.newline() .newline()
@ -429,12 +399,8 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
} }
3 => { 3 => {
// ADDRESS MISMATCH // ADDRESS MISMATCH
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::arrow_none_text("QUIT".into());
Some(ButtonDetails::left_arrow_icon()), let btn_actions = ButtonActions::beginning_none_cancel();
None,
Some(ButtonDetails::text("QUIT".into())),
);
let btn_actions = ButtonActions::beginning_cancel();
Page::<15>::new(btn_layout, btn_actions, Font::MONO) Page::<15>::new(btn_layout, btn_actions, Font::MONO)
.text_bold("ADDRESS MISMATCH?".into()) .text_bold("ADDRESS MISMATCH?".into())
.newline() .newline()
@ -482,6 +448,73 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } 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 app_name: StrBuffer = 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();
let title: StrBuffer = if page_count > 1 {
"IMPORT".into()
} else {
"IMPORT CREDENTIAL".into()
};
// 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 as usize));
let account = account_obj.try_into().unwrap_or_else(|_| "".into());
let (btn_layout, btn_actions) = if page_count == 1 {
// There is only one page
(
ButtonLayout::cancel_none_text("CONFIRM".into()),
ButtonActions::cancel_none_confirm(),
)
} else if page_index == 0 {
// First page
(
ButtonLayout::cancel_armed_arrow("SELECT".into()),
ButtonActions::cancel_confirm_next(),
)
} else if page_index as usize == page_count - 1 {
// Last page
(
ButtonLayout::arrow_armed_none("SELECT".into()),
ButtonActions::prev_confirm_none(),
)
} else {
// Page in the middle
(
ButtonLayout::arrow_armed_icon("SELECT".into()),
ButtonActions::prev_confirm_next(),
)
};
Page::<10>::new(btn_layout, btn_actions, Font::MONO)
.newline()
.text_mono(app_name)
.newline()
.text_bold(account)
};
let pages = FlowPages::new(get_page, page_count as u8);
// 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) }
}
/// General pattern of most tutorial screens. /// General pattern of most tutorial screens.
/// (title, text, btn_layout, btn_actions) /// (title, text, btn_layout, btn_actions)
fn tutorial_screen( fn tutorial_screen(
@ -523,47 +556,47 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen( tutorial_screen(
"HELLO".into(), "HELLO".into(),
"Welcome to Trezor.\nPress right to continue.".into(), "Welcome to Trezor.\nPress right to continue.".into(),
ButtonLayout::cancel_and_arrow(), ButtonLayout::cancel_none_arrow(),
ButtonActions::last_next(), ButtonActions::last_none_next(),
) )
}, },
1 => { 1 => {
tutorial_screen( tutorial_screen(
"".into(), "".into(),
"Use Trezor by clicking left and right.\n\nContinue right.".into(), "Use Trezor by clicking left and right.\n\nContinue right.".into(),
ButtonLayout::left_right_arrows(), ButtonLayout::arrow_none_arrow(),
ButtonActions::prev_next(), ButtonActions::prev_none_next(),
) )
}, },
2 => { 2 => {
tutorial_screen( tutorial_screen(
"HOLD TO CONFIRM".into(), "HOLD TO CONFIRM".into(),
"Press and hold right to approve important operations.".into(), "Press and hold right to approve important operations.".into(),
ButtonLayout::back_and_htc_text("HOLD TO CONFIRM".into(), Duration::from_millis(1000)), ButtonLayout::arrow_none_htc("HOLD TO CONFIRM".into()),
ButtonActions::prev_next(), ButtonActions::prev_none_next(),
) )
}, },
3 => { 3 => {
tutorial_screen( tutorial_screen(
"SCREEN SCROLL".into(), "SCREEN SCROLL".into(),
"Press right to scroll down to read all content when text\ndoesn't fit on one screen. Press left to scroll up.".into(), "Press right to scroll down to read all content when text\ndoesn't fit on one screen. Press left to scroll up.".into(),
ButtonLayout::back_and_text("GOT IT".into()), ButtonLayout::arrow_none_text("GOT IT".into()),
ButtonActions::prev_next(), ButtonActions::prev_none_next(),
) )
}, },
4 => { 4 => {
tutorial_screen( tutorial_screen(
"CONFIRM".into(), "CONFIRM".into(),
"Press both left and right at the same time to confirm.".into(), "Press both left and right at the same time to confirm.".into(),
ButtonLayout::middle_armed_text("CONFIRM".into()), ButtonLayout::none_armed_none("CONFIRM".into()),
ButtonActions::prev_next_with_middle(), ButtonActions::prev_next_none(),
) )
}, },
// This page is special // This page is special
5 => { 5 => {
Page::<10>::new( Page::<10>::new(
ButtonLayout::left_right_text("AGAIN".into(), "FINISH".into()), ButtonLayout::text_none_text("AGAIN".into(), "FINISH".into()),
ButtonActions::beginning_confirm(), ButtonActions::beginning_none_confirm(),
Font::MONO, Font::MONO,
) )
.newline() .newline()
@ -577,8 +610,8 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen( tutorial_screen(
"SKIP TUTORIAL".into(), "SKIP TUTORIAL".into(),
"Are you sure you want to skip the tutorial?".into(), "Are you sure you want to skip the tutorial?".into(),
ButtonLayout::cancel_and_text("SKIP".into()), ButtonLayout::cancel_none_text("SKIP".into()),
ButtonActions::beginning_cancel(), ButtonActions::beginning_none_cancel(),
) )
}, },
_ => unreachable!(), _ => unreachable!(),
@ -773,9 +806,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def confirm_action( /// def confirm_action(
/// *, /// *,
/// title: str, /// title: str,
/// action: str | None = None, /// action: str | None,
/// description: str | None = None, /// description: str | None,
/// verb: str | None = None, /// verb: str = "CONFIRM",
/// verb_cancel: str | None = None, /// verb_cancel: str | None = None,
/// hold: bool = False, /// hold: bool = False,
/// hold_danger: bool = False, # unused on TR /// hold_danger: bool = False, # unused on TR
@ -837,6 +870,17 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Info modal.""" /// """Info modal."""
Qstr::MP_QSTR_show_info => obj_fn_kw!(0, new_show_info).as_obj(), Qstr::MP_QSTR_show_info => obj_fn_kw!(0, new_show_info).as_obj(),
/// def confirm_fido(
/// *,
/// app_name: str,
/// accounts: list[str | None],
/// ) -> int | object:
/// """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 tutorial() -> object: /// def tutorial() -> object:
/// """Show user how to interact with the device.""" /// """Show user how to interact with the device."""
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, tutorial).as_obj(), Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, tutorial).as_obj(),

View File

@ -1330,7 +1330,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// title: str, /// title: str,
/// action: str | None, /// action: str | None,
/// description: str | None, /// description: str | None,
/// verb: str | None = None, /// verb: str = "CONFIRM",
/// verb_cancel: str | None = None, /// verb_cancel: str | None = None,
/// hold: bool = False, /// hold: bool = False,
/// hold_danger: bool = False, /// hold_danger: bool = False,

View File

@ -13,9 +13,9 @@ def disable_animation(disable: bool) -> None:
def confirm_action( def confirm_action(
*, *,
title: str, title: str,
action: str | None = None, action: str | None,
description: str | None = None, description: str | None,
verb: str | None = None, verb: str = "CONFIRM",
verb_cancel: str | None = None, verb_cancel: str | None = None,
hold: bool = False, hold: bool = False,
hold_danger: bool = False, # unused on TR hold_danger: bool = False, # unused on TR
@ -82,6 +82,17 @@ def show_info(
"""Info modal.""" """Info modal."""
# rust/src/ui/model_tr/layout.rs
def confirm_fido(
*,
app_name: str,
accounts: list[str | None],
) -> int | object:
"""FIDO confirmation.
Returns page index in case of confirmation and CANCELLED otherwise.
"""
# rust/src/ui/model_tr/layout.rs # rust/src/ui/model_tr/layout.rs
def tutorial() -> object: def tutorial() -> object:
"""Show user how to interact with the device.""" """Show user how to interact with the device."""
@ -197,7 +208,7 @@ def confirm_action(
title: str, title: str,
action: str | None, action: str | None,
description: str | None, description: str | None,
verb: str | None = None, verb: str = "CONFIRM",
verb_cancel: str | None = None, verb_cancel: str | None = None,
hold: bool = False, hold: bool = False,
hold_danger: bool = False, hold_danger: bool = False,

View File

@ -115,7 +115,7 @@ class Approver:
self, self,
txo: TxOutput, txo: TxOutput,
script_pubkey: bytes, script_pubkey: bytes,
index: int | None, index: int | None = None,
orig_txo: TxOutput | None = None, orig_txo: TxOutput | None = None,
) -> None: ) -> None:
self._add_output(txo, script_pubkey) self._add_output(txo, script_pubkey)
@ -169,7 +169,7 @@ class BasicApprover(Approver):
self, self,
txo: TxOutput, txo: TxOutput,
script_pubkey: bytes, script_pubkey: bytes,
index: int | None, index: int | None = None,
orig_txo: TxOutput | None = None, orig_txo: TxOutput | None = None,
) -> None: ) -> None:
from trezor.enums import OutputScriptType from trezor.enums import OutputScriptType

View File

@ -469,7 +469,7 @@ async def get_bool(
title: str, title: str,
data: str | None = None, data: str | None = None,
description: str | None = None, description: str | None = None,
verb: str | None = "CONFIRM", verb: str = "CONFIRM",
verb_cancel: str | None = "", verb_cancel: str | None = "",
hold: bool = False, hold: bool = False,
reverse: bool = False, reverse: bool = False,
@ -723,7 +723,7 @@ async def _show_modal(
title=header.upper(), title=header.upper(),
action=subheader, action=subheader,
description=content, description=content,
verb=button_confirm, verb=button_confirm or "",
verb_cancel=button_cancel, verb_cancel=button_cancel,
exc=exc, exc=exc,
) )

View File

@ -1,5 +1,12 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from trezor.enums import ButtonRequestType
import trezorui2
from ..common import interact
from . import RustLayout
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.wire import GenericContext from trezor.wire import GenericContext
@ -14,8 +21,37 @@ async def confirm_fido(
accounts: list[str | None], accounts: list[str | None],
) -> int: ) -> int:
"""Webauthn confirmation for one or more credentials.""" """Webauthn confirmation for one or more credentials."""
raise NotImplementedError confirm = RustLayout(
trezorui2.confirm_fido( # type: ignore [Arguments missing]
app_name=app_name,
accounts=accounts,
)
)
if ctx is None:
result = await confirm
else:
result = await interact(ctx, confirm, "confirm_fido", ButtonRequestType.Other)
# The Rust side returns either an int or `CANCELLED`. We detect the int situation
# and assume cancellation otherwise.
if isinstance(result, int):
return result
# Late import won't get executed on the happy path.
from trezor.wire import ActionCancelled
raise ActionCancelled
async def confirm_fido_reset() -> bool: async def confirm_fido_reset() -> bool:
raise NotImplementedError confirm = RustLayout(
trezorui2.confirm_action(
title="FIDO2 RESET",
description="Do you really want to erase all credentials?",
action=None,
verb_cancel="",
verb="CONFIRM",
)
)
return (await confirm) is trezorui2.CONFIRMED

View File

@ -95,13 +95,13 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)), TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True, True,
helpers.UiConfirmTotal(12300000, 11000, fee_rate, coin, AmountUnit.BITCOIN), helpers.UiConfirmTotal(12300000, 11000, fee_rate, coin, AmountUnit.BITCOIN),
@ -229,7 +229,7 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),

View File

@ -93,13 +93,13 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)), TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True, True,
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False), helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
@ -227,7 +227,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),

View File

@ -92,13 +92,13 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)), TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True, True,
helpers.UiConfirmTotal(123445789 + 11000, 11000, fee_rate, coin, AmountUnit.BITCOIN), helpers.UiConfirmTotal(123445789 + 11000, 11000, fee_rate, coin, AmountUnit.BITCOIN),
@ -223,7 +223,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
@ -371,7 +371,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),

View File

@ -93,13 +93,13 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)), TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True, True,
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False), helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
@ -226,7 +226,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase):
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),

View File

@ -85,7 +85,7 @@ class TestSignTxFeeThreshold(unittest.TestCase):
TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)), TxAckPrevOutput(tx=TxAckPrevOutputWrapper(output=pout1)),
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0),
True, True,
helpers.UiConfirmFeeOverThreshold(100000, coin_bitcoin), helpers.UiConfirmFeeOverThreshold(100000, coin_bitcoin),
True, True,
@ -146,7 +146,7 @@ class TestSignTxFeeThreshold(unittest.TestCase):
True, True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0),
True, True,
helpers.UiConfirmTotal(300000 + 90000, 90000, fee_rate, coin_bitcoin, AmountUnit.BITCOIN), helpers.UiConfirmTotal(300000 + 90000, 90000, fee_rate, coin_bitcoin, AmountUnit.BITCOIN),
True, True,

View File

@ -111,7 +111,7 @@ class TestSignTx(unittest.TestCase):
serialized=EMPTY_SERIALIZED, serialized=EMPTY_SERIALIZED,
), ),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0),
True, True,
helpers.UiConfirmTotal(3_801_747, 50_000, fee_rate, coin_bitcoin, AmountUnit.BITCOIN), helpers.UiConfirmTotal(3_801_747, 50_000, fee_rate, coin_bitcoin, AmountUnit.BITCOIN),
True, True,

View File

@ -109,7 +109,7 @@ class TestSignTxDecred(unittest.TestCase):
), ),
), ),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin_decred, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin_decred, AmountUnit.BITCOIN, 0),
True, True,
helpers.UiConfirmTotal( helpers.UiConfirmTotal(
200_000_000, 100_000, fee_rate, coin_decred, AmountUnit.BITCOIN 200_000_000, 100_000, fee_rate, coin_decred, AmountUnit.BITCOIN

View File

@ -70,7 +70,7 @@ class TestSignTx_GRS(unittest.TestCase):
TxAckInput(tx=TxAckInputWrapper(input=inp1)), TxAckInput(tx=TxAckInputWrapper(input=inp1)),
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)), TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN), helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True, True,
helpers.UiConfirmTotal(210016, 192, fee_rate, coin, AmountUnit.BITCOIN), helpers.UiConfirmTotal(210016, 192, fee_rate, coin, AmountUnit.BITCOIN),
True, True,

View File

@ -30,9 +30,6 @@ RK_CAPACITY = 100
@pytest.mark.altcoin @pytest.mark.altcoin
@pytest.mark.setup_client(mnemonic=MNEMONIC12) @pytest.mark.setup_client(mnemonic=MNEMONIC12)
def test_add_remove(client: Client): def test_add_remove(client: Client):
if client.features.model == "R":
pytest.skip("Webauthn is not supported on model R")
# Remove index 0 should fail. # Remove index 0 should fail.
with pytest.raises(TrezorFailure): with pytest.raises(TrezorFailure):
fido.remove_credential(client, 0) fido.remove_credential(client, 0)