From f9ddb0c0feed1d88adc2b9d941b5af8c05ed568e Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Wed, 11 Jan 2023 12:40:18 +0100 Subject: [PATCH] feat(core): Show path for internal outputs in BTC signing. --- core/.changelog.d/2152.added | 1 + core/src/apps/bitcoin/keychain.py | 110 ++++++++++++++++++- core/src/apps/bitcoin/sign_tx/layout.py | 14 ++- core/src/trezor/ui/layouts/tr/__init__.py | 4 +- core/src/trezor/ui/layouts/tt_v2/__init__.py | 4 +- 5 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 core/.changelog.d/2152.added diff --git a/core/.changelog.d/2152.added b/core/.changelog.d/2152.added new file mode 100644 index 0000000000..7a3780573b --- /dev/null +++ b/core/.changelog.d/2152.added @@ -0,0 +1 @@ +Show path for internal outputs in BTC signing. diff --git a/core/src/apps/bitcoin/keychain.py b/core/src/apps/bitcoin/keychain.py index eed1252d98..319b0ef2dc 100644 --- a/core/src/apps/bitcoin/keychain.py +++ b/core/src/apps/bitcoin/keychain.py @@ -1,9 +1,10 @@ from micropython import const from typing import TYPE_CHECKING +from trezor.enums import InputScriptType from trezor.messages import AuthorizeCoinJoin, SignMessage -from apps.common.paths import PATTERN_BIP44, PathSchema +from apps.common.paths import PATTERN_BIP44, PathSchema, unharden from . import authorization from .common import BITCOIN_NAMES @@ -15,7 +16,6 @@ if TYPE_CHECKING: from trezor.protobuf import MessageType from trezor.wire import Context - from trezor.enums import InputScriptType from trezor.messages import ( GetAddress, GetOwnershipId, @@ -331,3 +331,109 @@ def with_keychain(func: HandlerWithCoinInfo[MsgOut]) -> Handler[MsgIn, MsgOut]: return await func(ctx, msg, keychain, coin) return wrapper + + +class AccountType: + def __init__( + self, + account_name: str, + pattern: str, + script_type: InputScriptType, + require_segwit: bool, + require_bech32: bool, + require_taproot: bool, + ): + self.account_name = account_name + self.pattern = pattern + self.script_type = script_type + self.require_segwit = require_segwit + self.require_bech32 = require_bech32 + self.require_taproot = require_taproot + + def get_name( + self, + coin: coininfo.CoinInfo, + address_n: Bip32Path, + script_type: InputScriptType | None, + ) -> str | None: + if ( + (script_type is not None and script_type != self.script_type) + or not PathSchema.parse(self.pattern, coin.slip44).match(address_n) + or (not coin.segwit and self.require_segwit) + or (not coin.bech32_prefix and self.require_bech32) + or (not coin.taproot and self.require_taproot) + ): + return None + + name = self.account_name + account_pos = self.pattern.find("/account'") + if account_pos >= 0: + i = self.pattern.count("/", 0, account_pos) + account_number = unharden(address_n[i]) + 1 + name += f" #{account_number}" + + return name + + +def address_n_to_name( + coin: coininfo.CoinInfo, + address_n: Bip32Path, + script_type: InputScriptType | None = None, +) -> str | None: + ACCOUNT_TYPES = ( + AccountType( + "Legacy account", + PATTERN_BIP44, + InputScriptType.SPENDADDRESS, + require_segwit=True, + require_bech32=False, + require_taproot=False, + ), + AccountType( + "Account", + PATTERN_BIP44, + InputScriptType.SPENDADDRESS, + require_segwit=False, + require_bech32=False, + require_taproot=False, + ), + AccountType( + "Legacy SegWit account", + PATTERN_BIP49, + InputScriptType.SPENDP2SHWITNESS, + require_segwit=True, + require_bech32=False, + require_taproot=False, + ), + AccountType( + "SegWit account", + PATTERN_BIP84, + InputScriptType.SPENDWITNESS, + require_segwit=True, + require_bech32=True, + require_taproot=False, + ), + AccountType( + "Taproot account", + PATTERN_BIP86, + InputScriptType.SPENDTAPROOT, + require_segwit=False, + require_bech32=True, + require_taproot=True, + ), + AccountType( + "Coinjoin account", + PATTERN_SLIP25_TAPROOT, + InputScriptType.SPENDTAPROOT, + require_segwit=False, + require_bech32=True, + require_taproot=True, + ), + ) + + for account in ACCOUNT_TYPES: + name = account.get_name(coin, address_n, script_type) + if name: + return name + + return None diff --git a/core/src/apps/bitcoin/sign_tx/layout.py b/core/src/apps/bitcoin/sign_tx/layout.py index 39ae255aa8..d27fc70934 100644 --- a/core/src/apps/bitcoin/sign_tx/layout.py +++ b/core/src/apps/bitcoin/sign_tx/layout.py @@ -6,8 +6,11 @@ from trezor.strings import format_amount from trezor.ui import layouts from trezor.ui.layouts import confirm_metadata +from apps.common.paths import address_n_to_str + from .. import addresses -from ..common import format_fee_rate +from ..common import CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES, format_fee_rate +from ..keychain import address_n_to_name if TYPE_CHECKING: from typing import Any @@ -76,11 +79,20 @@ async def confirm_output( else: title = "Confirm sending" + address_label = None + if output.address_n and not output.multisig: + script_type = CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES[output.script_type] + address_label = ( + address_n_to_name(coin, output.address_n, script_type) + or f"address path {address_n_to_str(output.address_n)}" + ) + layout = layouts.confirm_output( ctx, address_short, format_coin_amount(output.amount, coin, amount_unit), title=title, + address_label=address_label, ) await layout diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index eb8c0062f2..7054c4b634 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -183,13 +183,15 @@ async def confirm_output( amount: str, title: str = "Confirm sending", br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, + address_label: str | None = None, ) -> None: + label = f" ({address_label})" if address_label else "" result = await interact( ctx, RustLayout( trezorui2.confirm_text( title=title, - data=f"Send {amount} to {address}?", + data=f"Send {amount} to {address}{label}?", description="Confirm Output", ) ), diff --git a/core/src/trezor/ui/layouts/tt_v2/__init__.py b/core/src/trezor/ui/layouts/tt_v2/__init__.py index 6d0db63058..2b9a9ba6c2 100644 --- a/core/src/trezor/ui/layouts/tt_v2/__init__.py +++ b/core/src/trezor/ui/layouts/tt_v2/__init__.py @@ -514,16 +514,18 @@ async def confirm_output( amount: str, title: str = "SENDING", br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, + address_label: str | None = None, ) -> None: title = title.upper() if title.startswith("CONFIRM "): title = title[len("CONFIRM ") :] + description = f"To your {address_label}:" if address_label else "To:" await confirm_value( ctx, title, address, - "To:", + description, "confirm_output", br_code, verb="NEXT",