From 611d4edc32c2546dab6f08350d8b7a199afb0503 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Mon, 22 May 2023 21:07:18 +0200 Subject: [PATCH] fix(core/ui): clarify transaction replacement screens [no changelog] --- core/embed/rust/librust_qstr.h | 1 + core/embed/rust/src/ui/model_tt/layout.rs | 77 +++++----- core/mocks/generated/trezorui2.pyi | 7 +- core/src/apps/bitcoin/sign_tx/approvers.py | 24 +-- core/src/apps/bitcoin/sign_tx/helpers.py | 13 +- core/src/apps/bitcoin/sign_tx/layout.py | 6 +- core/src/trezor/ui/layouts/tt_v2/__init__.py | 140 +++++++++++------- core/src/trezor/ui/layouts/tt_v2/reset.py | 1 + tests/device_tests/bitcoin/test_signtx.py | 74 ++++++++- .../bitcoin/test_signtx_replacement.py | 4 +- tests/ui_tests/fixtures.json | 91 ++++++------ 11 files changed, 283 insertions(+), 155 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 807e31fda..59dd11851 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -61,6 +61,7 @@ static void _librust_qstrs(void) { MP_QSTR_fee_label; MP_QSTR_fee_rate; MP_QSTR_fee_rate_amount; + MP_QSTR_fee_rate_title; MP_QSTR_hold; MP_QSTR_hold_danger; MP_QSTR_icon_name; diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index c57fb7d38..5e568b31e 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -715,25 +715,29 @@ extern "C" fn new_show_address_details(n_args: usize, args: *const Obj, kwargs: extern "C" fn new_show_spending_details(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { - let account: StrBuffer = kwargs.get(Qstr::MP_QSTR_account)?.try_into()?; + let title: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_title, "INFORMATION".into())?; + let account: Option = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?; let fee_rate: Option = kwargs.get(Qstr::MP_QSTR_fee_rate)?.try_into_option()?; + let fee_rate_title: StrBuffer = + kwargs.get_or(Qstr::MP_QSTR_fee_rate_title, "Fee rate:".into())?; let mut paragraphs = ParagraphVecShort::new(); - paragraphs.add(Paragraph::new( - &theme::TEXT_NORMAL, - "Sending from account:".into(), - )); - paragraphs.add(Paragraph::new(&theme::TEXT_MONO, account)); - + if let Some(a) = account { + paragraphs.add(Paragraph::new( + &theme::TEXT_NORMAL, + "Sending from account:".into(), + )); + paragraphs.add(Paragraph::new(&theme::TEXT_MONO, a)); + } if let Some(f) = fee_rate { - paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, "Fee rate:".into())); + paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, fee_rate_title)); paragraphs.add(Paragraph::new(&theme::TEXT_MONO, f)); } let obj = LayoutObj::new( Frame::left_aligned( theme::label_title(), - "INFORMATION", + title, SwipePage::new(paragraphs.into_paragraphs(), Empty, theme::BG).with_swipe_right(), ) .with_cancel_button(), @@ -750,7 +754,7 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; let value: Obj = kwargs.get(Qstr::MP_QSTR_value)?; - let info_button: bool = unwrap!(kwargs.get_or(Qstr::MP_QSTR_info_button, false)); + let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?; let verb: Option = kwargs .get(Qstr::MP_QSTR_verb) @@ -774,7 +778,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma let block = move |_args: &[Obj], kwargs: &Map| { let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false).unwrap(); + let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?; let mut paragraphs = ParagraphVecShort::new(); @@ -802,7 +806,6 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { - let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?; let sign: i32 = kwargs.get(Qstr::MP_QSTR_sign)?.try_into()?; let amount_change: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_change)?.try_into()?; let amount_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_new)?.try_into()?; @@ -814,24 +817,17 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: }; let paragraphs = Paragraphs::new([ - Paragraph::new(&theme::TEXT_NORMAL, "Address:".into()), - Paragraph::new(&theme::TEXT_MONO, address).break_after(), Paragraph::new(&theme::TEXT_NORMAL, description.into()), Paragraph::new(&theme::TEXT_MONO, amount_change), Paragraph::new(&theme::TEXT_NORMAL, "New amount:".into()), Paragraph::new(&theme::TEXT_MONO, amount_new), ]); - let buttons = Button::cancel_confirm( - Button::with_icon(theme::ICON_CANCEL), - Button::with_text("CONFIRM").styled(theme::button_confirm()), - true, - ); - + let buttons = Button::cancel_confirm_text(Some("^"), Some("CONTINUE")); let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), "MODIFY AMOUNT", - SwipePage::new(paragraphs, buttons, theme::BG).with_cancel_on_first_page(), + SwipePage::new(paragraphs, buttons, theme::BG), ))?; Ok(obj.into()) }; @@ -840,32 +836,36 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { + let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let sign: i32 = kwargs.get(Qstr::MP_QSTR_sign)?.try_into()?; let user_fee_change: StrBuffer = kwargs.get(Qstr::MP_QSTR_user_fee_change)?.try_into()?; let total_fee_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_fee_new)?.try_into()?; - // TODO: use this, most probably in "i" icon - let _fee_rate_amount: Option = kwargs - .get(Qstr::MP_QSTR_fee_rate_amount)? - .try_into_option()?; - let (description, change) = match sign { - s if s < 0 => ("Decrease your fee by:", user_fee_change), - s if s > 0 => ("Increase your fee by:", user_fee_change), - _ => ("Your fee did not change.", StrBuffer::empty()), + let (description, change, total_label) = match sign { + s if s < 0 => ("Decrease fee by:", user_fee_change, "New transaction fee:"), + s if s > 0 => ("Increase fee by:", user_fee_change, "New transaction fee:"), + _ => ( + "Fee did not change.\r", + StrBuffer::empty(), + "Transaction fee:", + ), }; let paragraphs = Paragraphs::new([ Paragraph::new(&theme::TEXT_NORMAL, description.into()), Paragraph::new(&theme::TEXT_MONO, change), - Paragraph::new(&theme::TEXT_NORMAL, "\nTransaction fee:".into()), + Paragraph::new(&theme::TEXT_NORMAL, total_label.into()), Paragraph::new(&theme::TEXT_MONO, total_fee_new), ]); - let obj = LayoutObj::new(Frame::left_aligned( - theme::label_title(), - "MODIFY FEE", - SwipeHoldPage::new(paragraphs, theme::BG), - ))?; + let obj = LayoutObj::new( + Frame::left_aligned( + theme::label_title(), + title, + SwipeHoldPage::new(paragraphs, theme::BG).with_swipe_left(), + ) + .with_info_button(), + )?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1660,8 +1660,10 @@ pub static mp_module_trezorui2: Module = obj_module! { /// def show_spending_details( /// *, - /// account: str, + /// title: str = "INFORMATION", + /// account: str | None, /// fee_rate: str | None, + /// fee_rate_title: str = "Fee rate:", /// ) -> object: /// """Show metadata when for outgoing transaction.""" Qstr::MP_QSTR_show_spending_details => obj_fn_kw!(0, new_show_spending_details).as_obj(), @@ -1691,7 +1693,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// def confirm_modify_output( /// *, - /// address: str, /// sign: int, /// amount_change: str, /// amount_new: str, @@ -1701,10 +1702,10 @@ pub static mp_module_trezorui2: Module = obj_module! { /// def confirm_modify_fee( /// *, + /// title: str, /// sign: int, /// user_fee_change: str, /// total_fee_new: str, - /// fee_rate_amount: str | None, /// ) -> object: /// """Decrease or increase transaction fee.""" Qstr::MP_QSTR_confirm_modify_fee => obj_fn_kw!(0, new_confirm_modify_fee).as_obj(), diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 5c90456ab..94e9eef00 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -469,8 +469,10 @@ def show_address_details( # rust/src/ui/model_tt/layout.rs def show_spending_details( *, - account: str, + title: str = "INFORMATION", + account: str | None, fee_rate: str | None, + fee_rate_title: str = "Fee rate:", ) -> object: """Show metadata when for outgoing transaction.""" @@ -503,7 +505,6 @@ def confirm_total( # rust/src/ui/model_tt/layout.rs def confirm_modify_output( *, - address: str, sign: int, amount_change: str, amount_new: str, @@ -514,10 +515,10 @@ def confirm_modify_output( # rust/src/ui/model_tt/layout.rs def confirm_modify_fee( *, + title: str, sign: int, user_fee_change: str, total_fee_new: str, - fee_rate_amount: str | None, ) -> object: """Decrease or increase transaction fee.""" diff --git a/core/src/apps/bitcoin/sign_tx/approvers.py b/core/src/apps/bitcoin/sign_tx/approvers.py index a65db4a8e..bbe601673 100644 --- a/core/src/apps/bitcoin/sign_tx/approvers.py +++ b/core/src/apps/bitcoin/sign_tx/approvers.py @@ -245,19 +245,23 @@ class BasicApprover(Approver): if not orig_txs: return + title = self._replacement_title(tx_info, orig_txs) + for orig in orig_txs: + await helpers.confirm_replacement(title, orig.orig_hash) + + def _replacement_title( + self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo] + ) -> str: if self.is_payjoin(): - description = "PayJoin" + return "PayJoin" elif tx_info.rbf_disabled() and any( not orig.rbf_disabled() for orig in orig_txs ): - description = "Finalize transaction" + return "Finalize transaction" elif len(orig_txs) > 1: - description = "Meld transactions" + return "Meld transactions" else: - description = "Update transaction" - - for orig in orig_txs: - await helpers.confirm_replacement(description, orig.orig_hash) + return "Update transaction" async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None: from trezor.wire import NotEnoughFunds @@ -322,18 +326,20 @@ class BasicApprover(Approver): ) if not self.is_payjoin(): + title = self._replacement_title(tx_info, orig_txs) # Not a PayJoin: Show the actual fee difference, since any difference in the fee is # coming entirely from the user's own funds and from decreases of external outputs. # We consider the decreases as belonging to the user. await helpers.confirm_modify_fee( - fee - orig_fee, fee, fee_rate, coin, amount_unit + title, fee - orig_fee, fee, fee_rate, coin, amount_unit ) elif spending > orig_spending: + title = self._replacement_title(tx_info, orig_txs) # PayJoin and user is spending more: Show the increase in the user's contribution # to the fee, ignoring any contribution from external inputs. Decreasing of # external outputs is not allowed in PayJoin, so there is no need to handle those. await helpers.confirm_modify_fee( - spending - orig_spending, fee, fee_rate, coin, amount_unit + title, spending - orig_spending, fee, fee_rate, coin, amount_unit ) else: # PayJoin and user is not spending more: When new external inputs are involved and diff --git a/core/src/apps/bitcoin/sign_tx/helpers.py b/core/src/apps/bitcoin/sign_tx/helpers.py index 025c46b52..5f90caadc 100644 --- a/core/src/apps/bitcoin/sign_tx/helpers.py +++ b/core/src/apps/bitcoin/sign_tx/helpers.py @@ -92,12 +92,12 @@ class UiConfirmPaymentRequest(UiConfirm): class UiConfirmReplacement(UiConfirm): - def __init__(self, description: str, txid: bytes): - self.description = description + def __init__(self, title: str, txid: bytes): + self.title = title self.txid = txid def confirm_dialog(self, ctx: Context) -> Awaitable[Any]: - return layout.confirm_replacement(ctx, self.description, self.txid) + return layout.confirm_replacement(ctx, self.title, self.txid) class UiConfirmModifyOutput(UiConfirm): @@ -122,12 +122,14 @@ class UiConfirmModifyOutput(UiConfirm): class UiConfirmModifyFee(UiConfirm): def __init__( self, + title: str, user_fee_change: int, total_fee_new: int, fee_rate: float, coin: CoinInfo, amount_unit: AmountUnit, ): + self.title = title self.user_fee_change = user_fee_change self.total_fee_new = total_fee_new self.fee_rate = fee_rate @@ -137,6 +139,7 @@ class UiConfirmModifyFee(UiConfirm): def confirm_dialog(self, ctx: Context) -> Awaitable[Any]: return layout.confirm_modify_fee( ctx, + self.title, self.user_fee_change, self.total_fee_new, self.fee_rate, @@ -255,10 +258,10 @@ def confirm_modify_output(txo: TxOutput, orig_txo: TxOutput, coin: CoinInfo, amo return (yield UiConfirmModifyOutput(txo, orig_txo, coin, amount_unit)) -def confirm_modify_fee(user_fee_change: int, total_fee_new: int, fee_rate: float, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[Any]: # type: ignore [awaitable-is-generator] +def confirm_modify_fee(title: str, user_fee_change: int, total_fee_new: int, fee_rate: float, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[Any]: # type: ignore [awaitable-is-generator] return ( yield UiConfirmModifyFee( - user_fee_change, total_fee_new, fee_rate, coin, amount_unit + title, user_fee_change, total_fee_new, fee_rate, coin, amount_unit ) ) diff --git a/core/src/apps/bitcoin/sign_tx/layout.py b/core/src/apps/bitcoin/sign_tx/layout.py index 9a7a16acb..28d059357 100644 --- a/core/src/apps/bitcoin/sign_tx/layout.py +++ b/core/src/apps/bitcoin/sign_tx/layout.py @@ -175,12 +175,12 @@ async def confirm_payment_request( ) -async def confirm_replacement(ctx: Context, description: str, txid: bytes) -> None: +async def confirm_replacement(ctx: Context, title: str, txid: bytes) -> None: from ubinascii import hexlify await layouts.confirm_replacement( ctx, - description, + title, hexlify(txid).decode(), ) @@ -206,6 +206,7 @@ async def confirm_modify_output( async def confirm_modify_fee( ctx: Context, + title: str, user_fee_change: int, total_fee_new: int, fee_rate: float, @@ -214,6 +215,7 @@ async def confirm_modify_fee( ) -> None: await layouts.confirm_modify_fee( ctx, + title, user_fee_change, format_coin_amount(abs(user_fee_change), coin, amount_unit), format_coin_amount(total_fee_new, coin, amount_unit), diff --git a/core/src/trezor/ui/layouts/tt_v2/__init__.py b/core/src/trezor/ui/layouts/tt_v2/__init__.py index 1806538f6..bda9fe640 100644 --- a/core/src/trezor/ui/layouts/tt_v2/__init__.py +++ b/core/src/trezor/ui/layouts/tt_v2/__init__.py @@ -728,6 +728,7 @@ async def confirm_blob( title: str, data: bytes | str, description: str | None = None, + verb: str = "CONFIRM", hold: bool = False, br_code: ButtonRequestType = BR_TYPE_OTHER, ask_pagination: bool = False, @@ -741,7 +742,7 @@ async def confirm_blob( data=data, extra=None, hold=hold, - verb="CONFIRM", + verb=verb, ) ) @@ -897,41 +898,23 @@ async def confirm_total( br_type: str = "confirm_total", br_code: ButtonRequestType = ButtonRequestType.SignTx, ) -> None: - layout = RustLayout( + total_layout = RustLayout( trezorui2.confirm_total( title=title, items=[ (total_label, total_amount), (fee_label, fee_amount), ], - info_button=account_label is not None, + info_button=bool(account_label or fee_rate_amount), ) ) - await button_request( - ctx, - br_type, - br_code, - pages=layout.page_count(), + info_layout = RustLayout( + trezorui2.show_spending_details( + account=account_label or "", + fee_rate=fee_rate_amount or "", + ) ) - - while True: - result = await ctx.wait(layout) - - if result is CONFIRMED: - return - elif result is INFO and account_label is not None: - result = await ctx.wait( - RustLayout( - trezorui2.show_spending_details( - account=account_label, fee_rate=fee_rate_amount - ) - ) - ) - assert result is CANCELLED - layout.request_complete_repaint() - continue - - raise ActionCancelled + await with_info(ctx, total_layout, info_layout, br_type, br_code) async def confirm_joint_total( @@ -978,12 +961,13 @@ async def confirm_metadata( ) -async def confirm_replacement(ctx: GenericContext, description: str, txid: str) -> None: +async def confirm_replacement(ctx: GenericContext, title: str, txid: str) -> None: await confirm_blob( ctx, - title=description.upper(), + title=title.upper(), data=txid, - description="Confirm transaction ID:", + description="Transaction ID:", + verb="CONTINUE", br_type="confirm_replacement", br_code=ButtonRequestType.SignTx, ) @@ -996,45 +980,101 @@ async def confirm_modify_output( amount_change: str, amount_new: str, ) -> None: - await raise_if_not_confirmed( - interact( - ctx, + send_button_request = True + while True: + if send_button_request: + await button_request( + ctx, + "modify_output", + ButtonRequestType.ConfirmOutput, + ) + await raise_if_not_confirmed( + ctx.wait( + RustLayout( + trezorui2.confirm_blob( + title="MODIFY AMOUNT", + data=address, + verb="CONTINUE", + verb_cancel=None, + description="Address:", + extra=None, + ) + ) + ) + ) + + if send_button_request: + send_button_request = False + await button_request( + ctx, + "modify_output", + ButtonRequestType.ConfirmOutput, + ) + result = await ctx.wait( RustLayout( trezorui2.confirm_modify_output( - address=address, sign=sign, amount_change=amount_change, amount_new=amount_new, ) ), - "modify_output", - ButtonRequestType.ConfirmOutput, ) - ) + + if result is CONFIRMED: + break + + +async def with_info( + ctx: GenericContext, + main_layout: RustLayout, + info_layout: RustLayout, + br_type: str, + br_code: ButtonRequestType, +) -> None: + await button_request(ctx, br_type, br_code, pages=main_layout.page_count()) + + while True: + result = await ctx.wait(main_layout) + + if result is CONFIRMED: + return + elif result is INFO: + info_layout.request_complete_repaint() + result = await ctx.wait(info_layout) + assert result is CANCELLED + main_layout.request_complete_repaint() + continue + + raise ActionCancelled async def confirm_modify_fee( ctx: GenericContext, + title: str, sign: int, user_fee_change: str, total_fee_new: str, fee_rate_amount: str | None = None, ) -> None: - await raise_if_not_confirmed( - interact( - ctx, - RustLayout( - trezorui2.confirm_modify_fee( - sign=sign, - user_fee_change=user_fee_change, - total_fee_new=total_fee_new, - fee_rate_amount=fee_rate_amount, - ) - ), - "modify_fee", - ButtonRequestType.SignTx, + fee_layout = RustLayout( + trezorui2.confirm_modify_fee( + title=title.upper(), + sign=sign, + user_fee_change=user_fee_change, + total_fee_new=total_fee_new, + ) + ) + info_layout = RustLayout( + trezorui2.show_spending_details( + account=None, + title="FEE INFORMATION", + fee_rate=fee_rate_amount, + fee_rate_title="New fee rate:", ) ) + await with_info( + ctx, fee_layout, info_layout, "modify_fee", ButtonRequestType.SignTx + ) async def confirm_coinjoin( diff --git a/core/src/trezor/ui/layouts/tt_v2/reset.py b/core/src/trezor/ui/layouts/tt_v2/reset.py index 36df8907b..c9e264380 100644 --- a/core/src/trezor/ui/layouts/tt_v2/reset.py +++ b/core/src/trezor/ui/layouts/tt_v2/reset.py @@ -28,6 +28,7 @@ def _split_share_into_pages(share_words: Sequence[str], per_page: int = 4) -> li pages.append(current) current = "" + # Align numbers to the right. lastnum = i + per_page + 1 fill = 1 if lastnum < 10 else 2 else: diff --git a/tests/device_tests/bitcoin/test_signtx.py b/tests/device_tests/bitcoin/test_signtx.py index 4f2cd5250..9f69b6007 100644 --- a/tests/device_tests/bitcoin/test_signtx.py +++ b/tests/device_tests/bitcoin/test_signtx.py @@ -93,7 +93,15 @@ TXHASH_25fee5 = bytes.fromhex( TXHASH_1f326f = bytes.fromhex( "1f326f65768d55ef146efbb345bd87abe84ac7185726d0457a026fc347a26ef3" ) - +TXHASH_334cd7 = bytes.fromhex( + "334cd7ad982b3b15d07dd1c84e939e95efb0803071648048a7f289492e7b4c8a" +) +TXHASH_5e7667 = bytes.fromhex( + "5e7667690076ae4737e2f872005de6f6b57592f32108ed9b301eeece6de24ad6" +) +TXHASH_efaa41 = bytes.fromhex( + "efaa41ff3e67edf508846c1a1ed56894cfd32725c590300108f40c9edc1aac35" +) CORNER_BUTTON = (215, 25) @@ -1671,3 +1679,67 @@ def test_information_cancel(client: Client): [out1], prev_txes=TX_CACHE_MAINNET, ) + + +@pytest.mark.skip_t1(reason="Cannot test layouts on T1") +@pytest.mark.skip_tr(reason="Input flow different on TR") +def test_information_replacement(client: Client): + # Use the change output and an external output to bump the fee. + # Originally fee was 3780, now 108060 (94280 from change and 10000 from external). + + inp1 = messages.TxInputType( + address_n=parse_path("m/49h/1h/0h/0/4"), + amount=100_000, + script_type=messages.InputScriptType.SPENDP2SHWITNESS, + prev_hash=TXHASH_5e7667, + prev_index=1, + orig_hash=TXHASH_334cd7, + orig_index=0, + ) + + inp2 = messages.TxInputType( + address_n=parse_path("m/49h/1h/0h/0/3"), + amount=998_060, + script_type=messages.InputScriptType.SPENDP2SHWITNESS, + prev_hash=TXHASH_efaa41, + prev_index=0, + orig_hash=TXHASH_334cd7, + orig_index=1, + ) + + out1 = messages.TxOutputType( + # Actually m/49'/1'/0'/0/5. + address="2MvUUSiQZDSqyeSdofKX9KrSCio1nANPDTe", + amount=990_000, + orig_hash=TXHASH_334cd7, + orig_index=0, + ) + + def input_flow(): + yield # confirm txid + client.debug.press_yes() + yield # confirm address + client.debug.press_yes() + # go back to address + client.debug.press_no() + # confirm address + client.debug.press_yes() + yield # confirm amount + client.debug.press_yes() + + yield # transaction summary, press info + client.debug.press_info(wait=True) + client.debug.click(CORNER_BUTTON, wait=True) + client.debug.press_yes() + + with client: + client.set_input_flow(input_flow) + client.watch_layout(True) + + btc.sign_tx( + client, + "Testnet", + [inp1, inp2], + [out1], + prev_txes=TX_CACHE_TESTNET, + ) diff --git a/tests/device_tests/bitcoin/test_signtx_replacement.py b/tests/device_tests/bitcoin/test_signtx_replacement.py index 474bb2601..9c1556d12 100644 --- a/tests/device_tests/bitcoin/test_signtx_replacement.py +++ b/tests/device_tests/bitcoin/test_signtx_replacement.py @@ -600,7 +600,7 @@ def test_p2wpkh_in_p2sh_fee_bump_from_external(client: Client): orig_index=0, ) - t1 = client.features.model == "1" + tr = client.features.model == "R" with client: client.set_expected_responses( [ @@ -613,7 +613,7 @@ def test_p2wpkh_in_p2sh_fee_bump_from_external(client: Client): request_output(0), request_orig_output(0, TXHASH_334cd7), messages.ButtonRequest(code=B.ConfirmOutput), - (t1, messages.ButtonRequest(code=B.ConfirmOutput)), + (not tr, messages.ButtonRequest(code=B.ConfirmOutput)), request_orig_output(1, TXHASH_334cd7), messages.ButtonRequest(code=B.SignTx), request_input(0), diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 81d4275dc..3b6945ae4 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -1919,13 +1919,13 @@ "TT_test_pin.py::test_pin_short": "b5377990e4a1f324133601e3ca4726cde7af50b3e2c3f53738022ed9108a6a79", "TT_test_pin.py::test_wipe_code_same_as_pin": "cb8aa7d781b689452e863a3e704c1df06d217cbf8761b698cd912e0417da1513", "TT_test_pin.py::test_wipe_code_setup": "353855511c20bcf4f8ec01d141c11a108472dc64e3dac06c5f567b42bddbaba9", -"TT_test_recovery.py::test_recovery_bip39": "6ff84bfab19bec7db2d5672a2059a69dbae56cc1e06464700024e431c9a1349f", -"TT_test_recovery.py::test_recovery_slip39_basic": "e038348233dc15169cd2678482f4512ed863fedca5cd70d94e571d88fa40c0a7", -"TT_test_reset_bip39.py::test_reset_bip39": "a37589806445d80b11edc57f3c80241fa14d538dbcd08f382bad7d010c3b708a", -"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "67207985c09645772e42512a6cc3389e9389247d2164bb8e5731f86e707bfef7", -"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "62f22bb7ec26ee3c12560bdf29496b7d4ca34b83083a6e4889c7d412c5a3e9ab", -"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "ff60196c3efff9f8f9fc25d29d7e5241521d3c80a7ee43ead5f48e4914fac099", -"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "35de95c810ac552b82d3af5b581dd3ade06f12b72a11cb45894d8a83c6e26be2" +"TT_test_recovery.py::test_recovery_bip39": "85f92b26902f1ce64913d6ca9874a508fcb21fff8411145c438de69077a4989a", +"TT_test_recovery.py::test_recovery_slip39_basic": "53fa3abfe1ce7b11ec189fa359f463763b53802ae5925e84a6593271fe4e8f9a", +"TT_test_reset_bip39.py::test_reset_bip39": "21633fd88045835f5ee6ca8e37f10e994345ccc0031662adbd58434db218c590", +"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "34680359511117bce691dc377169f55f515c07ab1d958a15757a81bee3314aed", +"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "a5e043a743d239181ecf73d2e6c48ae05485cee7cdcd4626ac8599709bcded47", +"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "ed849cb2b3b0c3917f78289e415f0d89fdba34821b8ed8126ee5653abfa8169b", +"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "309c4136c0621f4ffb3c1ff64796736111e95544165fa5bfd4147d9a75d1c71b" }, "device_tests": { "TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "483ff25f0ff24de80631dfb202ac681c38dfbf44c5905505281c2d0719a94fc6", @@ -2205,6 +2205,7 @@ "TT_bitcoin-test_signtx.py::test_information": "54c4b3a4deff8fcbbcf7fa06b7eb594ec191ec64ee5f1d69bb3692cbfd902a8c", "TT_bitcoin-test_signtx.py::test_information_cancel": "d0989971cdaea07efc154c1508ce02eb20d05718443f3be7c34184f14621c2ec", "TT_bitcoin-test_signtx.py::test_information_mixed": "bcc21fdced7033efbdc307650f5e766ab030ae9b2f3c1d1a19f30be1951d0173", +"TT_bitcoin-test_signtx.py::test_information_replacement": "bacaa02b0fa60db7410411932f0936a5c8bbf343597f8497b3285c7cbc6e11bf", "TT_bitcoin-test_signtx.py::test_lock_time[1-4294967295]": "940c512b4932e8da6a27507d1aa1fa499d074b5105ebe88627e8c20d2138a65f", "TT_bitcoin-test_signtx.py::test_lock_time[499999999-4294967294]": "29911873931f3e312bc9794cb57e507af02c7a1324717df59507fe1ce088e3cd", "TT_bitcoin-test_signtx.py::test_lock_time[500000000-4294967294]": "fb6bded1f891884c9a82c582126fc7e883a8c70a513ca177d92c47d1256f8f5f", @@ -2293,21 +2294,21 @@ "TT_bitcoin-test_signtx_replacement.py::test_attack_fake_ext_input_amount": "f97a9ce841c7ceddbe8e1fd4c39a68c40b4606058eab4cc2c02c1d2143c60a38", "TT_bitcoin-test_signtx_replacement.py::test_attack_fake_int_input_amount": "f97a9ce841c7ceddbe8e1fd4c39a68c40b4606058eab4cc2c02c1d2143c60a38", "TT_bitcoin-test_signtx_replacement.py::test_attack_false_internal": "f97a9ce841c7ceddbe8e1fd4c39a68c40b4606058eab4cc2c02c1d2143c60a38", -"TT_bitcoin-test_signtx_replacement.py::test_attack_steal_change": "11f992c1ee9aebb9c672be1ff0b6606368b84ecb340cfdde2a085c89db712657", -"TT_bitcoin-test_signtx_replacement.py::test_p2pkh_fee_bump": "fbf869ddec563f34f168eaa42a7e4c2c6764d21541d3199ce38660639d5ca094", -"TT_bitcoin-test_signtx_replacement.py::test_p2tr_fee_bump": "036353f716b4cc3dfe8159896e5fbbda99083428d20529a0e0bdadee3b065f9b", -"TT_bitcoin-test_signtx_replacement.py::test_p2tr_invalid_signature": "036353f716b4cc3dfe8159896e5fbbda99083428d20529a0e0bdadee3b065f9b", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_finalize": "b6cc31733a28894d9a9e4399f544ca87a3d36b80aa4eb405bba87c93f366f533", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_fee_bump_from_external": "b713534ff7d9daab50ed23497cc40f24d9f97d967980045f37e8f61c1c282813", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_remove_change": "bd04e0c7d675c9fdb92cbd35e6fff88ecfc99900e6f2e1aaa71ee73e74da1ad6", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_invalid_signature": "b6cc31733a28894d9a9e4399f544ca87a3d36b80aa4eb405bba87c93f366f533", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_op_return_fee_bump": "5ba37ea0a488085f84a977ab26bc892c4ef592a7ef16e7b248781637e493d9bb", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909659-90000-02483045022100aa1b91-c9b963ae": "86967f42e1e91312cf364a9e9b0b99d9c3cf01ceb7d5c6eb94c0925fd7220fc8", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909718-90000-024730440220753f5304-ecb983d1": "86967f42e1e91312cf364a9e9b0b99d9c3cf01ceb7d5c6eb94c0925fd7220fc8", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909800-89859-0248304502210097a42b-7a89e474": "86967f42e1e91312cf364a9e9b0b99d9c3cf01ceb7d5c6eb94c0925fd7220fc8", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909859-89800-02483045022100af3a87-80428fad": "30f6706f52ef795d528909cab43fd9e07dd9382b8bf2d77228c998ad4d0be62c", -"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909859-89859-02483045022100eb74ab-881c7bef": "86967f42e1e91312cf364a9e9b0b99d9c3cf01ceb7d5c6eb94c0925fd7220fc8", -"TT_bitcoin-test_signtx_replacement.py::test_tx_meld": "1d4c6f043bfff920d09b8da2201ddffe84d970756a603ff54b9f9b59696c3ef6", +"TT_bitcoin-test_signtx_replacement.py::test_attack_steal_change": "5010e2df48c6250924a26345336beb0cd945f1a37c628572f70fbaad405ee7e2", +"TT_bitcoin-test_signtx_replacement.py::test_p2pkh_fee_bump": "4a8efb3ca3927cb6353ed86deea6240c16f27f41a493e9420b123dd09fcb6f13", +"TT_bitcoin-test_signtx_replacement.py::test_p2tr_fee_bump": "0c608c9cae88cde8cb3901fbfa69c511c10b88d81021d2134c5a77ce08553070", +"TT_bitcoin-test_signtx_replacement.py::test_p2tr_invalid_signature": "0c608c9cae88cde8cb3901fbfa69c511c10b88d81021d2134c5a77ce08553070", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_finalize": "8e01e2115bb0d48548797013cb74ba1ee50908945846b2e22eeee282a97bee98", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_fee_bump_from_external": "57e1e2ab76ed05ddc9833b63999e846e94ff40046d0d9a88323b0eb82cd10fe1", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_remove_change": "0a8cf9e7f974b5b827fe347379d26bbf5ffba68a7be040c77d6a86e2ea0765bc", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_invalid_signature": "8e01e2115bb0d48548797013cb74ba1ee50908945846b2e22eeee282a97bee98", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_op_return_fee_bump": "644ae548f9be2447a6ff6021f1495e58e337e67f15016ad6e18f764a7b5fc8d4", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909659-90000-02483045022100aa1b91-c9b963ae": "a055056fba39475606357ee9b8379c58b8d44577cb85d1a611cf77e7f205cfd9", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909718-90000-024730440220753f5304-ecb983d1": "a055056fba39475606357ee9b8379c58b8d44577cb85d1a611cf77e7f205cfd9", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909800-89859-0248304502210097a42b-7a89e474": "a055056fba39475606357ee9b8379c58b8d44577cb85d1a611cf77e7f205cfd9", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909859-89800-02483045022100af3a87-80428fad": "5355a4deff79234a6d6d1e4456bc7c9123fb7f0a0037c9dff52bc82f419cdc97", +"TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909859-89859-02483045022100eb74ab-881c7bef": "a055056fba39475606357ee9b8379c58b8d44577cb85d1a611cf77e7f205cfd9", +"TT_bitcoin-test_signtx_replacement.py::test_tx_meld": "e3d55c8342b34b161bac785a5e41c0b09104cfadd2c8473e4fed57c2eb321edd", "TT_bitcoin-test_signtx_segwit.py::test_attack_change_input_address": "64771558b8dfb4f6317f95a1bc87c77e8461a7fd7ef6fee39770330984b55373", "TT_bitcoin-test_signtx_segwit.py::test_attack_mixed_inputs": "b1230b100c552e5be052c169f46a5cdd5a2fe37c4e9e8051600fed6d91399cd1", "TT_bitcoin-test_signtx_segwit.py::test_send_multisig_1": "4f66563032c2db963ad141655c8f362d54360902bc401d5d1101fa243b67dbc6", @@ -2876,24 +2877,24 @@ "TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[2]": "be1dd4ecbfc29a767dd3f1faf6ee12282422f0504175b996afa352b39a7f540b", "TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_dryrun": "87718399c69d11e8656d4af3af448f542970fbfca524a53955adcd911903efd0", "TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "0fbabbe652adc6a4e0d7285b165687dce0ad617762ed228f6a21d56de409d799", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "774d71f7654fa0fd89198359c96cfac655a21f4410c056f40e12b9309874b196", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "8410e57b746339b2a35078412fc2b83d26ddffbf73a8545f52dcda07b052b7b3", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "6dedfe69fd4f29b5c0fc65e85ef51cb0294b95c30021b401655c0516409a09ad", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "1aa3ce14a715dfaf9bba07c613bd96473bcfa57e48888f959d40a37fd8cda4c6", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "a5fc539ca06049a3753b767a2dc409e0dacefd5878cfeda0b2c20c8ed87036b5", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "12b002d816c964295d1f49318871555ab7eb71f42af04fab94e7978f05b25d9e", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "0109cf31e69f4bd666644b866ca26dfd47e5cc3cb211d4b7863dcbfd94f5a542", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "c631c45a838fd1f167fd2149886fc54c28cf0fe032fe846a7f7e0f27dea7270a", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "1180763b4f38e2836fef413f7107483375d1c4c851ee825528f9c6278b6ed992", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "2f8a27fa1fb5c5454a74885d361aa5507b029679e35032611e5bb032e2195bcf", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "aa094ca5d4658b33cd08260b8a465ebbe4862d1f22e76d34cd576d57f1027908", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "eb31a91d2d8f3be95aff992c6ba69e38b3db5cbb7355194aebb6ad4f042f587a", "TT_reset_recovery-test_reset_bip39_t2.py::test_already_initialized": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "cb7f55d17ebf06888ff02ab3db0a5bd2b5355dcc465eab5fc009089845623c72", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "8f08225079c56dc3514277913a3fd409fd9ca68fb899b1b04fe94c31eba20da0", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "dcdb1b242a1747766fcb3f91d95226c47ad68e31c1a68c15b3dad219d4bf733a", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "0fb3df94993fe08bb219ff293329a07e367e6dc2e659a8817a3a9b039835e5b9", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "e447e861244e3ee749842f503ac344192fa6bb7fee499317f53985579c5c840e", -"TT_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "28187d0bd07df432025468ed1ec5dc96ad5a8df7eceb5013e66cead01db76524", -"TT_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "0b6c7b04b3c71326d6849d2d93b52f49fae67393fcd967d16fd64062bab23142", -"TT_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "a43eb7f0c6fe3f42f7086efd984c78c02994798dcc134cd8e683c1b53f394d1f", -"TT_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "bdf31edf5ed0384dfd429427d6c8362f6e6862ac8424a2e04bdc0603c9167e64", -"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "ec94b714938f999d301198681e22eecaca59cfbc5ca3499fc58268f7d52bab89", -"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "f5e65eeadaa2a3adc45c6425135434d84131d81056f7426bded9503b98e2517c", +"TT_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "3ec95417936f90e01b92ab89b50d1a5acb98befb51c5767914f501298e1a794c", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "36f6689acdafb02680f061608792295b0136a9ab5789ab82f2952266d5823107", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "e6d944b6e94a8810354026a454c3d7b918865e230897d4027b453011105eb5ad", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "7c8a9725873c105663ed7b78cd9f01c236850de356012d4e99f24d90dfc66bb1", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "12b3e2b0f6df93af5ce1e88b9c6d99d0be18c60e6b67e42a172badbd2cd9c100", +"TT_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "51944865c548c77adc948bfa068bc9631d2e6f5f6df3af1089cf53bc5b43a28b", +"TT_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "822dc0f5e2dd89d5d7c1aca7d0b932de459f400622c59c8fd4a56e28022c02c1", +"TT_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "ee89833730540c24f204fc1fad392393b3177d12bacd2d602b1b7434350ea0a8", +"TT_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "f472b076ffdec128622e39b121f05f9bcb29733ea751da968c51a6dfe4942d7e", +"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "390c996a1d11b47696a6a03c019a2e4ec37485b795c1dcc9885679201003cb83", +"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "c91bf595acacba296a3fcb9db979a0475a86cfba67f92f260ef3827a00472bc0", "TT_ripple-test_get_address.py::test_ripple_get_address": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_ripple-test_get_address.py::test_ripple_get_address_other": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_ripple-test_sign_tx.py::test_ripple_sign_invalid_fee": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", @@ -2978,12 +2979,12 @@ "TT_test_msg_applysettings.py::test_experimental_features": "e7bdcfa8708fc0fbe88f5f4cc7f93497c94fa5ae9e08282496c04e993123674c", "TT_test_msg_applysettings.py::test_label_too_long": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_msg_applysettings.py::test_safety_checks": "4ec93f34a54d13317c2d5434ed5853d0edf0f6d91772aab2a15acf7e3de75458", -"TT_test_msg_backup_device.py::test_backup_bip39": "f893431c45c12b0cab6695ad1ed3740bef8a546ea77c000e02287acc51f49396", -"TT_test_msg_backup_device.py::test_backup_slip39_advanced[click_info]": "6e0456552d918a2bfeff518f664bf217ff69fb78585b9bedb786e453a602e0ed", -"TT_test_msg_backup_device.py::test_backup_slip39_advanced[no_click_info]": "58261eb9d6e99256ceb5ebbea9fb91529bc9fd3bdb178787aa4e1e7ad85fc4f3", -"TT_test_msg_backup_device.py::test_backup_slip39_basic[click_info]": "7977be97fb539b1f0706fb792a3ba603148e656e6d1f1dcf599fc033aa8e02e2", -"TT_test_msg_backup_device.py::test_backup_slip39_basic[no_click_info]": "5e1e10a652aac8c95a771e873a1823ca35811df7fdb2356c15d1c2ab06ae7d3c", -"TT_test_msg_backup_device.py::test_interrupt_backup_fails": "ae147498028d68aa71c7337544e4a5049c4c943897f905c6fe29e88e5c3ab056", +"TT_test_msg_backup_device.py::test_backup_bip39": "443cbf213767f7152abe6ca70dbc09f8a8b1e325bbb866805b404dc345232d67", +"TT_test_msg_backup_device.py::test_backup_slip39_advanced[click_info]": "1a04ac093951e611863a6a52a6ea14360eb13caeabfda438b5ac7c379f66cb8a", +"TT_test_msg_backup_device.py::test_backup_slip39_advanced[no_click_info]": "1e2d27cf6d2634293b2a971065e809498f4ade47530d4f1d6aa04f01d87a6349", +"TT_test_msg_backup_device.py::test_backup_slip39_basic[click_info]": "999e1072a46dd9576d1ac51aede71c0ae650e8d0bb5cd140c68bad3826592601", +"TT_test_msg_backup_device.py::test_backup_slip39_basic[no_click_info]": "ff61a3e19bfde5105e727484fccb919febb0bf02c61c92dbad548439bdd0339d", +"TT_test_msg_backup_device.py::test_interrupt_backup_fails": "cdf801e16b046079569e4055f43a86a45d7eace58793427ef5433f71e75961f9", "TT_test_msg_backup_device.py::test_no_backup_fails": "fada9d38ec099b3c6a4fd8bf994bb1f3431e40085128b4e0cd9deb8344dec53e", "TT_test_msg_backup_device.py::test_no_backup_show_entropy_fails": "001377ce61dcd189e6a9d17e20dcd71130e951dc3314b40ff26f816bd9355bdd", "TT_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "b3abe56cbc85b9012e1fa27dce0e869f0a01de7a72db24e44177690a57392b52",