From b5f3511c1ca318fd89e0d2ef3c804c9258d69fd7 Mon Sep 17 00:00:00 2001 From: gabrielkerekes Date: Wed, 29 Jul 2020 15:28:41 +0200 Subject: [PATCH] Add support for script addresses in tx outputs --- common/protob/messages-cardano.proto | 7 + core/src/apps/cardano/address.py | 40 ++--- .../messages/CardanoAddressParametersType.py | 4 +- .../src/trezor/messages/CardanoAddressType.py | 6 + .../messages/CardanoAddressParametersType.py | 4 +- .../trezorlib/messages/CardanoAddressType.py | 6 + .../test_msg_cardano_sign_transaction.py | 150 +++++++----------- 7 files changed, 94 insertions(+), 123 deletions(-) diff --git a/common/protob/messages-cardano.proto b/common/protob/messages-cardano.proto index bc076c093..f54eaca83 100644 --- a/common/protob/messages-cardano.proto +++ b/common/protob/messages-cardano.proto @@ -9,13 +9,20 @@ import "messages-common.proto"; /** * Values correspond to address header values given by the spec. + * Script addresses are only supported in transaction outputs. */ enum CardanoAddressType { BASE = 0; + BASE_SCRIPT_KEY = 1; + BASE_KEY_SCRIPT = 2; + BASE_SCRIPT_SCRIPT = 3; POINTER = 4; + POINTER_SCRIPT = 5; ENTERPRISE = 6; + ENTERPRISE_SCRIPT = 7; BYRON = 8; REWARD = 14; + REWARD_SCRIPT = 15; } /** diff --git a/core/src/apps/cardano/address.py b/core/src/apps/cardano/address.py index 718320de6..d0a5f7166 100644 --- a/core/src/apps/cardano/address.py +++ b/core/src/apps/cardano/address.py @@ -20,31 +20,18 @@ if False: ADDRESS_TYPES_SHELLEY = ( CardanoAddressType.BASE, + CardanoAddressType.BASE_SCRIPT_KEY, + CardanoAddressType.BASE_KEY_SCRIPT, + CardanoAddressType.BASE_SCRIPT_SCRIPT, CardanoAddressType.POINTER, + CardanoAddressType.POINTER_SCRIPT, CardanoAddressType.ENTERPRISE, + CardanoAddressType.ENTERPRISE_SCRIPT, CardanoAddressType.REWARD, + CardanoAddressType.REWARD_SCRIPT, ) - -HEADER_LENGTH = 1 -HASH_LENGTH = 28 -MIN_POINTER_SIZE = 0 -MAX_POINTER_SIZE = 12 - -ADDRESS_BYTES_MIN_LENGTHS = { - CardanoAddressType.BASE: HEADER_LENGTH + HASH_LENGTH + HASH_LENGTH, - CardanoAddressType.POINTER: HEADER_LENGTH + HASH_LENGTH + MIN_POINTER_SIZE, - CardanoAddressType.ENTERPRISE: HEADER_LENGTH + HASH_LENGTH, - CardanoAddressType.REWARD: HEADER_LENGTH + HASH_LENGTH, -} - -ADDRESS_BYTES_MAX_LENGTHS = { - CardanoAddressType.BASE: ADDRESS_BYTES_MIN_LENGTHS[CardanoAddressType.BASE], - CardanoAddressType.POINTER: HEADER_LENGTH + HASH_LENGTH + MAX_POINTER_SIZE, - CardanoAddressType.ENTERPRISE: ADDRESS_BYTES_MIN_LENGTHS[ - CardanoAddressType.ENTERPRISE - ], - CardanoAddressType.REWARD: ADDRESS_BYTES_MIN_LENGTHS[CardanoAddressType.REWARD], -} +MIN_ADDRESS_BYTES_LENGTH = 29 +MAX_ADDRESS_BYTES_LENGTH = 65 def validate_full_path(path: List[int]) -> bool: @@ -104,7 +91,10 @@ def _validate_output_shelley_address( ) -> None: address_type = _get_address_type(address_bytes) # reward address cannot be an output address - if address_type == CardanoAddressType.REWARD: + if ( + address_type == CardanoAddressType.REWARD + or address_type == CardanoAddressType.REWARD_SCRIPT + ): raise INVALID_ADDRESS _validate_address_size(address_bytes, address_type) @@ -115,11 +105,7 @@ def _validate_output_shelley_address( def _validate_address_size( address_bytes: bytes, address_type: EnumTypeCardanoAddressType ) -> None: - if not ( - ADDRESS_BYTES_MIN_LENGTHS[address_type] - <= len(address_bytes) - <= ADDRESS_BYTES_MAX_LENGTHS[address_type] - ): + if not (MIN_ADDRESS_BYTES_LENGTH <= len(address_bytes) <= MAX_ADDRESS_BYTES_LENGTH): raise INVALID_ADDRESS diff --git a/core/src/trezor/messages/CardanoAddressParametersType.py b/core/src/trezor/messages/CardanoAddressParametersType.py index 5cae7b764..ae6f4c84a 100644 --- a/core/src/trezor/messages/CardanoAddressParametersType.py +++ b/core/src/trezor/messages/CardanoAddressParametersType.py @@ -8,7 +8,7 @@ if __debug__: try: from typing import Dict, List # noqa: F401 from typing_extensions import Literal # noqa: F401 - EnumTypeCardanoAddressType = Literal[0, 4, 6, 8, 14] + EnumTypeCardanoAddressType = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15] except ImportError: pass @@ -32,7 +32,7 @@ class CardanoAddressParametersType(p.MessageType): @classmethod def get_fields(cls) -> Dict: return { - 1: ('address_type', p.EnumType("CardanoAddressType", (0, 4, 6, 8, 14)), 0), + 1: ('address_type', p.EnumType("CardanoAddressType", (0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15)), 0), 2: ('address_n', p.UVarintType, p.FLAG_REPEATED), 3: ('address_n_staking', p.UVarintType, p.FLAG_REPEATED), 4: ('staking_key_hash', p.BytesType, 0), diff --git a/core/src/trezor/messages/CardanoAddressType.py b/core/src/trezor/messages/CardanoAddressType.py index 804184b6f..4867b775c 100644 --- a/core/src/trezor/messages/CardanoAddressType.py +++ b/core/src/trezor/messages/CardanoAddressType.py @@ -4,7 +4,13 @@ if False: from typing_extensions import Literal BASE = 0 # type: Literal[0] +BASE_SCRIPT_KEY = 1 # type: Literal[1] +BASE_KEY_SCRIPT = 2 # type: Literal[2] +BASE_SCRIPT_SCRIPT = 3 # type: Literal[3] POINTER = 4 # type: Literal[4] +POINTER_SCRIPT = 5 # type: Literal[5] ENTERPRISE = 6 # type: Literal[6] +ENTERPRISE_SCRIPT = 7 # type: Literal[7] BYRON = 8 # type: Literal[8] REWARD = 14 # type: Literal[14] +REWARD_SCRIPT = 15 # type: Literal[15] diff --git a/python/src/trezorlib/messages/CardanoAddressParametersType.py b/python/src/trezorlib/messages/CardanoAddressParametersType.py index b3f7ece94..56e22280c 100644 --- a/python/src/trezorlib/messages/CardanoAddressParametersType.py +++ b/python/src/trezorlib/messages/CardanoAddressParametersType.py @@ -8,7 +8,7 @@ if __debug__: try: from typing import Dict, List # noqa: F401 from typing_extensions import Literal # noqa: F401 - EnumTypeCardanoAddressType = Literal[0, 4, 6, 8, 14] + EnumTypeCardanoAddressType = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15] except ImportError: pass @@ -32,7 +32,7 @@ class CardanoAddressParametersType(p.MessageType): @classmethod def get_fields(cls) -> Dict: return { - 1: ('address_type', p.EnumType("CardanoAddressType", (0, 4, 6, 8, 14)), 0), + 1: ('address_type', p.EnumType("CardanoAddressType", (0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15)), 0), 2: ('address_n', p.UVarintType, p.FLAG_REPEATED), 3: ('address_n_staking', p.UVarintType, p.FLAG_REPEATED), 4: ('staking_key_hash', p.BytesType, 0), diff --git a/python/src/trezorlib/messages/CardanoAddressType.py b/python/src/trezorlib/messages/CardanoAddressType.py index 804184b6f..4867b775c 100644 --- a/python/src/trezorlib/messages/CardanoAddressType.py +++ b/python/src/trezorlib/messages/CardanoAddressType.py @@ -4,7 +4,13 @@ if False: from typing_extensions import Literal BASE = 0 # type: Literal[0] +BASE_SCRIPT_KEY = 1 # type: Literal[1] +BASE_KEY_SCRIPT = 2 # type: Literal[2] +BASE_SCRIPT_SCRIPT = 3 # type: Literal[3] POINTER = 4 # type: Literal[4] +POINTER_SCRIPT = 5 # type: Literal[5] ENTERPRISE = 6 # type: Literal[6] +ENTERPRISE_SCRIPT = 7 # type: Literal[7] BYRON = 8 # type: Literal[8] REWARD = 14 # type: Literal[14] +REWARD_SCRIPT = 15 # type: Literal[15] diff --git a/tests/device_tests/test_msg_cardano_sign_transaction.py b/tests/device_tests/test_msg_cardano_sign_transaction.py index 5f1fc376a..41ce4e9e4 100644 --- a/tests/device_tests/test_msg_cardano_sign_transaction.py +++ b/tests/device_tests/test_msg_cardano_sign_transaction.py @@ -59,6 +59,10 @@ SAMPLE_OUTPUTS = { "address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r", "amount": "1", }, + "base_address_with_script_output": { + "address": "addr1z90z7zqwhya6mpk5q929ur897g3pp9kkgalpreny8y304r2dcrtx0sf3dluyu4erzr3xtmdnzvcyfzekkuteu2xagx0qeva0pr", + "amount": "7120787", + }, "base_address_change_output": { "addressType": 0, "path": "m/1852'/1815'/0'/0/0", @@ -96,34 +100,23 @@ SAMPLE_OUTPUTS = { "address": "Ae2tdPwUPEZ5YUb8sM3eS8JqKgrRLzhiu71crfuH2MFtqaYr5ACNRZR3Mbm", "amount": "3003112", }, - "invalid_base_address_too_short": { - "address": "addr1q89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jrlrqcfqqj922xhxkn6twlq2wn4q50q352annk3903tj00h45mggqvpjcf", - "amount": "3003112", - }, - "invalid_base_address_too_long": { - "address": "addr1q89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jrlrqcfqqj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfm5zhnjqfc", - "amount": "3003112", - }, - "invalid_pointer_address_too_short": { - "address": "addr1g89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jrlrqcgrfjd3l", - "amount": "3003112", - }, - "invalid_pointer_address_too_long": { - "address": "addr1g89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jrlrqcfqysszqgqqysszqgqqysszqgqqzpqv0wa7", - "amount": "3003112", - }, - "invalid_enterprise_address_too_short": { - "address": "addr1v89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jrlrqcg0c7m2w", + "invalid_address_too_short": { + "address": "addr1q89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jry45scvehcr", "amount": "3003112", }, - "invalid_enterprise_address_too_long": { - "address": "addr1v89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jrlrqcfqzp9v4srv", + "invalid_address_too_long": { + "address": "addr1q89s8py7y68e3x66sscs0wkhlg5ssfrfs65084jrlrqcfqqj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfm5z3vcwsfrvkr5zglq4rxu", "amount": "3003112", }, "large_simple_byron_output": { "address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2", "amount": "449999999199999999", }, + # address type 10 + "unsupported_address_type": { + "address": "addr1590z7zqwhya6mpk5q929ur897g3pp9kkgalpreny8y304r2dcrtx0sf3dluyu4erzr3xtmdnzvcyfzekkuteu2xagx0qt7gvvj", + "amount": "3003112", + }, "testnet_output": { "address": "2657WMsDfac7BteXkJq5Jzdog4h47fPbkwUM49isuWbYAr2cFRHa3rURP236h9PBe", "amount": "3003112", @@ -205,6 +198,30 @@ VALID_VECTORS = [ # tx body "83a400818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018282583901eb0baa5e570cffbe2934db29df0b6a3d7c0430ee65d4c3a7ab2fefb91bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff018258390180f9e2c88e6c817008f3a812ed889b4a4da8e0bd103f86e7335422aa122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b42771a006ca79302182a030aa100818258205d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c158406a78f07836dcf4a303448d2b16b217265a9226be3984a69a04dba5d04f4dbb2a47b5e1cbb345f474c0b9634a2f37b921ab26e6a65d5dfd015dacb4455fb8430af6", ), + # simple transaction with base script address change output + ( + # protocol magic + PROTOCOL_MAGICS["mainnet"], + # network id + NETWORK_IDS["mainnet"], + # inputs + [SAMPLE_INPUTS["shelley_input"]], + # outputs + [ + SAMPLE_OUTPUTS["base_address_with_script_output"], + SAMPLE_OUTPUTS["base_address_change_output"], + ], + # fee + 42, + # ttl + 10, + # input flow + [[InputAction.SWIPE, InputAction.YES], [InputAction.SWIPE, InputAction.YES]], + # tx hash + "5ddbb530b8a89e2b08fc91db03950c876c4a9c1c3fb6e628c4cab638b1c97648", + # tx body + "83a400818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7000182825839115e2f080eb93bad86d401545e0ce5f2221096d6477e11e6643922fa8d4dc0d667c1316ff84e572310e265edb31330448b36b7179e28dd419e1a006ca7938258390180f9e2c88e6c817008f3a812ed889b4a4da8e0bd103f86e7335422aa122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b42771a006ca79302182a030aa100818258205d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c15840e0bdaa59016f2a521d31179b60364eacdcb53c34ae01c56b339afa62d312f5f89783579691cac777e3d5f2e7810aa8fe554ba545a8d1578c55405af5ae51b30ff6", + ), # simple transaction with base address change output with staking key hash ( # protocol magic @@ -373,7 +390,7 @@ INVALID_VECTORS = [ # error message "Invalid address", ), - # Output base address is too short + # Output address is too short ( # protocol magic (mainnet) PROTOCOL_MAGICS["mainnet"], @@ -382,7 +399,7 @@ INVALID_VECTORS = [ # inputs [SAMPLE_INPUTS["shelley_input"]], # outputs - [SAMPLE_OUTPUTS["invalid_base_address_too_short"]], + [SAMPLE_OUTPUTS["invalid_address_too_short"]], # fee 42, # ttl @@ -390,7 +407,7 @@ INVALID_VECTORS = [ # error message "Invalid address", ), - # Output base address is too long + # Output address is too long ( # protocol magic (mainnet) PROTOCOL_MAGICS["mainnet"], @@ -399,75 +416,7 @@ INVALID_VECTORS = [ # inputs [SAMPLE_INPUTS["shelley_input"]], # outputs - [SAMPLE_OUTPUTS["invalid_base_address_too_long"]], - # fee - 42, - # ttl - 10, - # error message - "Invalid address", - ), - # Output pointer address is too short - ( - # protocol magic (mainnet) - PROTOCOL_MAGICS["mainnet"], - # network id - NETWORK_IDS["mainnet"], - # inputs - [SAMPLE_INPUTS["shelley_input"]], - # outputs - [SAMPLE_OUTPUTS["invalid_pointer_address_too_short"]], - # fee - 42, - # ttl - 10, - # error message - "Invalid address", - ), - # Output pointer address is too long - ( - # protocol magic (mainnet) - PROTOCOL_MAGICS["mainnet"], - # network id - NETWORK_IDS["mainnet"], - # inputs - [SAMPLE_INPUTS["shelley_input"]], - # outputs - [SAMPLE_OUTPUTS["invalid_pointer_address_too_long"]], - # fee - 42, - # ttl - 10, - # error message - "Invalid address", - ), - # Output enterprise address is too short - ( - # protocol magic (mainnet) - PROTOCOL_MAGICS["mainnet"], - # network id - NETWORK_IDS["mainnet"], - # inputs - [SAMPLE_INPUTS["shelley_input"]], - # outputs - [SAMPLE_OUTPUTS["invalid_enterprise_address_too_short"]], - # fee - 42, - # ttl - 10, - # error message - "Invalid address", - ), - # Output enterprise address is too long - ( - # protocol magic (mainnet) - PROTOCOL_MAGICS["mainnet"], - # network id - NETWORK_IDS["mainnet"], - # inputs - [SAMPLE_INPUTS["shelley_input"]], - # outputs - [SAMPLE_OUTPUTS["invalid_enterprise_address_too_long"]], + [SAMPLE_OUTPUTS["invalid_address_too_long"]], # fee 42, # ttl @@ -614,6 +563,23 @@ INVALID_VECTORS = [ # error message "Invalid network id/protocol magic combination!", ), + # Unsupported address type + ( + # protocol magic + PROTOCOL_MAGICS["mainnet"], + # network id + NETWORK_IDS["mainnet"], + # inputs + [SAMPLE_INPUTS["shelley_input"]], + # outputs + [SAMPLE_OUTPUTS["unsupported_address_type"]], + # fee + 42, + # ttl + 10, + # error message + "Invalid address", + ), ]