1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-04 21:05:29 +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
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:
stage: build
<<: *gitlab_caching
@ -370,22 +354,6 @@ core unix frozen debug build arm:
tags:
- 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:
stage: build
<<: *gitlab_caching

View File

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

View File

@ -9,8 +9,6 @@ use crate::{
},
};
use heapless::String;
use super::theme;
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)]
enum State {
Released,
@ -453,7 +439,7 @@ impl ButtonDetails {
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 {
self.duration = Some(Duration::from_millis(1000));
self
@ -511,11 +497,11 @@ impl ButtonLayout {
/// Default button layout for all three buttons - icons.
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.
pub fn three_icons_middle_text(middle_text: StrBuffer) -> Self {
pub fn arrow_armed_icon(middle_text: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
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.
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(
Some(ButtonDetails::text(text_left)),
None,
@ -533,12 +546,12 @@ impl ButtonLayout {
}
/// 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)))
}
/// Left and right arrow icons for navigation.
pub fn left_right_arrows() -> Self {
pub fn arrow_none_arrow() -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
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.
pub fn cancel_and_arrow() -> Self {
pub fn cancel_none_arrow() -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
None,
@ -556,7 +587,7 @@ impl ButtonLayout {
}
/// 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(
Some(ButtonDetails::cancel_icon()),
None,
@ -565,7 +596,7 @@ impl ButtonLayout {
}
/// 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(
Some(ButtonDetails::cancel_icon()),
None,
@ -574,39 +605,30 @@ impl ButtonLayout {
}
/// 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(
Some(ButtonDetails::cancel_icon()),
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.
pub fn back_and_htc_text(text: StrBuffer, duration: Duration) -> Self {
pub fn arrow_none_htc(text: StrBuffer) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::text(text).with_duration(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)),
Some(ButtonDetails::text(text).with_default_duration()),
)
}
/// 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)
}
/// 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(
None,
None,
@ -614,30 +636,14 @@ impl ButtonLayout {
)
}
/// Only right arrow facing down.
pub fn only_arrow_down() -> Self {
Self::new(None, None, Some(ButtonDetails::down_arrow_icon_wide()))
/// Only left arrow.
pub fn arrow_none_none() -> Self {
Self::new(Some(ButtonDetails::left_arrow_icon()), None, None)
}
}
#[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();
/// Only right arrow facing down.
pub fn none_none_arrow_wide() -> Self {
Self::new(None, None, Some(ButtonDetails::down_arrow_icon_wide()))
}
}
@ -667,6 +673,195 @@ pub enum ButtonAction {
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")]
impl ButtonAction {
/// Describing the action as a string. Debug-only.
@ -697,126 +892,3 @@ impl ButtonAction {
"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.
#[derive(Copy, Clone, PartialEq, Eq)]
enum ButtonState {
@ -435,8 +433,12 @@ impl Component for ButtonController {
}
}
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
use super::ButtonContent;
#[cfg(feature = "ui_debug")]
use heapless::String;
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ButtonContainer {

View File

@ -14,6 +14,7 @@ use super::{
/// To be returned directly from Flow.
pub enum FlowMsg {
Confirmed,
ConfirmedIndex(u8),
Cancelled,
}
@ -30,6 +31,7 @@ pub struct Flow<F, const M: usize> {
pad: Pad,
buttons: Child<ButtonController>,
page_counter: u8,
return_confirmed_index: bool,
}
impl<F, const M: usize> Flow<F, M>
@ -51,6 +53,7 @@ where
// `content.page_count()`.
buttons: Child::new(ButtonController::new(ButtonLayout::empty())),
page_counter: 0,
return_confirmed_index: false,
}
}
@ -61,6 +64,12 @@ where
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.
/// Also updating the possible title and moving the scrollbar to correct
/// position.
@ -222,7 +231,13 @@ where
return None;
}
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::Action(_) => {}
}
@ -247,6 +262,8 @@ where
}
}
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
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")]
pub mod trace {
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")]
impl<T> crate::trace::Trace for Frame<T>
where

View File

@ -108,6 +108,8 @@ impl Component for HoldToConfirm {
}
}
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for HoldToConfirm {
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 {
label: StrBuffer,
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")]
impl crate::trace::Trace for Lockscreen {
fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

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

View File

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

View File

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

View File

@ -135,10 +135,7 @@ impl ChoiceFactoryPassphrase {
/// return back
fn get_character_item(&self, choice_index: u8) -> ChoiceItem {
if is_menu_choice(&self.current_category, choice_index) {
ChoiceItem::new(
"MENU",
ButtonLayout::three_icons_middle_text("RETURN".into()),
)
ChoiceItem::new("MENU", ButtonLayout::arrow_armed_icon("RETURN".into()))
} else {
let ch = get_char(&self.current_category, choice_index);
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")]
use super::super::{ButtonAction, ButtonPos};
#[cfg(feature = "ui_debug")]

View File

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

View File

@ -9,9 +9,6 @@ use crate::{
use super::super::{ButtonLayout, ChoiceFactory, ChoiceItem, ChoicePage, ChoicePageMsg};
use heapless::{String, Vec};
#[cfg(feature = "ui_debug")]
use super::super::{ButtonAction, ButtonPos};
pub enum SimpleChoiceMsg {
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")]
impl<const N: usize> crate::trace::Trace for SimpleChoice<N> {
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")]
impl crate::trace::Trace for Loader {
fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

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

View File

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

View File

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

View File

@ -120,6 +120,8 @@ impl Component for Progress {
}
}
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Progress {
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")]
impl crate::trace::Trace for ResultAnim {
fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

@ -49,7 +49,7 @@ impl ResultPopup {
.with_placement(LinearPlacement::vertical().align_at_center());
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))
});
@ -155,6 +155,8 @@ impl Component for ResultPopup {
}
}
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ResultPopup {
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")]
impl<const N: usize> crate::trace::Trace for ShareWords<N> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {

View File

@ -6,14 +6,15 @@ use crate::{
error::Error,
micropython::{
buffer::StrBuffer,
gc::Gc,
iter::{Iter, IterBuf},
list::List,
map::Map,
module::Module,
obj::Obj,
qstr::Qstr,
util,
},
time::Duration,
ui::{
component::{
base::Component,
@ -38,7 +39,7 @@ use super::{
Bip39Entry, Bip39EntryMsg, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, Flow,
FlowMsg, FlowPages, Frame, Homescreen, HomescreenMsg, Lockscreen, NoBtnDialog,
NoBtnDialogMsg, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry, PinEntryMsg, Progress,
QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg,
ShareWords, SimpleChoice, SimpleChoiceMsg,
},
constant, theme,
};
@ -94,18 +95,7 @@ where
match msg {
FlowMsg::Confirmed => Ok(CONFIRMED.as_obj()),
FlowMsg::Cancelled => Ok(CANCELLED.as_obj()),
}
}
}
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()),
FlowMsg::ConfirmedIndex(page) => Ok(page.into()),
}
}
}
@ -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 description: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let verb: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?;
let verb_cancel: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into_option()?;
let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?;
let hold: bool = kwargs.get(Qstr::MP_QSTR_hold)?.try_into()?;
let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?;
let verb_cancel: Option<StrBuffer> = kwargs
.get(Qstr::MP_QSTR_verb_cancel)
.unwrap_or_else(|_| Obj::const_none())
.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 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.
let verb = verb.unwrap_or_default();
let mut confirm_btn = if verb.len() > 0 {
Some(ButtonDetails::text(verb))
} else {
@ -293,24 +284,16 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
match page_index {
0 => {
// RECIPIENT + address
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()),
None,
Some(ButtonDetails::text("CONFIRM".into())),
);
let btn_actions = ButtonActions::cancel_next();
let btn_layout = ButtonLayout::cancel_none_text("CONFIRM".into());
let btn_actions = ButtonActions::cancel_none_next();
Page::<10>::new(btn_layout, btn_actions, Font::MONO)
.with_title(address_title)
.text_mono(address)
}
1 => {
// AMOUNT + amount
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::up_arrow_icon()),
None,
Some(ButtonDetails::text("CONFIRM".into())),
);
let btn_actions = ButtonActions::cancel_confirm();
let btn_layout = ButtonLayout::up_arrow_none_text("CONFIRM".into());
let btn_actions = ButtonActions::prev_none_confirm();
Page::<10>::new(btn_layout, btn_actions, Font::MONO)
.with_title(amount_title)
.newline()
@ -341,12 +324,8 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
// Total amount + fee
assert!(page_index == 0);
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()),
None,
Some(ButtonDetails::text("HOLD TO CONFIRM".into()).with_default_duration()),
);
let btn_actions = ButtonActions::cancel_confirm();
let btn_layout = ButtonLayout::cancel_none_htc("HOLD TO CONFIRM".into());
let btn_actions = ButtonActions::cancel_none_confirm();
let mut flow_page = Page::<15>::new(btn_layout, btn_actions, Font::MONO)
.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 {
0 => {
// RECEIVE ADDRESS
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text("CONFIRM".into())),
Some(ButtonDetails::text("i".into())),
);
let btn_layout = ButtonLayout::cancel_armed_text("CONFIRM".into(), "i".into());
let btn_actions = ButtonActions::last_confirm_next();
Page::<15>::new(btn_layout, btn_actions, Font::BOLD)
.text_bold(title)
@ -400,12 +375,8 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
}
1 => {
// QR CODE
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::right_arrow_icon()),
);
let btn_actions = ButtonActions::prev_next();
let btn_layout = ButtonLayout::arrow_none_arrow();
let btn_actions = ButtonActions::prev_none_next();
Page::<15>::new(btn_layout, btn_actions, Font::MONO).qr_code(
address_qr,
theme::QR_SIDE_MAX,
@ -415,9 +386,8 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
}
2 => {
// ADDRESS INFO
let btn_layout =
ButtonLayout::new(Some(ButtonDetails::left_arrow_icon()), None, None);
let btn_actions = ButtonActions::only_prev();
let btn_layout = ButtonLayout::arrow_none_none();
let btn_actions = ButtonActions::prev_none_none();
Page::<15>::new(btn_layout, btn_actions, Font::MONO)
.text_bold("Account:".into())
.newline()
@ -429,12 +399,8 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
}
3 => {
// ADDRESS MISMATCH
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::text("QUIT".into())),
);
let btn_actions = ButtonActions::beginning_cancel();
let btn_layout = ButtonLayout::arrow_none_text("QUIT".into());
let btn_actions = ButtonActions::beginning_none_cancel();
Page::<15>::new(btn_layout, btn_actions, Font::MONO)
.text_bold("ADDRESS MISMATCH?".into())
.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) }
}
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.
/// (title, text, btn_layout, btn_actions)
fn tutorial_screen(
@ -523,47 +556,47 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen(
"HELLO".into(),
"Welcome to Trezor.\nPress right to continue.".into(),
ButtonLayout::cancel_and_arrow(),
ButtonActions::last_next(),
ButtonLayout::cancel_none_arrow(),
ButtonActions::last_none_next(),
)
},
1 => {
tutorial_screen(
"".into(),
"Use Trezor by clicking left and right.\n\nContinue right.".into(),
ButtonLayout::left_right_arrows(),
ButtonActions::prev_next(),
ButtonLayout::arrow_none_arrow(),
ButtonActions::prev_none_next(),
)
},
2 => {
tutorial_screen(
"HOLD TO CONFIRM".into(),
"Press and hold right to approve important operations.".into(),
ButtonLayout::back_and_htc_text("HOLD TO CONFIRM".into(), Duration::from_millis(1000)),
ButtonActions::prev_next(),
ButtonLayout::arrow_none_htc("HOLD TO CONFIRM".into()),
ButtonActions::prev_none_next(),
)
},
3 => {
tutorial_screen(
"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(),
ButtonLayout::back_and_text("GOT IT".into()),
ButtonActions::prev_next(),
ButtonLayout::arrow_none_text("GOT IT".into()),
ButtonActions::prev_none_next(),
)
},
4 => {
tutorial_screen(
"CONFIRM".into(),
"Press both left and right at the same time to confirm.".into(),
ButtonLayout::middle_armed_text("CONFIRM".into()),
ButtonActions::prev_next_with_middle(),
ButtonLayout::none_armed_none("CONFIRM".into()),
ButtonActions::prev_next_none(),
)
},
// This page is special
5 => {
Page::<10>::new(
ButtonLayout::left_right_text("AGAIN".into(), "FINISH".into()),
ButtonActions::beginning_confirm(),
ButtonLayout::text_none_text("AGAIN".into(), "FINISH".into()),
ButtonActions::beginning_none_confirm(),
Font::MONO,
)
.newline()
@ -577,8 +610,8 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen(
"SKIP TUTORIAL".into(),
"Are you sure you want to skip the tutorial?".into(),
ButtonLayout::cancel_and_text("SKIP".into()),
ButtonActions::beginning_cancel(),
ButtonLayout::cancel_none_text("SKIP".into()),
ButtonActions::beginning_none_cancel(),
)
},
_ => unreachable!(),
@ -773,9 +806,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def confirm_action(
/// *,
/// title: str,
/// action: str | None = None,
/// description: str | None = None,
/// verb: str | None = None,
/// action: str | None,
/// description: str | None,
/// verb: str = "CONFIRM",
/// verb_cancel: str | None = None,
/// hold: bool = False,
/// hold_danger: bool = False, # unused on TR
@ -837,6 +870,17 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Info modal."""
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:
/// """Show user how to interact with the device."""
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,
/// action: str | None,
/// description: str | None,
/// verb: str | None = None,
/// verb: str = "CONFIRM",
/// verb_cancel: str | None = None,
/// hold: bool = False,
/// hold_danger: bool = False,

View File

@ -13,9 +13,9 @@ def disable_animation(disable: bool) -> None:
def confirm_action(
*,
title: str,
action: str | None = None,
description: str | None = None,
verb: str | None = None,
action: str | None,
description: str | None,
verb: str = "CONFIRM",
verb_cancel: str | None = None,
hold: bool = False,
hold_danger: bool = False, # unused on TR
@ -82,6 +82,17 @@ def show_info(
"""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
def tutorial() -> object:
"""Show user how to interact with the device."""
@ -197,7 +208,7 @@ def confirm_action(
title: str,
action: str | None,
description: str | None,
verb: str | None = None,
verb: str = "CONFIRM",
verb_cancel: str | None = None,
hold: bool = False,
hold_danger: bool = False,

View File

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

View File

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

View File

@ -1,5 +1,12 @@
from typing import TYPE_CHECKING
from trezor.enums import ButtonRequestType
import trezorui2
from ..common import interact
from . import RustLayout
if TYPE_CHECKING:
from trezor.wire import GenericContext
@ -14,8 +21,37 @@ async def confirm_fido(
accounts: list[str | None],
) -> int:
"""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:
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True,
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True,
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True,
helpers.UiConfirmTotal(123445789 + 11000, 11000, fee_rate, coin, AmountUnit.BITCOIN),
@ -223,7 +223,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=1, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1),
True,
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),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
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)),
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0),
True,
helpers.UiConfirmFeeOverThreshold(100000, coin_bitcoin),
True,
@ -146,7 +146,7 @@ class TestSignTxFeeThreshold(unittest.TestCase):
True,
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0),
True,
helpers.UiConfirmTotal(300000 + 90000, 90000, fee_rate, coin_bitcoin, AmountUnit.BITCOIN),
True,

View File

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

View File

@ -109,7 +109,7 @@ class TestSignTxDecred(unittest.TestCase):
),
),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin_decred, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin_decred, AmountUnit.BITCOIN, 0),
True,
helpers.UiConfirmTotal(
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)),
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED),
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN),
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0),
True,
helpers.UiConfirmTotal(210016, 192, fee_rate, coin, AmountUnit.BITCOIN),
True,

View File

@ -30,9 +30,6 @@ RK_CAPACITY = 100
@pytest.mark.altcoin
@pytest.mark.setup_client(mnemonic=MNEMONIC12)
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.
with pytest.raises(TrezorFailure):
fido.remove_credential(client, 0)