Skip to content

Commit

Permalink
feat(core): confirm blob with optional pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
ibz committed Oct 18, 2024
1 parent f1e01ed commit cb3e437
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 56 deletions.
1 change: 1 addition & 0 deletions core/embed/rust/librust_qstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ static void _librust_qstrs(void) {
MP_QSTR_notification;
MP_QSTR_notification_level;
MP_QSTR_page_count;
MP_QSTR_page_limit;
MP_QSTR_pages;
MP_QSTR_paint;
MP_QSTR_passphrase__access_wallet;
Expand Down
11 changes: 11 additions & 0 deletions core/embed/rust/src/ui/flow/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct SwipePage<T> {
axis: Axis,
pages: usize,
current: usize,
limit: Option<usize>,
}

impl<T: Component + Paginate> SwipePage<T> {
Expand All @@ -23,6 +24,7 @@ impl<T: Component + Paginate> SwipePage<T> {
axis: Axis::Vertical,
pages: 1,
current: 0,
limit: None,
}
}

Expand All @@ -33,12 +35,18 @@ impl<T: Component + Paginate> SwipePage<T> {
axis: Axis::Horizontal,
pages: 1,
current: 0,
limit: None,
}
}

pub fn inner(&self) -> &T {
&self.inner
}

pub fn with_limit(mut self, limit: Option<usize>) -> Self {
self.limit = limit;
self
}
}

impl<T: Component + Paginate> Component for SwipePage<T> {
Expand All @@ -47,6 +55,9 @@ impl<T: Component + Paginate> Component for SwipePage<T> {
fn place(&mut self, bounds: Rect) -> Rect {
self.bounds = self.inner.place(bounds);
self.pages = self.inner.page_count();
if let Some(limit) = self.limit {
self.pages = self.pages.min(limit);
}
self.bounds
}

Expand Down
4 changes: 3 additions & 1 deletion core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ fn new_confirm_action_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
prompt_screen.then_some(prompt_title),
hold,
false,
None,
)
}

Expand Down Expand Up @@ -310,9 +311,10 @@ pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>
prompt_screen: Option<TString<'static>>,
hold: bool,
info: bool,
page_limit: Option<usize>,
) -> Result<Obj, error::Error> {
new_confirm_action_uni(
SwipeContent::new(SwipePage::vertical(content)),
SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)),
title,
subtitle,
verb_cancel,
Expand Down
18 changes: 17 additions & 1 deletion core/embed/rust/src/ui/model_mercury/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m
Some(title),
false,
false,
None,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
Expand All @@ -263,6 +264,7 @@ struct ConfirmBlobParams {
hold: bool,
chunkify: bool,
text_mono: bool,
page_limit: Option<usize>,
}

impl ConfirmBlobParams {
Expand All @@ -288,6 +290,7 @@ impl ConfirmBlobParams {
hold,
chunkify: false,
text_mono: true,
page_limit: None,
}
}

Expand Down Expand Up @@ -316,6 +319,11 @@ impl ConfirmBlobParams {
self
}

fn with_page_limit(mut self, page_limit: Option<usize>) -> Self {
self.page_limit = page_limit;
self
}

fn into_flow(self) -> Result<Obj, Error> {
let paragraphs = ConfirmBlob {
description: self.description.unwrap_or("".into()),
Expand All @@ -342,6 +350,7 @@ impl ConfirmBlobParams {
self.prompt.then_some(self.title),
self.hold,
self.info_button,
self.page_limit,
)
}
}
Expand All @@ -364,6 +373,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
let prompt_screen: bool = kwargs.get_or(Qstr::MP_QSTR_prompt_screen, true)?;
let page_limit: Option<usize> = kwargs.get(Qstr::MP_QSTR_page_limit)?.try_into_option()?;

ConfirmBlobParams::new(
title,
Expand All @@ -375,7 +385,9 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
hold,
)
.with_extra(extra)
.with_info_button(true)
.with_chunkify(chunkify)
.with_page_limit(page_limit)
.into_flow()
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
Expand Down Expand Up @@ -407,7 +419,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
}
.into_paragraphs();

