From 208283e13e9c43a72240656a40733ff96a1d187c Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Mon, 29 Jun 2020 18:12:21 +0200 Subject: [PATCH] core/bitcoin: Implement AuthorizeCoinJoin message. --- core/src/apps/bitcoin/__init__.py | 1 + core/src/apps/bitcoin/authorize_coinjoin.py | 78 +++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 core/src/apps/bitcoin/authorize_coinjoin.py diff --git a/core/src/apps/bitcoin/__init__.py b/core/src/apps/bitcoin/__init__.py index 47154d5466..b553702525 100644 --- a/core/src/apps/bitcoin/__init__.py +++ b/core/src/apps/bitcoin/__init__.py @@ -3,6 +3,7 @@ from trezor.messages import MessageType def boot() -> None: + wire.add(MessageType.AuthorizeCoinJoin, __name__, "authorize_coinjoin") wire.add(MessageType.GetPublicKey, __name__, "get_public_key") wire.add(MessageType.GetAddress, __name__, "get_address") wire.add(MessageType.GetOwnershipId, __name__, "get_ownership_id") diff --git a/core/src/apps/bitcoin/authorize_coinjoin.py b/core/src/apps/bitcoin/authorize_coinjoin.py new file mode 100644 index 0000000000..70f44dd235 --- /dev/null +++ b/core/src/apps/bitcoin/authorize_coinjoin.py @@ -0,0 +1,78 @@ +from micropython import const + +from trezor import ui +from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin +from trezor.messages.Success import Success +from trezor.strings import format_amount +from trezor.ui.text import Text + +from apps.base import set_authorization +from apps.common.confirm import require_confirm, require_hold_to_confirm +from apps.common.paths import validate_path + +from . import addresses +from .authorization import FEE_PER_ANONYMITY_DECIMALS, CoinJoinAuthorization +from .common import BIP32_WALLET_DEPTH +from .keychain import get_keychain_for_coin + +if False: + from trezor import wire + +_MAX_COORDINATOR_LEN = const(18) + + +async def authorize_coinjoin(ctx: wire.Context, msg: AuthorizeCoinJoin,) -> Success: + # We cannot use the @with_keychain decorator here, because we need the keychain + # to survive the function exit. The ownership of the keychain is transferred to + # the CoinJoinAuthorization object, which takes care of its destruction. + keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name) + + try: + if len(msg.coordinator) > _MAX_COORDINATOR_LEN or not all( + 32 <= ord(x) <= 126 for x in msg.coordinator + ): + raise wire.DataError("Invalid coordinator name.") + + if not msg.address_n: + raise wire.DataError("Empty path not allowed.") + + await validate_path( + ctx, + addresses.validate_full_path, + keychain, + msg.address_n + [0] * BIP32_WALLET_DEPTH, + coin.curve_name, + coin=coin, + script_type=msg.script_type, + ) + + text = Text("Authorize CoinJoin", ui.ICON_RECOVERY) + text.normal("Do you really want to") + text.normal("take part in a CoinJoin") + text.normal("transaction at:") + text.mono(msg.coordinator) + await require_confirm(ctx, text) + + text = Text("Authorize CoinJoin", ui.ICON_RECOVERY) + if msg.fee_per_anonymity is not None: + text.normal("Fee per anonymity set:") + text.bold( + "{} %".format( + format_amount(msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS) + ) + ) + text.normal("Maximum total fees:") + text.bold( + "{} {}".format( + format_amount(msg.max_total_fee, coin.decimals), coin.coin_shortcut + ) + ) + await require_hold_to_confirm(ctx, text) + + set_authorization(CoinJoinAuthorization(msg, keychain, coin)) + + except BaseException: + keychain.__del__() + raise + + return Success(message="CoinJoin authorized")