diff --git a/core/src/apps/bitcoin/sign_tx/__init__.py b/core/src/apps/bitcoin/sign_tx/__init__.py index 49715a8ff..34c98f163 100644 --- a/core/src/apps/bitcoin/sign_tx/__init__.py +++ b/core/src/apps/bitcoin/sign_tx/__init__.py @@ -10,7 +10,7 @@ if not utils.BITCOIN_ONLY: from . import bitcoinlike, decred, zcash if False: - from typing import Optional, Union + from typing import Protocol, Optional, Type, Union from protobuf import FieldCache @@ -36,6 +36,19 @@ if False: TxAckPrevExtraData, ] + class SignerClass(Protocol): + def __init__( + self, + tx: SignTx, + keychain: Keychain, + coin: CoinInfo, + approver: Optional[approvers.Approver], + ) -> None: + ... + + async def signer(self) -> None: + ... + @with_keychain async def sign_tx( @@ -45,15 +58,12 @@ async def sign_tx( coin: CoinInfo, authorization: Optional[CoinJoinAuthorization] = None, ) -> TxRequest: + approver: Optional[approvers.Approver] = None if authorization: - approver: approvers.Approver = approvers.CoinJoinApprover( - msg, coin, authorization - ) - else: - approver = approvers.BasicApprover(msg, coin) + approver = approvers.CoinJoinApprover(msg, coin, authorization) if utils.BITCOIN_ONLY or coin.coin_name in BITCOIN_NAMES: - signer_class = bitcoin.Bitcoin + signer_class: Type[SignerClass] = bitcoin.Bitcoin else: if coin.decred: signer_class = decred.Decred diff --git a/core/src/apps/bitcoin/sign_tx/approvers.py b/core/src/apps/bitcoin/sign_tx/approvers.py index b2b1fa935..79b6dbb2c 100644 --- a/core/src/apps/bitcoin/sign_tx/approvers.py +++ b/core/src/apps/bitcoin/sign_tx/approvers.py @@ -2,7 +2,6 @@ from micropython import const from trezor import wire from trezor.messages import OutputScriptType -from trezor.utils import ensure from apps.common import safety_checks @@ -77,11 +76,6 @@ class Approver: self.weight.add_output(script_pubkey) self.total_out += txo.amount - async def add_decred_sstx_submission( - self, txo: TxOutput, script_pubkey: bytes - ) -> None: - raise NotImplementedError - def add_orig_external_output(self, txo: TxOutput) -> None: self.orig_total_out += txo.amount @@ -131,13 +125,6 @@ class BasicApprover(Approver): else: await helpers.confirm_output(txo, self.coin, self.amount_unit) - async def add_decred_sstx_submission( - self, txo: TxOutput, script_pubkey: bytes - ) -> None: - ensure(self.coin.decred) - await super().add_external_output(txo, script_pubkey, None) - await helpers.confirm_decred_sstx_submission(txo, self.coin, self.amount_unit) - async def approve_tx(self, tx_info: TxInfo, orig_txs: List[OriginalTxInfo]) -> None: fee = self.total_in - self.total_out diff --git a/core/src/apps/bitcoin/sign_tx/bitcoin.py b/core/src/apps/bitcoin/sign_tx/bitcoin.py index 06631d106..f22ba6f60 100644 --- a/core/src/apps/bitcoin/sign_tx/bitcoin.py +++ b/core/src/apps/bitcoin/sign_tx/bitcoin.py @@ -73,12 +73,16 @@ class Bitcoin: tx: SignTx, keychain: Keychain, coin: CoinInfo, - approver: approvers.Approver, + approver: Optional[approvers.Approver], ) -> None: self.tx_info = TxInfo(self, helpers.sanitize_sign_tx(tx, coin)) self.keychain = keychain self.coin = coin - self.approver = approver + + if approver is not None: + self.approver = approver + else: + self.approver = approvers.BasicApprover(tx, coin) # set of indices of inputs which are segwit self.segwit: Set[int] = set() diff --git a/core/src/apps/bitcoin/sign_tx/decred.py b/core/src/apps/bitcoin/sign_tx/decred.py index 6a607b1e6..c27d15b12 100644 --- a/core/src/apps/bitcoin/sign_tx/decred.py +++ b/core/src/apps/bitcoin/sign_tx/decred.py @@ -11,6 +11,7 @@ from apps.common.writers import write_bitcoin_varint from .. import multisig, scripts_decred, writers from ..common import ecdsa_hash_pubkey, ecdsa_sign from . import approvers, helpers, progress +from .approvers import BasicApprover from .bitcoin import Bitcoin DECRED_SERIALIZE_FULL = const(0 << 16) @@ -37,6 +38,16 @@ if False: from .hash143 import Hash143 +class DecredApprover(BasicApprover): + async def add_decred_sstx_submission( + self, txo: TxOutput, script_pubkey: bytes + ) -> None: + # NOTE: The following calls Approver.add_external_output(), not BasicApprover.add_external_output(). + # This is needed to skip calling helpers.confirm_output(), which is what BasicApprover would do. + await super(BasicApprover, self).add_external_output(txo, script_pubkey, None) + await helpers.confirm_decred_sstx_submission(txo, self.coin, self.amount_unit) + + class DecredHash: def __init__(self, h_prefix: HashWriter) -> None: self.h_prefix = h_prefix @@ -65,11 +76,13 @@ class Decred(Bitcoin): tx: SignTx, keychain: Keychain, coin: CoinInfo, - approver: approvers.Approver, + approver: Optional[approvers.Approver], ) -> None: ensure(coin.decred) self.h_prefix = HashWriter(blake256()) + ensure(approver is None) + approver = DecredApprover(tx, coin) super().__init__(tx, keychain, coin, approver) self.write_tx_header(self.serialized_tx, self.tx_info.tx, witness_marker=True) @@ -233,6 +246,8 @@ class Decred(Bitcoin): return scripts_decred.output_script_paytoopreturn(op_return_data) async def approve_staking_ticket(self) -> None: + assert isinstance(self.approver, DecredApprover) + if self.tx_info.tx.outputs_count != 3: raise wire.DataError("Ticket has wrong number of outputs.") diff --git a/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh.py b/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh.py index 405cfb695..afea4d414 100644 --- a/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh.py +++ b/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh.py @@ -30,7 +30,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.bitcoin.keychain import get_schemas_for_coin from apps.bitcoin.sign_tx import helpers, bitcoin -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -160,8 +159,7 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin, None).signer() for request, response in chunks(messages, 2): res = signer.send(request) if isinstance(res, tuple): @@ -289,8 +287,7 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple): @@ -350,8 +347,7 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): if expected_response is None: with self.assertRaises(wire.DataError): diff --git a/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh_grs.py b/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh_grs.py index 319a57bb6..1366eadab 100644 --- a/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh_grs.py +++ b/core/tests/test_apps.bitcoin.segwit.signtx.native_p2wpkh_grs.py @@ -29,7 +29,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.bitcoin.keychain import get_schemas_for_coin from apps.bitcoin.sign_tx import bitcoinlike, helpers -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -161,8 +160,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer() + signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple): @@ -290,8 +288,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer() + signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple): diff --git a/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh.py b/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh.py index 2b2d5edad..8fc6fd41c 100644 --- a/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh.py +++ b/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh.py @@ -30,7 +30,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.bitcoin.keychain import get_schemas_for_coin from apps.bitcoin.sign_tx import bitcoin, helpers -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -157,8 +156,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple): @@ -294,8 +292,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple): @@ -401,8 +398,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoin.Bitcoin(tx, keychain, coin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin, None).signer() i = 0 messages_count = int(len(messages) / 2) for request, expected_response in chunks(messages, 2): diff --git a/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh_grs.py b/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh_grs.py index 95c88251b..e857955a5 100644 --- a/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh_grs.py +++ b/core/tests/test_apps.bitcoin.segwit.signtx.p2wpkh_in_p2sh_grs.py @@ -29,7 +29,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.bitcoin.keychain import get_schemas_for_coin from apps.bitcoin.sign_tx import bitcoinlike, helpers -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -161,8 +160,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer() + signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple): @@ -298,8 +296,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase): ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer() + signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple): diff --git a/core/tests/test_apps.bitcoin.signtx.fee_threshold.py b/core/tests/test_apps.bitcoin.signtx.fee_threshold.py index e44c21947..e8916c501 100644 --- a/core/tests/test_apps.bitcoin.signtx.fee_threshold.py +++ b/core/tests/test_apps.bitcoin.signtx.fee_threshold.py @@ -28,7 +28,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.common.paths import AlwaysMatchingSchema from apps.bitcoin.sign_tx import bitcoin, helpers -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -164,8 +163,7 @@ class TestSignTxFeeThreshold(unittest.TestCase): seed = bip39.seed('alcohol woman abuse must during monitor noble actual mixed trade anger aisle', '') keychain = Keychain(seed, coin_bitcoin.curve_name, [AlwaysMatchingSchema]) - approver = BasicApprover(tx, coin_bitcoin) - signer = bitcoin.Bitcoin(tx, keychain, coin_bitcoin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin_bitcoin, None).signer() for request, response in chunks(messages, 2): res = signer.send(request) if isinstance(res, tuple): diff --git a/core/tests/test_apps.bitcoin.signtx.py b/core/tests/test_apps.bitcoin.signtx.py index 24f819419..d2b384bfe 100644 --- a/core/tests/test_apps.bitcoin.signtx.py +++ b/core/tests/test_apps.bitcoin.signtx.py @@ -28,7 +28,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.bitcoin.keychain import get_schemas_for_coin from apps.bitcoin.sign_tx import bitcoin, helpers -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -110,8 +109,7 @@ class TestSignTx(unittest.TestCase): seed = bip39.seed('alcohol woman abuse must during monitor noble actual mixed trade anger aisle', '') ns = get_schemas_for_coin(coin_bitcoin) keychain = Keychain(seed, coin_bitcoin.curve_name, ns) - approver = BasicApprover(tx, coin_bitcoin) - signer = bitcoin.Bitcoin(tx, keychain, coin_bitcoin, approver).signer() + signer = bitcoin.Bitcoin(tx, keychain, coin_bitcoin, None).signer() for request, response in chunks(messages, 2): res = signer.send(request) diff --git a/core/tests/test_apps.bitcoin.signtx_decred.py b/core/tests/test_apps.bitcoin.signtx_decred.py index 61ce05122..ab2258edd 100644 --- a/core/tests/test_apps.bitcoin.signtx_decred.py +++ b/core/tests/test_apps.bitcoin.signtx_decred.py @@ -1,7 +1,7 @@ from common import * from trezor.utils import chunks -from trezor.crypto import bip32, bip39 +from trezor.crypto import bip39 from trezor.messages.SignTx import SignTx from trezor.messages.TxAckInput import TxAckInput from trezor.messages.TxAckInputWrapper import TxAckInputWrapper @@ -28,7 +28,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.bitcoin.keychain import get_schemas_for_coin from apps.bitcoin.sign_tx import decred, helpers -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -190,8 +189,7 @@ class TestSignTxDecred(unittest.TestCase): ) ns = get_schemas_for_coin(coin_decred) keychain = Keychain(seed, coin_decred.curve_name, ns) - approver = BasicApprover(tx, coin_decred) - signer = decred.Decred(tx, keychain, coin_decred, approver).signer() + signer = decred.Decred(tx, keychain, coin_decred, None).signer() for request, response in chunks(messages, 2): res = signer.send(request) @@ -353,8 +351,7 @@ class TestSignTxDecred(unittest.TestCase): ) ns = get_schemas_for_coin(coin_decred) keychain = Keychain(seed, coin_decred.curve_name, ns) - approver = BasicApprover(tx, coin_decred) - signer = decred.Decred(tx, keychain, coin_decred, approver).signer() + signer = decred.Decred(tx, keychain, coin_decred, None).signer() for request, response in chunks(messages, 2): res = signer.send(request) diff --git a/core/tests/test_apps.bitcoin.signtx_grs.py b/core/tests/test_apps.bitcoin.signtx_grs.py index d7ec509e6..53d519997 100644 --- a/core/tests/test_apps.bitcoin.signtx_grs.py +++ b/core/tests/test_apps.bitcoin.signtx_grs.py @@ -28,7 +28,6 @@ from apps.common import coins from apps.common.keychain import Keychain from apps.bitcoin.keychain import get_schemas_for_coin from apps.bitcoin.sign_tx import bitcoinlike, helpers -from apps.bitcoin.sign_tx.approvers import BasicApprover EMPTY_SERIALIZED = TxRequestSerializedType(serialized_tx=bytearray()) @@ -104,8 +103,7 @@ class TestSignTx_GRS(unittest.TestCase): seed = bip39.seed(' '.join(['all'] * 12), '') ns = get_schemas_for_coin(coin) keychain = Keychain(seed, coin.curve_name, ns) - approver = BasicApprover(tx, coin) - signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, approver).signer() + signer = bitcoinlike.Bitcoinlike(tx, keychain, coin, None).signer() for request, expected_response in chunks(messages, 2): response = signer.send(request) if isinstance(response, tuple):