From 74ed5b70184f140cd976d4077b95c5aebae2754d Mon Sep 17 00:00:00 2001 From: Rafael Korbas Date: Tue, 2 Mar 2021 19:08:14 +0100 Subject: [PATCH] feat(core/cardano): Implement bech32 asset ids based on CIP-0014 --- common/tests/fixtures/cardano/sign_tx.json | 4 +- core/src/apps/cardano/helpers/utils.py | 13 +++++ core/src/apps/cardano/layout.py | 56 +++++++--------------- core/tests/test_apps.cardano.utils.py | 14 +++++- tests/ui_tests/fixtures.json | 4 +- 5 files changed, 48 insertions(+), 43 deletions(-) diff --git a/common/tests/fixtures/cardano/sign_tx.json b/common/tests/fixtures/cardano/sign_tx.json index 10ec49da9..778363211 100644 --- a/common/tests/fixtures/cardano/sign_tx.json +++ b/common/tests/fixtures/cardano/sign_tx.json @@ -663,7 +663,7 @@ "certificates": [], "withdrawals": [], "metadata": "", - "input_flow": [["YES"], ["YES"], ["YES"], ["SWIPE", "YES"], ["SWIPE", "YES"]], + "input_flow": [["YES"], ["SWIPE", "YES"], ["SWIPE", "SWIPE", "YES"], ["SWIPE", "YES"]], "inputs": [ { "path": "m/1852'/1815'/0'/0/0", @@ -711,7 +711,7 @@ "certificates": [], "withdrawals": [], "metadata": "", - "input_flow": [["YES"], ["YES"], ["YES"], ["SWIPE", "YES"], ["YES"], ["SWIPE", "YES"], ["SWIPE", "SWIPE", "YES"], ["YES"]], + "input_flow": [["YES"], ["SWIPE", "YES"], ["SWIPE", "YES"], ["SWIPE", "YES"], ["SWIPE", "SWIPE", "YES"], ["SWIPE", "YES"]], "inputs": [ { "path": "m/1852'/1815'/0'/0/0", diff --git a/core/src/apps/cardano/helpers/utils.py b/core/src/apps/cardano/helpers/utils.py index a5fc40e39..b00ae3b1d 100644 --- a/core/src/apps/cardano/helpers/utils.py +++ b/core/src/apps/cardano/helpers/utils.py @@ -1,3 +1,5 @@ +from trezor.crypto import hashlib + from apps.cardano.helpers.paths import ACCOUNT_PATH_INDEX, unharden from . import bech32 @@ -48,3 +50,14 @@ def format_optional_int(number: Optional[int]) -> str: def format_stake_pool_id(pool_id_bytes: bytes) -> str: return bech32.encode("pool", pool_id_bytes) + + +def format_asset_fingerprint(policy_id: bytes, asset_name_bytes: bytes) -> str: + fingerprint = hashlib.blake2b( + # bytearrays are being promoted to bytes: https://github.com/python/mypy/issues/654 + # but bytearrays are not concatenable, this casting works around this limitation + data=bytes(policy_id) + bytes(asset_name_bytes), + outlen=20, + ).digest() + + return bech32.encode("asset", fingerprint) diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index da27b8258..73cfebede 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -25,6 +25,7 @@ from .address import ( from .helpers import protocol_magics from .helpers.utils import ( format_account_number, + format_asset_fingerprint, format_optional_int, format_stake_pool_id, to_account_path, @@ -42,7 +43,6 @@ if False: from trezor.messages.CardanoPoolOwnerType import CardanoPoolOwnerType from trezor.messages.CardanoPoolMetadataType import CardanoPoolMetadataType from trezor.messages.CardanoAssetGroupType import CardanoAssetGroupType - from trezor.messages.CardanoTokenType import CardanoTokenType from trezor.messages.CardanoAddressParametersType import EnumTypeCardanoAddressType @@ -79,8 +79,7 @@ async def confirm_sending( token_bundle: List[CardanoAssetGroupType], to: str, ) -> None: - for token_group in token_bundle: - await confirm_sending_token_group(ctx, token_group) + await confirm_sending_token_bundle(ctx, token_bundle) page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm sending:") @@ -96,42 +95,23 @@ async def confirm_sending( await require_confirm(ctx, Paginated(pages)) -async def confirm_sending_token_group( - ctx: wire.Context, token_group: CardanoAssetGroupType -) -> None: - page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) - page1.bold("Policy id: ") - page1.mono(hexlify(token_group.policy_id).decode()) - await require_confirm(ctx, page1) - - for token_number, token in enumerate(token_group.tokens, 1): - if is_printable_ascii_bytestring(token.asset_name_bytes): - await confirm_sending_token_ascii(ctx, token, token_number) - else: - await confirm_sending_token_hex(ctx, token, token_number) - - -async def confirm_sending_token_ascii( - ctx: wire.Context, token: CardanoTokenType, token_number: int +async def confirm_sending_token_bundle( + ctx: wire.Context, token_bundle: List[CardanoAssetGroupType] ) -> None: - page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) - page1.normal("Asset #%s name (ASCII):" % (token_number)) - page1.bold(token.asset_name_bytes.decode("ascii")) - page1.normal("Amount sent:") - page1.bold(format_amount(token.amount, 0)) - await require_confirm(ctx, page1) - - -async def confirm_sending_token_hex( - ctx: wire.Context, token: CardanoTokenType, token_number: int -) -> None: - page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) - page1.bold("Asset #%s name (hex):" % (token_number)) - page1.mono(hexlify(token.asset_name_bytes).decode()) - page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) - page2.normal("Amount sent:") - page2.bold(format_amount(token.amount, 0)) - await require_confirm(ctx, Paginated([page1, page2])) + for token_group in token_bundle: + for token in token_group.tokens: + page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) + page1.normal("Asset fingerprint:") + page1.bold( + format_asset_fingerprint( + policy_id=token_group.policy_id, + asset_name_bytes=token.asset_name_bytes, + ) + ) + page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) + page2.normal("Amount sent:") + page2.bold(format_amount(token.amount, 0)) + await require_confirm(ctx, Paginated([page1, page2])) async def show_warning_tx_output_contains_tokens(ctx: wire.Context) -> None: diff --git a/core/tests/test_apps.cardano.utils.py b/core/tests/test_apps.cardano.utils.py index f70c6ca1a..434fd70cf 100644 --- a/core/tests/test_apps.cardano.utils.py +++ b/core/tests/test_apps.cardano.utils.py @@ -1,7 +1,7 @@ from common import * if not utils.BITCOIN_ONLY: - from apps.cardano.helpers.utils import variable_length_encode + from apps.cardano.helpers.utils import variable_length_encode, format_asset_fingerprint @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") @@ -28,6 +28,18 @@ class TestCardanoUtils(unittest.TestCase): with self.assertRaises(ValueError): variable_length_encode(-1) + def test_format_asset_fingerprint(self): + # source: https://github.com/cardano-foundation/CIPs/pull/64 + test_vectors = [ + (("7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373", ""), "asset1rjklcrnsdzqp65wjgrg55sy9723kw09mlgvlc3"), + (("7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373", "504154415445"), "asset13n25uv0yaf5kus35fm2k86cqy60z58d9xmde92"), + (("1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209", "7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373"), "asset1aqrdypg669jgazruv5ah07nuyqe0wxjhe2el6f"), + ] + + for params, expected in test_vectors: + actual = format_asset_fingerprint(policy_id=unhexlify(params[0]), asset_name_bytes=unhexlify(params[1])) + self.assertEqual(actual, expected) + if __name__ == '__main__': unittest.main() diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index a2d91be8e..c08945140 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -5,8 +5,8 @@ "cardano-test_sign_tx.py::test_cardano_sign_tx[mainnet_transaction_with_multiple_inputs]": "5ba2589aeda7cb2b707b5dd0d40ac26a5abe6eb0c3ec3d47d834701ef07a42bc", "cardano-test_sign_tx.py::test_cardano_sign_tx[mainnet_transaction_without_change0]": "5ba2589aeda7cb2b707b5dd0d40ac26a5abe6eb0c3ec3d47d834701ef07a42bc", "cardano-test_sign_tx.py::test_cardano_sign_tx[mainnet_transaction_without_change1]": "c57c4097446de48b1f79850854a83410816ac05c8a7476452771cdc71e0aeefa", -"cardano-test_sign_tx.py::test_cardano_sign_tx[mary_era_transaction_with_different_policies_-1dbb1bfb": "0f14d371c83adf4ceaa05530581df86eb653b9e8226be61fcd2bc1cc97e3581a", -"cardano-test_sign_tx.py::test_cardano_sign_tx[mary_era_transaction_with_multiasset_output]": "a24edeaaefca10b45235836264b8385148048a23bd34c56570c3de69de4f3d06", +"cardano-test_sign_tx.py::test_cardano_sign_tx[mary_era_transaction_with_different_policies_-1dbb1bfb": "02f8517926fe8d38d96606929df43372d8949e917299199d37e648672fed9256", +"cardano-test_sign_tx.py::test_cardano_sign_tx[mary_era_transaction_with_multiasset_output]": "d0a58ab8b68fcd9f1191f309de52efb6be9157383357f5f9c0e0d116fccc08e4", "cardano-test_sign_tx.py::test_cardano_sign_tx[mary_era_transaction_with_no_ttl-validity_start]": "db76676358164a3b3dab5148f09ba5515d079212366b79c44f95e2ba613abc71", "cardano-test_sign_tx.py::test_cardano_sign_tx[sample_stake_pool_registration_certificate]": "f56482d4a0f2c07fe04291a7d9c29ed4aedfd31e6a80fed1563d80e3a4c4005c", "cardano-test_sign_tx.py::test_cardano_sign_tx[sample_stake_pool_registration_certificate_wi-d3427614": "629ccaa5b660247843b366582c1bc505831c1800e028474a784b0f8b35fc473a",