flow::new_confirm_action_simple(paragraphs, title, None, None, None, false, false)
flow::new_confirm_action_simple(paragraphs, title, None, None, None, false, false, None)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
Expand All @@ -433,6 +445,7 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
hold.then_some(title),
hold,
false,
None,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
Expand Down Expand Up @@ -462,6 +475,7 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
Some(TR::homescreen__settings_title.into()),
false,
false,
None,
)
} else {
if !check_homescreen_format(jpeg) {
Expand Down Expand Up @@ -570,6 +584,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
Some(title),
true,
true,
None,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
Expand Down Expand Up @@ -859,6 +874,7 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
Some(TR::coinjoin__title.into()),
true,
false,
None,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
Expand Down
6 changes: 3 additions & 3 deletions core/src/apps/ethereum/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from trezor.enums import ButtonRequestType
from trezor.ui.layouts import (
confirm_blob,
confirm_blob_with_optional_pagination,
confirm_ethereum_staking_tx,
confirm_text,
should_show_more,
Expand Down Expand Up @@ -174,13 +175,13 @@ def require_confirm_address(address_bytes: bytes) -> Awaitable[None]:


def require_confirm_other_data(data: bytes, data_total: int) -> Awaitable[None]:
return confirm_blob(
return confirm_blob_with_optional_pagination(
"confirm_data",
TR.ethereum__title_confirm_data,
data,
TR.ethereum__data_size_template.format(data_total),
br_code=ButtonRequestType.SignTx,
ask_pagination=True,
prompt_screen=False,
)


Expand Down Expand Up @@ -301,7 +302,6 @@ async def confirm_typed_value(
title,
data,
description,
ask_pagination=True,
)
else:
await confirm_text(
Expand Down
8 changes: 4 additions & 4 deletions core/src/apps/ethereum/sign_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async def sign_tx(
raise DataError("Fee overflow")
check_common_fields(msg)

# have a user confirm signing
# have the user confirm signing
await paths.validate_path(keychain, msg.address_n)
address_bytes = bytes_from_address(msg.to)
gas_price = int.from_bytes(msg.gas_price, "big")
Expand Down Expand Up @@ -130,9 +130,9 @@ async def confirm_tx_data(
return

# Handle ERC-20, currently only 'transfer' function
token, recipient, value = await handle_erc20_transfer(msg, defs, address_bytes)
token, recipient, value = await _handle_erc20_transfer(msg, defs, address_bytes)

if token is None and data_total_len > 0:
if data_total_len > 0:
await require_confirm_other_data(msg.data_initial_chunk, data_total_len)

await require_confirm_tx(
Expand Down Expand Up @@ -189,7 +189,7 @@ async def handle_staking(
return False


async def handle_erc20_transfer(
async def _handle_erc20_transfer(
msg: MsgInSignTx,
definitions: Definitions,
address_bytes: bytes,
Expand Down
99 changes: 52 additions & 47 deletions core/src/trezor/ui/layouts/mercury/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,43 +750,54 @@ async def should_show_more(
raise ActionCancelled


async def _confirm_ask_pagination(
async def confirm_blob_with_optional_pagination(
br_name: str,
title: str,
data: bytes | str,
description: str,
br_code: ButtonRequestType,
) -> None:
paginated: ui.Layout | None = None
# TODO: make should_show_more/confirm_more accept bytes directly
if isinstance(data, bytes):
from ubinascii import hexlify

data = hexlify(data).decode()
while True:
if not await should_show_more(
title,
para=[(ui.NORMAL, description), (ui.MONO, data)],
description: str | None = None,
verb: str | None = None,
verb_cancel: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = BR_CODE_OTHER,
chunkify: bool = False,
prompt_screen: bool = True,
) -> Awaitable[None]:
# show first page first
layout = RustLayout(
trezorui2.confirm_blob(
title=title,
data=data,
description=description,
extra=None,
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
chunkify=chunkify,
prompt_screen=prompt_screen,
page_limit=1,
)
)
result = await interact(
layout,
br_name,
br_code,
)
if result is INFO:
# user requested to view the whole blob
return await confirm_blob(
br_name=br_name,
title=title,
data=data,
description=description,
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
br_code=br_code,
):
return

if paginated is None:
paginated = RustLayout(
trezorui2.confirm_more(
title=title,
button=TR.buttons__close,
items=[(ui.MONO, data)],
)
)
else:
paginated.request_complete_repaint()

result = await interact(paginated, br_name, br_code)
assert result in (CONFIRMED, CANCELLED)

assert False
chunkify=chunkify,
prompt_screen=prompt_screen,
)
elif result is not CONFIRMED:
raise ActionCancelled


def confirm_blob(
Expand All @@ -798,36 +809,30 @@ def confirm_blob(
verb_cancel: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = BR_CODE_OTHER,
ask_pagination: bool = False,
chunkify: bool = False,
prompt_screen: bool = True,
) -> Awaitable[None]:
layout = RustLayout(
trezorui2.confirm_blob(
title=title,
description=description,
data=data,
description=description,
extra=None,
hold=hold,
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
chunkify=chunkify,
prompt_screen=prompt_screen,
page_limit=None,
)
)

if ask_pagination and layout.page_count() > 1:
assert not hold
return _confirm_ask_pagination(br_name, title, data, description or "", br_code)

else:
return raise_if_not_confirmed(
interact(
layout,
br_name,
br_code,
)
return raise_if_not_confirmed(
interact(
layout,
br_name,
br_code,
)
)


def confirm_address(
Expand Down

0 comments on commit cb3e437

Please sign in to comment.