chore(core/eckhart): fix pin UI deviations
What changed, and why it matters
This commit is a routine user-interface polish change for the PIN entry screen on Trezor hardware wallets. It renames an internal parameter from 'subprompt' to 'attempts', adds a new flag so the screen can visually highlight the final PIN attempt, and fixes text alignment and spacing. There is no indication this change fixes a security vulnerability or introduces one.
No security action required. Treat as normal UI refactoring/bugfix. If reviewing, verify the Eckhart PIN screen renders correctly and that the `last_attempt` flag is passed consistently across layouts.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the device-side PIN request API across four UI layouts (bolt, caesar, delizia, eckhart). It replaces the subprompt string argument with attempts, renames warning to wrong_pin, and adds a last_attempt boolean. The Eckhart layout’s PinKeyboard is updated to style the attempts label differently on the last try, correctly place the prompt and attempts text, and adjust the hidden/shown PIN digit layout (centering icons, handling multi-line shown PINs, fixing last-digit vertical placement). Other layouts mostly ignore the new flag. No cryptographic, authentication, or storage logic is changed.
Changed components
core/embed/rust/src/ui/api/firmware_micropython.rscore/embed/rust/src/ui/layout_bolt/ui_firmware.rscore/embed/rust/src/ui/layout_caesar/ui_firmware.rscore/embed/rust/src/ui/layout_delizia/ui_firmware.rscore/embed/rust/src/ui/layout_eckhart/firmware/keyboard/common.rscore/embed/rust/src/ui/layout_eckhart/firmware/keyboard/pin.rscore/embed/rust/src/ui/layout_eckhart/ui_firmware.rscore/embed/rust/src/ui/ui_firmware.rscore/mocks/generated/trezorui_api.pyicore/src/trezor/ui/layouts/bolt/__init__.pycore/src/trezor/ui/layouts/caesar/__init__.pycore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pyInspect captured patch +143 / −90
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index f3ed3045..98d6de86 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -118,6 +118,7 @@ static void _librust_qstrs(void) {
MP_QSTR_app_name;
MP_QSTR_area_bytesize;
MP_QSTR_attach_timer_fn;
+ MP_QSTR_attempts;
MP_QSTR_authenticate__confirm_template;
MP_QSTR_authenticate__header;
MP_QSTR_auto_lock__change_template;
@@ -404,6 +405,7 @@ static void _librust_qstrs(void) {
MP_QSTR_language__changed;
MP_QSTR_language__progress;
MP_QSTR_language__title;
+ MP_QSTR_last_attempt;
MP_QSTR_led__disable;
MP_QSTR_led__enable;
MP_QSTR_led__title;
@@ -808,7 +810,6 @@ static void _librust_qstrs(void) {
MP_QSTR_storage_msg__starting;
MP_QSTR_storage_msg__verifying_pin;
MP_QSTR_storage_msg__wrong_pin;
- MP_QSTR_subprompt;
MP_QSTR_subtext;
MP_QSTR_subtitle;
MP_QSTR_summary_br_code;
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index 223c2340..bf0e32d4 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -741,11 +741,12 @@ extern "C" fn new_request_duration(n_args: usize, args: *const Obj, kwargs: *mut
extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let prompt: TString = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
- let subprompt: TString = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
+ let attempts: TString = kwargs.get(Qstr::MP_QSTR_attempts)?.try_into()?;
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
- let warning: bool = kwargs.get_or(Qstr::MP_QSTR_wrong_pin, false)?;
+ let wrong_pin: bool = kwargs.get_or(Qstr::MP_QSTR_wrong_pin, false)?;
+ let last_attempt: bool = kwargs.get_or(Qstr::MP_QSTR_last_attempt, false)?;
- let layout = ModelUI::request_pin(prompt, subprompt, allow_cancel, warning)?;
+ let layout = ModelUI::request_pin(prompt, attempts, allow_cancel, wrong_pin, last_attempt)?;
Ok(LayoutObj::new_root(layout)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@@ -1775,9 +1776,10 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// def request_pin(
/// *,
/// prompt: str,
- /// subprompt: str,
+ /// attempts: str,
/// allow_cancel: bool = True,
/// wrong_pin: bool = False,
+ /// last_attempt: bool = False,
/// ) -> LayoutObj[str | UiResult]:
/// """Request pin on device."""
Qstr::MP_QSTR_request_pin => obj_fn_kw!(0, new_request_pin).as_obj(),
diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
index 3c006957..eff1b1bd 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -708,16 +708,17 @@ impl FirmwareUI for UIBolt {
fn request_pin(
prompt: TString<'static>,
- subprompt: TString<'static>,
+ attempts: TString<'static>,
allow_cancel: bool,
- warning: bool,
+ wrong_pin: bool,
+ _last_attempt: bool,
) -> Result<impl LayoutMaybeTrace, Error> {
- let warning = if warning {
+ let warning = if wrong_pin {
Some(TR::pin__wrong_pin.into())
} else {
None
};
- let layout = RootComponent::new(PinKeyboard::new(prompt, subprompt, warning, allow_cancel));
+ let layout = RootComponent::new(PinKeyboard::new(prompt, attempts, warning, allow_cancel));
Ok(layout)
}
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index c3993fa1..7d29c7d8 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -907,11 +907,12 @@ impl FirmwareUI for UICaesar {
fn request_pin(
prompt: TString<'static>,
- subprompt: TString<'static>,
+ attempts: TString<'static>,
_allow_cancel: bool,
- _warning: bool,
+ _wrong_pin: bool,
+ _last_attempt: bool,
) -> Result<impl LayoutMaybeTrace, Error> {
- let layout = RootComponent::new(PinEntry::new(prompt, subprompt));
+ let layout = RootComponent::new(PinEntry::new(prompt, attempts));
Ok(layout)
}
diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
index e8b429a6..321bec28 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -799,17 +799,18 @@ impl FirmwareUI for UIDelizia {
fn request_pin(
prompt: TString<'static>,
- subprompt: TString<'static>,
+ attempts: TString<'static>,
allow_cancel: bool,
- warning: bool,
+ wrong_pin: bool,
+ _last_attempt: bool,
) -> Result<impl LayoutMaybeTrace, Error> {
- let warning = if warning {
+ let warning = if wrong_pin {
Some(TR::pin__wrong_pin.into())
} else {
None
};
- let layout = RootComponent::new(PinKeyboard::new(prompt, subprompt, warning, allow_cancel));
+ let layout = RootComponent::new(PinKeyboard::new(prompt, attempts, warning, allow_cancel));
Ok(layout)
}
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/common.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/common.rs
index 0dc127f5..2f98bdab 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/common.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/common.rs
@@ -190,13 +190,28 @@ pub const KEYPAD_VISIBLE_HEIGHT: i16 = 440;
pub const INPUT_TOUCH_HEIGHT: i16 = 96;
const TEXTBOX_HEIGHT: i16 = 72;
-const INPUT_SIDE_PADDING: i16 = 24;
const INPUT_TOP_PADDING: i16 = 16;
+const PROMPT_HEIGHT: i16 = 44;
+const PROMPT_TOP_PADDING: i16 = 35;
+const PROMPT_RIGHT_PADDING: i16 = 38;
pub const KEYBOARD_INPUT_RADIUS: i16 = 12;
pub const KEYBOARD_INPUT_INSETS: Insets = Insets::new(
INPUT_TOP_PADDING,
- INPUT_SIDE_PADDING,
+ theme::PADDING,
INPUT_TOUCH_HEIGHT - INPUT_TOP_PADDING - TEXTBOX_HEIGHT,
- INPUT_SIDE_PADDING,
+ theme::PADDING,
+);
+pub const KEYBOARD_PROMPT_INSETS: Insets = Insets::new(
+ PROMPT_TOP_PADDING,
+ PROMPT_RIGHT_PADDING,
+ INPUT_TOUCH_HEIGHT - PROMPT_TOP_PADDING - PROMPT_HEIGHT,
+ theme::PADDING,
+);
+
+pub const SHOWN_INSETS: Insets = Insets::new(
+ INPUT_TOP_PADDING,
+ theme::PADDING,
+ INPUT_TOP_PADDING,
+ theme::PADDING,
);
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/pin.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/pin.rs
index 42c2b217..ac7cf3d4 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/pin.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/keyboard/pin.rs
@@ -4,8 +4,8 @@ use crate::{
ui::{
component::{
text::{
- layout::{Chunks, LayoutFit, LineBreaking},
- TextStyle,
+ layout::{Chunks, LayoutFit},
+ LineBreaking, TextStyle,
},
Component, Event, EventCtx, Label, TextLayout, Timer,
},
@@ -21,7 +21,7 @@ use super::{
super::super::{component::ButtonContent, constant::SCREEN, theme},
common::{
FADING_ICON_COLORS, FADING_ICON_COUNT, INPUT_TOUCH_HEIGHT, KEYBOARD_INPUT_INSETS,
- KEYBOARD_INPUT_RADIUS, KEYPAD_VISIBLE_HEIGHT,
+ KEYBOARD_INPUT_RADIUS, KEYBOARD_PROMPT_INSETS, KEYPAD_VISIBLE_HEIGHT, SHOWN_INSETS,
},
keypad::{ButtonState, Keypad, KeypadMsg, KeypadState},
};
@@ -32,10 +32,11 @@ pub enum PinKeyboardMsg {
}
pub struct PinKeyboard<'a> {
+ prompt: Label<'a>,
+ attempts: Label<'a>,
+ warning: Option<Label<'a>>,
allow_cancel: bool,
- major_prompt: Label<'a>,
- minor_prompt: Label<'a>,
- major_warning: Option<Label<'a>>,
+ last_attempt: bool,
keypad: Keypad,
input: PinInput,
warning_timer: Timer,
@@ -43,22 +44,32 @@ pub struct PinKeyboard<'a> {
impl<'a> PinKeyboard<'a> {
const LAST_DIGIT_TIMEOUT: Duration = Duration::from_secs(1);
+ const MAJOR_WARNING_TIMEOUT: Duration = Duration::from_secs(2);
+ // Ad hoc number that so that all languages can reasonably show the attempts
+ // prompt
+ const ATTEMPTS_WIDTH: i16 = 85;
pub fn new(
- major_prompt: TString<'a>,
- minor_prompt: TString<'a>,
- major_warning: Option<TString<'a>>,
+ prompt: TString<'a>,
+ attempts: TString<'a>,
+ warning: Option<TString<'a>>,
allow_cancel: bool,
+ last_attempt: bool,
) -> Self {
+ let attempts_style = if last_attempt {
+ theme::label_title_warning()
+ } else {
+ theme::TEXT_SMALL_LIGHT
+ }
+ .with_line_breaking(LineBreaking::BreakAtWhitespace);
Self {
- allow_cancel,
- major_prompt: Label::left_aligned(major_prompt, theme::firmware::TEXT_SMALL)
- .vertically_centered(),
- minor_prompt: Label::right_aligned(minor_prompt, theme::firmware::TEXT_SMALL)
- .vertically_centered(),
- major_warning: major_warning.map(|text| {
+ prompt: Label::left_aligned(prompt, theme::firmware::TEXT_SMALL).vertically_centered(),
+ attempts: Label::centered(attempts, attempts_style).vertically_centered(),
+ warning: warning.map(|text| {
Label::left_aligned(text, theme::firmware::TEXT_SMALL).vertically_centered()
}),
+ allow_cancel,
+ last_attempt,
input: PinInput::new(),
keypad: Keypad::new_numeric(true),
warning_timer: Timer::new(),
@@ -135,15 +146,15 @@ impl Component for PinKeyboard<'_> {
let (_, keypad_area) = bounds.split_bottom(KEYPAD_VISIBLE_HEIGHT);
let (input_touch_area, _) = bounds.split_top(INPUT_TOUCH_HEIGHT);
+ let prompts_area = input_touch_area.inset(KEYBOARD_PROMPT_INSETS);
+ let (prompt_area, attempts_area) = prompts_area.split_right(Self::ATTEMPTS_WIDTH);
+
// Prompts and PIN dots placement.
self.input.place(input_touch_area);
- self.major_prompt
- .place(input_touch_area.inset(KEYBOARD_INPUT_INSETS));
- self.minor_prompt
- .place(input_touch_area.inset(KEYBOARD_INPUT_INSETS));
- self.major_warning
- .as_mut()
- .map(|c| c.place(input_touch_area.inset(KEYBOARD_INPUT_INSETS)));
+ self.prompt.place(prompt_area);
+ // Remaining tries prompt
+ self.attempts.place(attempts_area);
+ self.warning.place(prompt_area);
// Keypad placement
self.keypad.place(keypad_area);
@@ -155,15 +166,15 @@ impl Component for PinKeyboard<'_> {
match event {
// Set up timer to switch off warning prompt.
Event::Attach(_) => {
- if self.major_warning.is_some() {
- self.warning_timer.start(ctx, Duration::from_secs(2));
+ if self.warning.is_some() {
+ self.warning_timer.start(ctx, Self::MAJOR_WARNING_TIMEOUT);
}
// Update the keypad state in the first event
self.update_keypad_state(ctx);
}
// Hide warning, show major prompt.
Event::Timer(_) if self.warning_timer.expire(event) => {
- self.major_warning = None;
+ self.warning = None;
}
_ => {}
@@ -229,12 +240,12 @@ impl Component for PinKeyboard<'_> {
// Render prompt when the pin is empty
if empty {
- if let Some(ref w) = self.major_warning {
+ if let Some(ref w) = self.warning {
w.render(target);
} else {
- self.major_prompt.render(target);
+ self.prompt.render(target);
}
- self.minor_prompt.render(target);
+ self.attempts.render(target);
}
// When the entire pin is shown, the input area might overlap the keypad so it
@@ -275,14 +286,13 @@ impl PinInput {
const MAX_SHOWN_LEN: usize = 19; // max number of icons per line
const TWITCH: i16 = 4;
- const SHOWN_INSETS: Insets = Insets::new(12, 24, 12, 24);
const SHOWN_STYLE: TextStyle = theme::TEXT_REGULAR
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
.with_chunks(Chunks::new(1, 8));
+ const HIDDEN_STYLE: TextStyle = theme::TEXT_REGULAR;
const SHOWN_TOUCH_OUTSET: Insets = Insets::bottom(200);
const PIN_ICON: Icon = theme::ICON_DASH_VERTICAL;
- const ICON_WIDTH: i16 = Self::PIN_ICON.toif.width();
- const ICON_SPACE: i16 = 12;
+ const ICON_SPACING: i16 = 12;
fn new() -> Self {
Self {
@@ -294,11 +304,12 @@ impl PinInput {
}
}
- fn size(&self) -> Offset {
+ fn width(&self) -> i16 {
let ndots = self.pin().len().min(Self::MAX_SHOWN_LEN);
- let mut width = Self::ICON_WIDTH * (ndots as i16);
- width += Self::ICON_SPACE * (ndots.saturating_sub(1) as i16);
- Offset::new(width, 6)
+ let mut width = Self::PIN_ICON.toif.width() * (ndots as i16);
+ // the last digit is wider than the icon so we count one extra space as well
+ width += Self::ICON_SPACING * (ndots as i16);
+ width
}
fn is_empty(&self) -> bool {
@@ -334,7 +345,7 @@ impl PinInput {
// Extend the shown area until the text fits
while let LayoutFit::OutOfBounds { .. } = TextLayout::new(Self::SHOWN_STYLE)
.with_align(Alignment::Start)
- .with_bounds(shown_area.inset(Self::SHOWN_INSETS))
+ .with_bounds(shown_area.inset(SHOWN_INSETS))
.fit_text(self.pin())
{
shown_area =
@@ -348,14 +359,24 @@ impl PinInput {
// Make sure the pin should be shown
debug_assert_eq!(self.display_style, DisplayStyle::Shown);
+ let base_shown_area = self.area.inset(KEYBOARD_INPUT_INSETS);
+ let multiline_pin = self.shown_area.height() > base_shown_area.height();
+ let alignment = if multiline_pin {
+ // Multi-line pin is left aligned
+ Alignment::Start
+ } else {
+ // FIXME: because of #5623, the chunkified PINs cannot be centered
+ Alignment::Start
+ };
+
Bar::new(self.shown_area)
.with_bg(theme::GREY_SUPER_DARK)
.with_radius(KEYBOARD_INPUT_RADIUS)
.render(target);
TextLayout::new(Self::SHOWN_STYLE)
- .with_bounds(self.shown_area.inset(Self::SHOWN_INSETS))
- .with_align(Alignment::Start)
+ .with_bounds(self.shown_area.inset(SHOWN_INSETS))
+ .with_align(alignment)
.render_text(self.pin(), target, true);
}
@@ -363,11 +384,10 @@ impl PinInput {
debug_assert_ne!(self.display_style, DisplayStyle::Shown);
let hidden_area: Rect = self.area.inset(KEYBOARD_INPUT_INSETS);
- let style = theme::TEXT_REGULAR;
let pin_len = self.pin().len();
let last_digit = self.display_style == DisplayStyle::LastOnly;
- let mut cursor = self.size().snap(hidden_area.center(), Alignment2D::CENTER);
+ let mut cursor = hidden_area.center().ofs(Offset::x(self.width() / 2).neg());
// Render only when there are characters
if pin_len == 0 {
@@ -389,10 +409,10 @@ impl PinInput {
for (i, &fg_color) in FADING_ICON_COLORS.iter().enumerate() {
if pin_len > visible_len + (FADING_ICON_COUNT - 1 - i) {
ToifImage::new(cursor, Self::PIN_ICON.toif)
- .with_align(Alignment2D::TOP_LEFT)
+ .with_align(Alignment2D::CENTER_LEFT)
.with_fg(fg_color)
.render(target);
- cursor.x += Self::ICON_SPACE + Self::ICON_WIDTH;
+ cursor.x += Self::ICON_SPACING + Self::PIN_ICON.toif.width();
char_idx += 1;
}
}
@@ -401,10 +421,10 @@ impl PinInput {
// Classical icons
for _ in char_idx..visible_icons {
ToifImage::new(cursor, Self::PIN_ICON.toif)
- .with_align(Alignment2D::TOP_LEFT)
- .with_fg(style.text_color)
+ .with_align(Alignment2D::CENTER_LEFT)
+ .with_fg(Self::HIDDEN_STYLE.text_color)
.render(target);
- cursor.x += Self::ICON_SPACE + Self::ICON_WIDTH;
+ cursor.x += Self::ICON_SPACING + Self::PIN_ICON.toif.width();
}
}
@@ -412,14 +432,13 @@ impl PinInput {
// This should not fail because pin_len > 0
let last = &self.digits.as_str()[(pin_len - 1)..pin_len];
- // Adapt x and y positions for the character
- cursor.y = hidden_area.left_center().y + style.text_font.allcase_text_height() / 2;
- cursor.x -= style.text_font.text_width(last) / 2 - Self::ICON_WIDTH / 2;
+ // Adapt y position for the character
+ cursor.y += Self::HIDDEN_STYLE.text_font.visible_text_height("1") / 2;
// Paint the last character
- Text::new(cursor, last, style.text_font)
+ Text::new(cursor, last, Self::HIDDEN_STYLE.text_font)
.with_align(Alignment::Start)
- .with_fg(style.text_color)
+ .with_fg(Self::HIDDEN_STYLE.text_color)
.render(target);
}
}
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
index 18a7e846..b1c2d427 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -946,17 +946,24 @@ impl FirmwareUI for UIEckhart {
fn request_pin(
prompt: TString<'static>,
- subprompt: TString<'static>,
+ attempts: TString<'static>,
allow_cancel: bool,
- warning: bool,
+ wrong_pin: bool,
+ last_attempt: bool,
) -> Result<impl LayoutMaybeTrace, Error> {
- let warning = if warning {
+ let warning = if wrong_pin {
Some(TR::pin__wrong_pin.into())
} else {
None
};
- let layout = RootComponent::new(PinKeyboard::new(prompt, subprompt, warning, allow_cancel));
+ let layout = RootComponent::new(PinKeyboard::new(
+ prompt,
+ attempts,
+ warning,
+ allow_cancel,
+ last_attempt,
+ ));
Ok(layout)
}
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index ef7f87ad..833344f2 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -285,6 +285,7 @@ pub trait FirmwareUI {
subprompt: TString<'static>,
allow_cancel: bool,
warning: bool,
+ last_attempt: bool,
) -> Result<impl LayoutMaybeTrace, Error>;
fn request_passphrase(
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index ed51c684..d72a8282 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -486,9 +486,10 @@ def request_duration(
def request_pin(
*,
prompt: str,
- subprompt: str,
+ attempts: str,
allow_cancel: bool = True,
wrong_pin: bool = False,
+ last_attempt: bool = False,
) -> LayoutObj[str | UiResult]:
"""Request pin on device."""
diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py
index 176f2dac..c9692ead 100644
--- a/core/src/trezor/ui/layouts/bolt/__init__.py
+++ b/core/src/trezor/ui/layouts/bolt/__init__.py
@@ -1737,16 +1737,16 @@ async def request_pin_on_device(
from trezor.wire import PinCancelled
if attempts_remaining is None:
- subprompt = ""
+ attempts = ""
elif attempts_remaining == 1:
- subprompt = TR.pin__last_attempt
+ attempts = TR.pin__last_attempt
else:
- subprompt = f"{attempts_remaining} {TR.pin__tries_left}"
+ attempts = f"{attempts_remaining} {TR.pin__tries_left}"
result = await interact(
trezorui_api.request_pin(
prompt=prompt,
- subprompt=subprompt,
+ attempts=attempts,
allow_cancel=allow_cancel,
wrong_pin=wrong_pin,
),
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index 7cf396fe..91780ae0 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -1739,16 +1739,16 @@ async def request_pin_on_device(
# Not showing the prompt in case user did not enter it badly yet
# (has full 16 attempts left)
if attempts_remaining is None or attempts_remaining == 16:
- subprompt = ""
+ attempts = ""
elif attempts_remaining == 1:
- subprompt = TR.pin__last_attempt
+ attempts = TR.pin__last_attempt
else:
- subprompt = f"{attempts_remaining} {TR.pin__tries_left}"
+ attempts = f"{attempts_remaining} {TR.pin__tries_left}"
result = await interact(
trezorui_api.request_pin(
prompt=prompt,
- subprompt=subprompt,
+ attempts=attempts,
allow_cancel=allow_cancel,
wrong_pin=wrong_pin,
),
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index c61afb4c..772a26e6 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -1659,16 +1659,16 @@ def request_pin_on_device(
from trezor.wire import PinCancelled
if attempts_remaining is None:
- subprompt = ""
+ attempts = ""
elif attempts_remaining == 1:
- subprompt = TR.pin__last_attempt
+ attempts = TR.pin__last_attempt
else:
- subprompt = f"{attempts_remaining} {TR.pin__tries_left}"
+ attempts = f"{attempts_remaining} {TR.pin__tries_left}"
result = interact(
trezorui_api.request_pin(
prompt=prompt,
- subprompt=subprompt,
+ attempts=attempts,
allow_cancel=allow_cancel,
wrong_pin=wrong_pin,
),
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index 630314dd..0b52c27c 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -1722,18 +1722,22 @@ def request_pin_on_device(
from trezor.wire import PinCancelled
if attempts_remaining is None:
- subprompt = ""
+ attempts = ""
+ last_attempt = False
elif attempts_remaining == 1:
- subprompt = TR.pin__last_attempt
+ attempts = TR.pin__last_attempt
+ last_attempt = True
else:
- subprompt = f"{attempts_remaining} {TR.pin__tries_left}"
+ attempts = f"{attempts_remaining}\n{TR.pin__tries_left}"
+ last_attempt = False
result = interact(
trezorui_api.request_pin(
prompt=prompt,
- subprompt=subprompt,
+ attempts=attempts,
allow_cancel=allow_cancel,
wrong_pin=wrong_pin,
+ last_attempt=last_attempt,
),
"pin_device",
ButtonRequestType.PinEntry,
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.