From 9bce1536236567b24f07adacdcc34f9e4adb202a Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Fri, 18 Mar 2022 16:55:42 +0100 Subject: [PATCH] feat(core): Warn about unverified external inputs. --- core/src/apps/bitcoin/sign_tx/approvers.py | 5 +++++ core/src/apps/bitcoin/sign_tx/helpers.py | 9 +++++++++ core/src/apps/bitcoin/sign_tx/layout.py | 10 ++++++++++ tests/ui_tests/fixtures.json | 4 ++-- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/core/src/apps/bitcoin/sign_tx/approvers.py b/core/src/apps/bitcoin/sign_tx/approvers.py index c21943d3b..76cb172dc 100644 --- a/core/src/apps/bitcoin/sign_tx/approvers.py +++ b/core/src/apps/bitcoin/sign_tx/approvers.py @@ -50,6 +50,7 @@ class Approver: self.orig_change_out = 0 # sum of original change output amounts self.amount_unit = tx.amount_unit + self.has_unverified_external_input = False def is_payjoin(self) -> bool: # A PayJoin is a replacement transaction which manipulates the external inputs of the @@ -73,6 +74,7 @@ class Approver: self.orig_total_in += txi.amount if input_is_external_unverified(txi): + self.has_unverified_external_input = True if safety_checks.is_strict(): raise wire.ProcessError("Unverifiable external input.") else: @@ -235,6 +237,9 @@ class BasicApprover(Approver): async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None: await super().approve_tx(tx_info, orig_txs) + if self.has_unverified_external_input: + await helpers.confirm_unverified_external_input() + fee = self.total_in - self.total_out # some coins require negative fees for reward TX diff --git a/core/src/apps/bitcoin/sign_tx/helpers.py b/core/src/apps/bitcoin/sign_tx/helpers.py index 0b17301fa..f981ee4bc 100644 --- a/core/src/apps/bitcoin/sign_tx/helpers.py +++ b/core/src/apps/bitcoin/sign_tx/helpers.py @@ -180,6 +180,11 @@ class UiConfirmChangeCountOverThreshold(UiConfirm): return layout.confirm_change_count_over_threshold(ctx, self.change_count) +class UiConfirmUnverifiedExternalInput(UiConfirm): + def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]: + return layout.confirm_unverified_external_input(ctx) + + class UiConfirmForeignAddress(UiConfirm): def __init__(self, address_n: list): self.address_n = address_n @@ -239,6 +244,10 @@ def confirm_change_count_over_threshold(change_count: int) -> Awaitable[Any]: # return (yield UiConfirmChangeCountOverThreshold(change_count)) +def confirm_unverified_external_input() -> Awaitable[Any]: # type: ignore [awaitable-is-generator] + return (yield UiConfirmUnverifiedExternalInput()) + + def confirm_foreign_address(address_n: list) -> Awaitable[Any]: # type: ignore [awaitable-is-generator] return (yield UiConfirmForeignAddress(address_n)) diff --git a/core/src/apps/bitcoin/sign_tx/layout.py b/core/src/apps/bitcoin/sign_tx/layout.py index 1c2a0e5b0..910a826ff 100644 --- a/core/src/apps/bitcoin/sign_tx/layout.py +++ b/core/src/apps/bitcoin/sign_tx/layout.py @@ -220,6 +220,16 @@ async def confirm_change_count_over_threshold( ) +async def confirm_unverified_external_input(ctx: wire.Context) -> None: + await layouts.confirm_metadata( + ctx, + "unverified_external_input", + "Warning", + "The transaction contains unverified external inputs.", + br_code=ButtonRequestType.SignTx, + ) + + async def confirm_nondefault_locktime( ctx: wire.Context, lock_time: int, lock_time_disabled: bool ) -> None: diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 14d664012..a8c3b2a98 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -898,9 +898,9 @@ "TT_bitcoin-test_signtx_external.py::test_p2pkh_presigned": "8dd8089941ceb0d82c9425c69d54240f99e3ae7932ef24acd49313d28389b683", "TT_bitcoin-test_signtx_external.py::test_p2pkh_with_proof": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "TT_bitcoin-test_signtx_external.py::test_p2tr_external_presigned": "c714c4a4ea8b98dfbdd8185925adafbafc62570f415688972d6003a19d7b4d23", -"TT_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "9919d854e2702be375ad09c74af4fbaaa92d36cc2ed360cc7cfbd196c21a39a6", +"TT_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "b398085c2fa6d4fa1ba97d872f95f3ac2268e9455f5831344f6e34a4badf7a17", "TT_bitcoin-test_signtx_external.py::test_p2tr_with_proof": "d6723e2243bc38231ec4eb9ed63afd39610460c0d859b4c576b12db1f7915d02", -"TT_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "7780f69c321c49c97ee970617ba91a63a7c375dd5ca3d13dec943c28511fb128", +"TT_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "d178bd3e2cadbe0992904b3d32df963b75e1da801804a471e45a1c5c0a9f2581", "TT_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_presigned": "8313bff77e41aef142c3b25818ab58dcc7e9d658d38e2e8fc50629ebbe05869b", "TT_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_with_proof": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "TT_bitcoin-test_signtx_external.py::test_p2wpkh_presigned": "4608478b1d61415cf0ec93a0ea4397c35d17a91d4b6d25e9c024b77330e398eb",