diff --git a/common/tests/fixtures/ethereum/definitions-latest.zip b/common/tests/fixtures/ethereum/definitions-latest.zip deleted file mode 100644 index 76b7cc92ff..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest.zip and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/network.dat deleted file mode 100644 index 4e6ee52253..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9.dat deleted file mode 100644 index f52da27ccf..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_d0d6d6c5fe4a677d343cc433536bb717bae167dd.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_d0d6d6c5fe4a677d343cc433536bb717bae167dd.dat deleted file mode 100644 index 9decc8d040..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_d0d6d6c5fe4a677d343cc433536bb717bae167dd.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_dac17f958d2ee523a2206206994597c13d831ec7.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_dac17f958d2ee523a2206206994597c13d831ec7.dat deleted file mode 100644 index 34f78aabb6..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/1/token_dac17f958d2ee523a2206206994597c13d831ec7.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/11297108109/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/11297108109/network.dat deleted file mode 100644 index 6bed959bfb..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/11297108109/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/28945486/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/28945486/network.dat deleted file mode 100644 index 2d99d58131..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/28945486/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/3125659152/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/3125659152/network.dat deleted file mode 100644 index f31796c7c8..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/3125659152/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/4/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/4/network.dat deleted file mode 100644 index d60644ffa1..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/4/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/4/token_275a5b346599b56917e7b1c9de019dcf9ead861a.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/4/token_275a5b346599b56917e7b1c9de019dcf9ead861a.dat deleted file mode 100644 index acc5324f80..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/4/token_275a5b346599b56917e7b1c9de019dcf9ead861a.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/61/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/61/network.dat deleted file mode 100644 index 0a9e34949f..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/61/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/8/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/8/network.dat deleted file mode 100644 index 7de43cecd4..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/8/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/8/token_20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/8/token_20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6.dat deleted file mode 100644 index 09d7ebe6de..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/8/token_20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/888/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/888/network.dat deleted file mode 100644 index f52879222a..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_chain_id/888/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/108/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_slip44/108/network.dat deleted file mode 100644 index 7de43cecd4..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/108/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/5718350/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_slip44/5718350/network.dat deleted file mode 100644 index f52879222a..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/5718350/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/6060/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_slip44/6060/network.dat deleted file mode 100644 index b0e935f967..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/6060/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/61/network.dat b/common/tests/fixtures/ethereum/definitions-latest/by_slip44/61/network.dat deleted file mode 100644 index 0a9e34949f..0000000000 Binary files a/common/tests/fixtures/ethereum/definitions-latest/by_slip44/61/network.dat and /dev/null differ diff --git a/common/tests/fixtures/ethereum/getaddress.failed.json b/common/tests/fixtures/ethereum/getaddress.failed.json new file mode 100644 index 0000000000..ae68d39424 --- /dev/null +++ b/common/tests/fixtures/ethereum/getaddress.failed.json @@ -0,0 +1,20 @@ +{ + "setup": { + "mnemonic": "all all all all all all all all all all all all", + "passphrase": "" + }, + "tests": [ + { + "name": "missing_extern_definition_GoChain", + "parameters": { + "path": "m/44'/6060'/0'/0/0", + "definitions": { + "slip44": -1 + } + }, + "result": { + "error": "DataError:.*Forbidden key path" + } + } + ] +} diff --git a/common/tests/fixtures/ethereum/sign_tx_definitions.failed.json b/common/tests/fixtures/ethereum/sign_tx_definitions.failed.json new file mode 100644 index 0000000000..446ecc7770 --- /dev/null +++ b/common/tests/fixtures/ethereum/sign_tx_definitions.failed.json @@ -0,0 +1,132 @@ +{ + "setup": { + "mnemonic": "all all all all all all all all all all all all", + "passphrase": "" + }, + "tests": [ + { + "name": "builtin_Ethereum_extern_adChain_outdated_token", + "parameters": { + "chain_id": 1, + "path": "m/44'/60'/0'/0/0", + "nonce": "0x0", + "gas_price": "0x14", + "gas_limit": "0x14", + "value": "0x0", + "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd", + "tx_type": null, + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "definitions": { + "timestamp": 0 + } + }, + "result": { + "error": "DataError:.*Definition is outdated" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_missing_network", + "parameters": { + "chain_id": 8, + "path": "m/44'/108'/0'/0/0", + "nonce": "0x0", + "gas_price": "0x14", + "gas_limit": "0x14", + "value": "0x0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "tx_type": null, + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "definitions": { + "chain_id": -1 + } + }, + "result": { + "error": "DataError:.*" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_missing_network_token", + "parameters": { + "chain_id": 8, + "path": "m/44'/108'/0'/0/0", + "nonce": "0x0", + "gas_price": "0x14", + "gas_limit": "0x14", + "value": "0x0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "tx_type": null, + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "definitions": { + "chain_id": -1, + "to_address": "" + } + }, + "result": { + "error": "DataError:.*Forbidden key path" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_wrong_network", + "parameters": { + "chain_id": 8, + "path": "m/44'/108'/0'/0/0", + "nonce": "0x0", + "gas_price": "0x14", + "gas_limit": "0x14", + "value": "0x0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "tx_type": null, + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "definitions": { + "chain_id": 1 + } + }, + "result": { + "error": "DataError:.*Network definition mismatch" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_wrong_network_token", + "parameters": { + "chain_id": 8, + "path": "m/44'/108'/0'/0/0", + "nonce": "0x0", + "gas_price": "0x14", + "gas_limit": "0x14", + "value": "0x0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "tx_type": null, + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "definitions": { + "chain_id": 1, + "token_chain_id": 1, + "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd" + } + }, + "result": { + "error": "DataError:.*Network definition mismatch" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_wrong_token", + "parameters": { + "chain_id": 8, + "path": "m/44'/108'/0'/0/0", + "nonce": "0x0", + "gas_price": "0x14", + "gas_limit": "0x14", + "value": "0x0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "tx_type": null, + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "definitions": { + "token_chain_id": 1, + "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd" + } + }, + "result": { + "error": "DataError:.*Token definition mismatch" + } + } + ] +} diff --git a/common/tests/fixtures/ethereum/sign_tx_eip1559_definitions.failed.json b/common/tests/fixtures/ethereum/sign_tx_eip1559_definitions.failed.json new file mode 100644 index 0000000000..787392eebf --- /dev/null +++ b/common/tests/fixtures/ethereum/sign_tx_eip1559_definitions.failed.json @@ -0,0 +1,112 @@ +{ + "setup": { + "mnemonic": "all all all all all all all all all all all all", + "passphrase": "" + }, + "tests": [ + { + "name": "extern_Ubiq_extern_Sphere_missing_network", + "parameters": { + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "path": "m/44'/108'/0'/0/0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "chain_id": 8, + "nonce": "0x0", + "gas_limit": "0x14", + "max_gas_fee": "0x14", + "max_priority_fee": "0x1", + "value": "0x0", + "definitions": { + "chain_id": -1 + } + }, + "result": { + "error": "DataError:.*" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_missing_network_token", + "parameters": { + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "path": "m/44'/108'/0'/0/0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "chain_id": 8, + "nonce": "0x0", + "gas_limit": "0x14", + "max_gas_fee": "0x14", + "max_priority_fee": "0x1", + "value": "0x0", + "definitions": { + "chain_id": -1, + "to_address": "" + } + }, + "result": { + "error": "DataError:.*Forbidden key path" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_wrong_network", + "parameters": { + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "path": "m/44'/108'/0'/0/0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "chain_id": 8, + "nonce": "0x0", + "gas_limit": "0x14", + "max_gas_fee": "0x14", + "max_priority_fee": "0x1", + "value": "0x0", + "definitions": { + "chain_id": 1 + } + }, + "result": { + "error": "DataError:.*Network definition mismatch" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_wrong_network_token", + "parameters": { + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "path": "m/44'/108'/0'/0/0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "chain_id": 8, + "nonce": "0x0", + "gas_limit": "0x14", + "max_gas_fee": "0x14", + "max_priority_fee": "0x1", + "value": "0x0", + "definitions": { + "chain_id": 1, + "token_chain_id": 1, + "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd" + } + }, + "result": { + "error": "DataError:.*Network definition mismatch" + } + }, + { + "name": "extern_Ubiq_extern_Sphere_wrong_token", + "parameters": { + "data": "a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "path": "m/44'/108'/0'/0/0", + "to_address": "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6", + "chain_id": 8, + "nonce": "0x0", + "gas_limit": "0x14", + "max_gas_fee": "0x14", + "max_priority_fee": "0x1", + "value": "0x0", + "definitions": { + "token_chain_id": 1, + "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd" + } + }, + "result": { + "error": "DataError:.*Token definition mismatch" + } + } + ] +} diff --git a/common/tests/fixtures/ethereum/sign_typed_data.failed.json b/common/tests/fixtures/ethereum/sign_typed_data.failed.json new file mode 100644 index 0000000000..66f00995e6 --- /dev/null +++ b/common/tests/fixtures/ethereum/sign_typed_data.failed.json @@ -0,0 +1,32 @@ +{ + "setup": { + "mnemonic": "all all all all all all all all all all all all", + "passphrase": "" + }, + "tests": [ + { + "name": "missing_extern_definition_GoChain", + "comment": "Bare minimum EIP-712 message (domain only)", + "parameters": { + "path": "m/44'/6060'/0'/0/0", + "metamask_v4_compat": true, + "data": { + "types": { + "EIP712Domain": [] + }, + "primaryType": "EIP712Domain", + "message": {}, + "domain": {} + }, + "message_hash": null, + "domain_separator_hash": "0x6192106f129ce05c9075d319c1fa6ea9b3ae37cbd0c1ef92e2be7137bb07baa1", + "definitions": { + "slip44": -1 + } + }, + "result": { + "error": "DataError:.*Forbidden key path" + } + } + ] +} diff --git a/common/tests/fixtures/ethereum/signmessage.failed.json b/common/tests/fixtures/ethereum/signmessage.failed.json new file mode 100644 index 0000000000..69583ab375 --- /dev/null +++ b/common/tests/fixtures/ethereum/signmessage.failed.json @@ -0,0 +1,21 @@ +{ + "setup": { + "mnemonic": "all all all all all all all all all all all all", + "passphrase": "" + }, + "tests": [ + { + "name": "missing_extern_definition_GoChain", + "parameters": { + "msg": "This is an example of a signed message.", + "path": "m/44'/6060'/0'/0/0", + "definitions": { + "slip44": -1 + } + }, + "result": { + "error": "DataError:.*Forbidden key path" + } + } + ] +} diff --git a/core/src/apps/ethereum/definitions.py b/core/src/apps/ethereum/definitions.py index 44a361de12..6a371f7810 100644 --- a/core/src/apps/ethereum/definitions.py +++ b/core/src/apps/ethereum/definitions.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: DefType = TypeVar("DefType", EthereumNetworkInfo, EthereumTokenInfo) DEFINITIONS_PUBLIC_KEY = b"" -MIN_DATA_VERSION = 1 # TODO: update +MIN_DATA_VERSION = 1 # TODO: update FORMAT_VERSION = b"trzd1" if __debug__: diff --git a/core/tests/common.py b/core/tests/common.py index 2fb8f0d1d8..3368467459 100644 --- a/core/tests/common.py +++ b/core/tests/common.py @@ -9,8 +9,6 @@ import unittest # noqa: F401 from trezor import utils # noqa: F401 from apps.common.paths import HARDENED -COMMON_FIXTURES_DIR='/'.join(__file__.split('/')[0:-1])+"/../../common/tests/fixtures" - def H_(x: int) -> int: """ Shortcut function that "hardens" a number in a BIP44 path. diff --git a/core/tests/ethereum_common.py b/core/tests/ethereum_common.py index 7cd49df769..3339f315c1 100644 --- a/core/tests/ethereum_common.py +++ b/core/tests/ethereum_common.py @@ -1,20 +1,14 @@ -import uio from ubinascii import unhexlify # noqa: F401 from apps.ethereum import networks, tokens -from common import COMMON_FIXTURES_DIR -from trezor import messages +from trezor import messages, protobuf +from trezor.enums import EthereumDefinitionType +from trezor.crypto.curve import ed25519 +from trezor.crypto.hashlib import sha256 - -FIXTURES_DEFINITIONS_DIR=COMMON_FIXTURES_DIR + "/ethereum/definitions-latest" -# following constants for "DEFS" were copied from "../../python/src/trezorlib/ethereum.py" -DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE = "by_chain_id" -DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE = "by_slip44" -DEFS_NETWORK_URI_NAME = "network.dat" -DEFS_TOKEN_URI_NAME = "token_{hex_address}.dat" - -EXPECTED_FORMAT_VERSION = 1 -EXPECTED_DATA_VERSION = 1663054984 # unix epoch time +DEFINITIONS_DEV_PRIVATE_KEY = unhexlify( + "4141414141414141414141414141414141414141414141414141414141414141" +) NETWORKS = { @@ -34,12 +28,6 @@ NETWORKS = { } -SLIP44_TO_CHAIN_ID_MAP = { - 1: 31, - 108: 8, -} - - TOKENS = { # chain_id: { address: token info, address: token info,... } 1: { @@ -95,9 +83,9 @@ def get_reference_ethereum_network_info(chain_id: int | None = None, slip44: int network = networks.by_slip44(slip44) if network is None: - cid = SLIP44_TO_CHAIN_ID_MAP.get(slip44) - if cid is not None: - return NETWORKS.get(cid[0]) + for _, network in NETWORKS.items(): + if network.slip44 == slip44: + return network return network if network else networks.UNKNOWN_NETWORK @@ -111,48 +99,44 @@ def get_reference_ethereum_token_info(chain_id: int, token_address: str) -> mess return token if token else tokens.UNKNOWN_TOKEN -def get_encoded_network_definition( - chain_id: int | None = None, - slip44: int | None = None, -) -> bytes | None: - if not ((chain_id is None) != (slip44 is None)): # not XOR - raise ValueError( - "Exactly one of chain_id or slip44 parameters are needed to construct network definition path." - ) - - path = "" - - if chain_id is not None: - path = "/".join([FIXTURES_DEFINITIONS_DIR, DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE, str(chain_id),DEFS_NETWORK_URI_NAME]) +def _serialize_eth_info( + info: messages.EthereumNetworkInfo | messages.EthereumTokenInfo, + data_type_num: messages.EthereumDefinitionType, + timestamp: int | None = None, +) -> bytes: + ser = b"trzd1" + ser += data_type_num.to_bytes(1, "big") + if timestamp is not None: + ser += timestamp.to_bytes(4, "big") else: - path = "/".join([FIXTURES_DEFINITIONS_DIR, DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE, str(slip44), DEFS_NETWORK_URI_NAME]) + # set data version to max to avoid "outdated" definition errors + ser += b'\xff' * 4 - return _get_definition_from_path(path) + # serialize message + length = protobuf.encoded_length(info) + buffer = bytearray(length) + protobuf.encode(buffer, info) + # write the length of encoded protobuf message + ser += length.to_bytes(2, "big") + ser += buffer + + # add Merkle tree proof length and signature + hash = sha256(b"\x00" + ser).digest() + ser += b'\x00' + ser += ed25519.sign(DEFINITIONS_DEV_PRIVATE_KEY, hash) + + return ser + + +def get_encoded_network_definition( + network_info: messages.EthereumNetworkInfo, + timestamp: int | None = None, +) -> bytes: + return _serialize_eth_info(network_info, EthereumDefinitionType.NETWORK, timestamp) def get_encoded_token_definition( - chain_id: int = None, - token_address: str = None, -) -> bytes | None: - if chain_id is None or token_address is None: - raise ValueError( - "Both chain_id and token_address parameters are needed to construct token definition path." - ) - - addr = token_address.lower() - if addr.startswith("0x"): - addr = addr[2:] - - path = "/".join([FIXTURES_DEFINITIONS_DIR, DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE, str(chain_id), DEFS_TOKEN_URI_NAME.format(hex_address=addr)]) - return _get_definition_from_path(path) - - -def _get_definition_from_path( - path: str, -) -> bytes | None: - try: - with uio.open(path, mode="rb") as f: - b = f.read() - return b - except OSError: - return None + token_info: messages.EthereumTokenInfo, + timestamp: int | None = None, +) -> bytes: + return _serialize_eth_info(token_info, EthereumDefinitionType.TOKEN, timestamp) diff --git a/core/tests/test_apps.ethereum.definitions.py b/core/tests/test_apps.ethereum.definitions.py index 446589ffa7..26fbc15cd6 100644 --- a/core/tests/test_apps.ethereum.definitions.py +++ b/core/tests/test_apps.ethereum.definitions.py @@ -12,13 +12,9 @@ if not utils.BITCOIN_ONLY: EthereumDefinitions, EthereumNetworkInfo, EthereumTokenInfo, - EthereumGetAddress, - EthereumGetPublicKey, - EthereumSignMessage, EthereumSignTx, EthereumSignTxEIP1559, EthereumSignTypedData, - EthereumVerifyMessage, ) @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") @@ -31,19 +27,20 @@ class TestDecodeDefinition(unittest.TestCase): # successful decode network def test_network_definition(self): - ubiq_network_encoded = get_encoded_network_definition(8) ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) self.assertEqual(dfs.decode_definition(ubiq_network_encoded, EthereumNetworkInfo), ubiq_network) # successful decode token def test_token_definition(self): # Sphere Token - sphr_token_encoded = get_encoded_token_definition(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") sphr_token = get_reference_ethereum_token_info(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token_encoded = get_encoded_token_definition(sphr_token) self.assertEqual(dfs.decode_definition(sphr_token_encoded, EthereumTokenInfo), sphr_token) def test_invalid_data(self): - ubiq_network_encoded = get_encoded_network_definition(8) + ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) invalid_dataset = [] @@ -76,45 +73,58 @@ class TestDecodeDefinition(unittest.TestCase): dfs.decode_definition(bytes(data), EthereumNetworkInfo) def test_wrong_requested_type(self): - ubiq_network_encoded = get_encoded_network_definition(8) + ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) with self.assertRaises(wire.DataError): dfs.decode_definition(ubiq_network_encoded, EthereumTokenInfo) + def test_outdated_definition(self): + ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network, 0) + + with self.assertRaises(wire.DataError): + dfs.decode_definition(ubiq_network_encoded, EthereumNetworkInfo) + @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") class TestGetAndCheckDefinition(unittest.TestCase): def test_get_network_definition(self): - eth_network_encoded = get_encoded_network_definition(1) eth_network = get_reference_ethereum_network_info(1) + eth_network_encoded = get_encoded_network_definition(eth_network) self.assertEqual(dfs.get_and_check_definition(eth_network_encoded, EthereumNetworkInfo, 1), eth_network) self.assertEqual(dfs.get_and_check_definition(eth_network_encoded, EthereumNetworkInfo), eth_network) def test_get_token_definition(self): - aave_token_encoded = get_encoded_token_definition(1, "7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9") aave_token = get_reference_ethereum_token_info(1, "7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9") + aave_token_encoded = get_encoded_token_definition(aave_token) self.assertEqual(dfs.get_and_check_definition(aave_token_encoded, EthereumTokenInfo, 1), aave_token) self.assertEqual(dfs.get_and_check_definition(aave_token_encoded, EthereumTokenInfo), aave_token) def test_invalid_expected_type(self): - ubiq_network_encoded = get_encoded_network_definition(8) + ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) with self.assertRaises(wire.DataError): dfs.get_and_check_definition(ubiq_network_encoded, EthereumTokenInfo, 8) - sphr_token_encoded = get_encoded_token_definition(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token = get_reference_ethereum_token_info(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token_encoded = get_encoded_token_definition(sphr_token) with self.assertRaises(wire.DataError): dfs.get_and_check_definition(sphr_token_encoded, EthereumNetworkInfo, 8) def test_fail_check_chain_id(self): - ubiq_network_encoded = get_encoded_network_definition(8) + ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) with self.assertRaises(wire.DataError): dfs.get_and_check_definition(ubiq_network_encoded, EthereumNetworkInfo, 1) - sphr_token_encoded = get_encoded_token_definition(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token = get_reference_ethereum_token_info(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token_encoded = get_encoded_token_definition(sphr_token) with self.assertRaises(wire.DataError): dfs.get_and_check_definition(sphr_token_encoded, EthereumTokenInfo, 1) def test_invalid_encoded_definition(self): - ubiq_network_encoded = get_encoded_network_definition(8) + ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) definition = bytearray(ubiq_network_encoded) # mangle signature - this should have the same effect as it has in "decode_definition" function definition[-1] += 1 @@ -145,10 +155,10 @@ class TestEthereumDefinitions(unittest.TestCase): eth_network = get_reference_ethereum_network_info(1) aave_token = get_reference_ethereum_token_info(1, "7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9") # not built-in - ubiq_network_encoded = get_encoded_network_definition(8) ubiq_network = get_reference_ethereum_network_info(8) - sphr_token_encoded = get_encoded_token_definition(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) sphr_token = get_reference_ethereum_token_info(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token_encoded = get_encoded_token_definition(sphr_token) calls_params = [ # no network @@ -182,13 +192,15 @@ class TestEthereumDefinitions(unittest.TestCase): def test_get_definitions_chain_id_mismatch(self): # built-in - eth_network_encoded = get_encoded_network_definition(1) eth_network = get_reference_ethereum_network_info(1) - aave_token_encoded = get_encoded_token_definition(1, "7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9") + eth_network_encoded = get_encoded_network_definition(eth_network) + aave_token = get_reference_ethereum_token_info(1, "7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9") + aave_token_encoded = get_encoded_token_definition(aave_token) # not built-in - ubiq_network_encoded = get_encoded_network_definition(8) ubiq_network = get_reference_ethereum_network_info(8) - sphr_token_encoded = get_encoded_token_definition(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) + sphr_token = get_reference_ethereum_token_info(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token_encoded = get_encoded_token_definition(sphr_token) # these variations should have the same result - error on chain id check in encoded definition calls_params = [ @@ -226,10 +238,10 @@ class TestGetDefinitonsFromMsg(unittest.TestCase): eth_network = get_reference_ethereum_network_info(1) aave_token = get_reference_ethereum_token_info(1, "7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9") # not built-in - ubiq_network_encoded = get_encoded_network_definition(8) ubiq_network = get_reference_ethereum_network_info(8) - sphr_token_encoded = get_encoded_token_definition(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) sphr_token = get_reference_ethereum_token_info(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") + sphr_token_encoded = get_encoded_token_definition(sphr_token) def create_EthereumSignTx_msg(**kwargs): return EthereumSignTx( @@ -300,8 +312,8 @@ class TestGetDefinitonsFromMsg(unittest.TestCase): self.get_and_compare_ethereum_definitions(*params) def test_EthereumSignTypedData_message(self): - ubiq_network_encoded = get_encoded_network_definition(8) ubiq_network = get_reference_ethereum_network_info(8) + ubiq_network_encoded = get_encoded_network_definition(ubiq_network) sphr_token = get_reference_ethereum_token_info(8, "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6") msg = EthereumSignTypedData( diff --git a/core/tests/test_apps.ethereum.keychain.py b/core/tests/test_apps.ethereum.keychain.py index 2bec604066..4b7a48ea57 100644 --- a/core/tests/test_apps.ethereum.keychain.py +++ b/core/tests/test_apps.ethereum.keychain.py @@ -18,6 +18,7 @@ if not utils.BITCOIN_ONLY: from ethereum_common import ( construct_network_info, get_encoded_network_definition, + get_reference_ethereum_network_info, ) from trezor.messages import ( @@ -106,7 +107,7 @@ class TestEthereumKeychain(unittest.TestCase): wire.DUMMY_CONTEXT, EthereumGetAddress( address_n=[44 | HARDENED, 60 | HARDENED, 0 | HARDENED], - encoded_network=get_encoded_network_definition(slip44=60), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(slip44=60)), ), ) ) @@ -115,7 +116,7 @@ class TestEthereumKeychain(unittest.TestCase): wire.DUMMY_CONTEXT, EthereumGetAddress( address_n=[44 | HARDENED, 108 | HARDENED, 0 | HARDENED], - encoded_network=get_encoded_network_definition(slip44=108), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(slip44=108)), ), ) ) @@ -142,7 +143,7 @@ class TestEthereumKeychain(unittest.TestCase): EthereumSignTypedData( primary_type="", address_n=[44 | HARDENED, 60 | HARDENED, 0 | HARDENED], - encoded_network=get_encoded_network_definition(slip44=60), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(slip44=60)), ), ) ) @@ -162,7 +163,7 @@ class TestEthereumKeychain(unittest.TestCase): EthereumSignTypedData( primary_type="", address_n=[44 | HARDENED, 108 | HARDENED, 0 | HARDENED], - encoded_network=get_encoded_network_definition(slip44=108), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(slip44=108)), ), ) ) @@ -197,7 +198,7 @@ class TestEthereumKeychain(unittest.TestCase): gas_price=b"", gas_limit=b"", definitions=EthereumDefinitions( - encoded_network=get_encoded_network_definition(chain_id=1), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(chain_id=1)), encoded_token=None, ), ), @@ -225,7 +226,7 @@ class TestEthereumKeychain(unittest.TestCase): gas_price=b"", gas_limit=b"", definitions=EthereumDefinitions( - encoded_network=get_encoded_network_definition(chain_id=61), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(chain_id=61)), encoded_token=None, ), ), @@ -243,7 +244,7 @@ class TestEthereumKeychain(unittest.TestCase): gas_price=b"", gas_limit=b"", definitions=EthereumDefinitions( - encoded_network=get_encoded_network_definition(chain_id=61), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(chain_id=61)), encoded_token=None, ), ), @@ -260,7 +261,7 @@ class TestEthereumKeychain(unittest.TestCase): gas_price=b"", gas_limit=b"", definitions=EthereumDefinitions( - encoded_network=get_encoded_network_definition(chain_id=2), + encoded_network=get_encoded_network_definition(get_reference_ethereum_network_info(chain_id=2)), encoded_token=None, ), ), diff --git a/python/src/trezorlib/cli/ethereum.py b/python/src/trezorlib/cli/ethereum.py index 385da06ec6..10e32e09a2 100644 --- a/python/src/trezorlib/cli/ethereum.py +++ b/python/src/trezorlib/cli/ethereum.py @@ -71,8 +71,6 @@ ETHER_UNITS = { # So that we can import the web3 library only when really used and reuse the instance _WEB3_INSTANCE: Optional["web3.Web3"] = None -DEFS_ZIP_FILENAME = "definitions-latest.zip" - def _print_eth_dependencies_and_die() -> NoReturn: click.echo("Ethereum requirements not installed.") @@ -271,7 +269,7 @@ def cli() -> None: type=click.Path( resolve_path=True, dir_okay=False, writable=True, path_type=pathlib.Path ), - default=f"./{DEFS_ZIP_FILENAME}", + default=f"./{ethereum.DEFS_ZIP_FILENAME}", help="File path to use to save downloaded definitions. Existing file will be overwritten!", ) def download_definitions(outfile: pathlib.Path) -> None: @@ -281,7 +279,7 @@ def download_definitions(outfile: pathlib.Path) -> None: # TODO: change once we know the urls archived_definitions = ethereum.download_from_url( - "https://data.trezor.io/eth_definitions/" + DEFS_ZIP_FILENAME + ethereum.DEFS_BASE_URL + ethereum.DEFS_ZIP_FILENAME ) # save diff --git a/python/src/trezorlib/ethereum.py b/python/src/trezorlib/ethereum.py index 866a13933d..d31f849d90 100644 --- a/python/src/trezorlib/ethereum.py +++ b/python/src/trezorlib/ethereum.py @@ -31,7 +31,10 @@ if TYPE_CHECKING: # TODO: change once we know the urls -DEFS_BASE_URL = "https://data.trezor.io/eth_definitions/{lookup_type}/{id}/{name}" +DEFS_BASE_URL = "https://data.trezor.io/eth_definitions/" +DEFS_URL_LOOKUP_TEMPLATE = DEFS_BASE_URL + "{lookup_type}/{id}/{name}" + +DEFS_ZIP_FILENAME = "definitions-latest.zip" DEFS_ZIP_TOPLEVEL_DIR = pathlib.PurePath("definitions-latest") DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE = "by_chain_id" DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE = "by_slip44" @@ -172,13 +175,13 @@ def get_network_definition_url( ) if chain_id is not None: - return DEFS_BASE_URL.format( + return DEFS_URL_LOOKUP_TEMPLATE.format( lookup_type=DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE, id=chain_id, name=DEFS_NETWORK_URI_NAME, ) else: - return DEFS_BASE_URL.format( + return DEFS_URL_LOOKUP_TEMPLATE.format( lookup_type=DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE, id=slip44, name=DEFS_NETWORK_URI_NAME, @@ -195,7 +198,7 @@ def get_token_definition_url(chain_id: int = None, token_address: str = None) -> if addr.startswith("0x"): addr = addr[2:] - return DEFS_BASE_URL.format( + return DEFS_URL_LOOKUP_TEMPLATE.format( lookup_type=DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE, id=chain_id, name=DEFS_TOKEN_URI_NAME.format(hex_address=addr), diff --git a/python/src/trezorlib/merkle_tree.py b/python/src/trezorlib/merkle_tree.py index 47512b23a7..2a76394ad6 100755 --- a/python/src/trezorlib/merkle_tree.py +++ b/python/src/trezorlib/merkle_tree.py @@ -42,7 +42,7 @@ class Node: "`left` and `right` value must not be None when not using `raw_value`." ) - self.hash = None + self.hash: Optional[bytes] = None self.left_child = left self.right_child = right self.proof_list: List[bytes] = [] @@ -59,16 +59,24 @@ class Node: self.hash = sha256(b"\x01" + hash_a + hash_b).digest() # distribute proof - self.left_child.add_to_proof(right_hash) # type: ignore ["add_to_proof" is not a known member of "None"] - self.right_child.add_to_proof(left_hash) # type: ignore ["add_to_proof" is not a known member of "None"] + self.left_child.add_to_proof_list(right_hash) # type: ignore ["add_to_proof_list" is not a known member of "None"] + self.right_child.add_to_proof_list(left_hash) # type: ignore ["add_to_proof_list" is not a known member of "None"] return self.hash - def add_to_proof(self, proof: bytes) -> None: + def add_to_proof_list(self, proof: bytes) -> None: self.proof_list.append(proof) if not self.raw_value: - self.left_child.add_to_proof(proof) # type: ignore ["add_to_proof" is not a known member of "None"] - self.right_child.add_to_proof(proof) # type: ignore ["add_to_proof" is not a known member of "None"] + self.left_child.add_to_proof_list(proof) # type: ignore ["add_to_proof_list" is not a known member of "None"] + self.right_child.add_to_proof_list(proof) # type: ignore ["add_to_proof_list" is not a known member of "None"] + + def get_proof_list(self) -> List[bytes]: + return self.proof_list + + def get_hash(self) -> Optional[bytes]: + if self.hash is None: + self.compute_hash() + return self.hash class MerkleTree: @@ -78,6 +86,11 @@ class MerkleTree: """ def __init__(self, values: List[bytes]) -> None: + if values is None or len(values) < 1: + raise ValueError( + "`left` and `right` value must not be None when not using `raw_value`." + ) + self.leaves = [Node(raw_value=v) for v in values] self.height = 0 @@ -105,11 +118,13 @@ class MerkleTree: def get_proofs(self) -> Dict[bytes, List[bytes]]: return { - n.raw_value: n.proof_list for n in self.leaves if n.raw_value is not None + n.raw_value: n.get_proof_list() + for n in self.leaves + if n.raw_value is not None } def get_tree_height(self) -> int: return self.height def get_root_hash(self) -> bytes: - return self.root_node.hash # type: ignore [Expression of type "bytes | None" cannot be assigned to return type "bytes"] + return self.root_node.get_hash() # type: ignore [Expression of type "bytes | None" cannot be assigned to return type "bytes"] diff --git a/python/tests/test_merkle_tree.py b/python/tests/test_merkle_tree.py new file mode 100644 index 0000000000..fbdbe67ab7 --- /dev/null +++ b/python/tests/test_merkle_tree.py @@ -0,0 +1,216 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2022 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import pytest + +from hashlib import sha256 +from typing import Dict, List + +from trezorlib.merkle_tree import MerkleTree, Node + + +NODE_VECTORS = ( + ( # leaf node + # node + Node( + raw_value=bytes.fromhex("dead"), + ), + # expected hash + sha256(b"\x00" + bytes.fromhex("dead")).digest() + ), + ( # node with leaf nodes + # node + Node( + left=Node( + raw_value=bytes.fromhex("dead"), + ), + right=Node( + raw_value=bytes.fromhex("beef"), + ), + ), + # expected hash + sha256( + b"\x01" + + sha256(b"\x00" + bytes.fromhex("beef")).digest() + + sha256(b"\x00" + bytes.fromhex("dead")).digest() + ).digest() + ), + ( # node with parent node (left) + # node + Node( + left=Node( + raw_value=bytes.fromhex("dead"), + ), + right=Node( + raw_value=bytes.fromhex("beef"), + ), + ).left_child, + # expected hash + sha256(b"\x00" + bytes.fromhex("dead")).digest() + ), + ( # node with parent node (right) + # node + Node( + left=Node( + raw_value=bytes.fromhex("dead"), + ), + right=Node( + raw_value=bytes.fromhex("beef"), + ), + ).right_child, + # expected hash + sha256(b"\x00" + bytes.fromhex("beef")).digest() + ), +) + + +NODE_FAILED_VECTORS = ( + ( # no inputs + # left + None, + # right + None, + # raw_value + None, + ), + ( # only left child + # left + Node(raw_value=bytes.fromhex("dead")), + # right + None, + # raw_value + None, + ), + ( # only right child + # left + None, + # right + Node(raw_value=bytes.fromhex("beef")), + # raw_value + None, + ), + ( # all inputs + # left + Node(raw_value=bytes.fromhex("dead")), + # right + Node(raw_value=bytes.fromhex("beef")), + # raw_value + bytes.fromhex("deadbeef"), + ), +) + +MERKLE_TREE_VECTORS = ( + ( # one value + # values + [bytes.fromhex("dead")], + # expected root hash + sha256(b"\x00" + bytes.fromhex("dead")).digest(), + # expected tree height + 0, + # expected dict of proof lists + { + bytes.fromhex("dead"): [], + }, + ), + ( # two values + # values + [bytes.fromhex("dead"), bytes.fromhex("beef")], + # expected root hash + sha256( + b"\x01" + + sha256(b"\x00" + bytes.fromhex("beef")).digest() + + sha256(b"\x00" + bytes.fromhex("dead")).digest() + ).digest(), + # expected tree height + 1, + # expected dict of proof lists + { + bytes.fromhex("dead"): [sha256(b"\x00" + bytes.fromhex("beef")).digest()], + bytes.fromhex("beef"): [sha256(b"\x00" + bytes.fromhex("dead")).digest()], + }, + ), + ( # three values + # values + [bytes.fromhex("dead"), bytes.fromhex("beef"), bytes.fromhex("cafe")], + # expected root hash + sha256( + b"\x01" + + sha256( + b"\x01" + + sha256(b"\x00" + bytes.fromhex("cafe")).digest() + + sha256(b"\x00" + bytes.fromhex("beef")).digest() + ).digest() + + sha256(b"\x00" + bytes.fromhex("dead")).digest() + ).digest(), + # expected tree height + 2, + # expected dict of proof lists + { + bytes.fromhex("dead"): [ + sha256( + b"\x01" + + sha256(b"\x00" + bytes.fromhex("cafe")).digest() + + sha256(b"\x00" + bytes.fromhex("beef")).digest() + ).digest() + ], + bytes.fromhex("beef"): [ + sha256(b"\x00" + bytes.fromhex("cafe")).digest(), + sha256(b"\x00" + bytes.fromhex("dead")).digest() + ], + bytes.fromhex("cafe"): [ + sha256(b"\x00" + bytes.fromhex("beef")).digest(), + sha256(b"\x00" + bytes.fromhex("dead")).digest() + ], + }, + ), +) + +MERKLE_TREE_FAILED_VECTORS = ( + ( # no values + # values + [], + ), +) + + +@pytest.mark.parametrize("node, expected_hash", NODE_VECTORS) +def test_node(node: Node, expected_hash: bytes) -> None: + assert node.compute_hash() == expected_hash + assert node.get_hash() == expected_hash + + +@pytest.mark.parametrize("left, right, raw_value", NODE_FAILED_VECTORS) +def test_node_failed(left: Node, right: Node, raw_value: bytes) -> None: + with pytest.raises(ValueError): + Node( + left=left, + right=right, + raw_value=raw_value, + ) + + +@pytest.mark.parametrize("values, expected_root_hash, expected_height, expected_proofs", MERKLE_TREE_VECTORS) +def test_tree(values: List[bytes], expected_root_hash: bytes, expected_height: int, expected_proofs: Dict[bytes, List[bytes]]) -> None: + mt = MerkleTree(values) + assert mt.get_root_hash() == expected_root_hash + assert mt.get_tree_height() == expected_height + assert mt.get_proofs() == expected_proofs + + +@pytest.mark.parametrize("values", MERKLE_TREE_FAILED_VECTORS) +def test_tree_failed(values: List[bytes]) -> None: + with pytest.raises(ValueError): + MerkleTree(values) diff --git a/python/tools/eth_defs_unpack.py b/python/tools/eth_defs_unpack.py index 8e3603bae2..6e21935600 100755 --- a/python/tools/eth_defs_unpack.py +++ b/python/tools/eth_defs_unpack.py @@ -20,7 +20,6 @@ import pathlib import click from trezorlib import ethereum -from trezorlib.cli import ethereum as ethereum_cli @click.command() @@ -29,7 +28,7 @@ from trezorlib.cli import ethereum as ethereum_cli type=click.Path( exists=True, dir_okay=False, resolve_path=True, path_type=pathlib.Path ), - default=f"./{ethereum_cli.DEFS_ZIP_FILENAME}", + default=f"./{ethereum.DEFS_ZIP_FILENAME}", help="Zip file with stored definitions. Zip file could be obtained using command" "`trezorctl ethereum download-definitions`.", ) diff --git a/tests/device_tests/ethereum/ethereum_common.py b/tests/device_tests/ethereum/ethereum_common.py new file mode 100644 index 0000000000..5558c31ed7 --- /dev/null +++ b/tests/device_tests/ethereum/ethereum_common.py @@ -0,0 +1,187 @@ +import io # noqa: F401 +from binascii import unhexlify +from hashlib import sha256 +from typing import Optional, Union + +import ed25519 + +from trezorlib import messages, protobuf + +DEFINITIONS_DEV_PRIVATE_KEY = unhexlify( + "4141414141414141414141414141414141414141414141414141414141414141" +) + + +NETWORKS = { + # chain_id: network info + 1: messages.EthereumNetworkInfo( + chain_id=1, + slip44=60, + shortcut="ETH", + name="Ethereum", + ), + 8: messages.EthereumNetworkInfo( + chain_id=8, + slip44=108, + shortcut="UBQ", + name="Ubiq", + ), + 31: messages.EthereumNetworkInfo( + chain_id=31, + slip44=1, + shortcut="tRBTC", + name="RSK Testnet", + ), + 60: messages.EthereumNetworkInfo( + chain_id=60, + slip44=6060, + shortcut="GO", + name="GoChain", + ), + 61: messages.EthereumNetworkInfo( + chain_id=61, + slip44=61, + shortcut="ETC", + name="Ethereum Classic", + ), + 888: messages.EthereumNetworkInfo( + chain_id=888, + slip44=5718350, + shortcut="WAN", + name="Wanchain", + ), + 28945486: messages.EthereumNetworkInfo( + chain_id=28945486, + slip44=344, + shortcut="AUX", + name="Auxilium Network", + ), + 3125659152: messages.EthereumNetworkInfo( + chain_id=3125659152, + slip44=164, + shortcut="PIRL", + name="Pirl", + ), + 11297108109: messages.EthereumNetworkInfo( + chain_id=11297108109, + slip44=60, + shortcut="PALM", + name="Palm", + ), +} + +TOKENS = { + # chain_id: { address: token info, address: token info,... } + 1: { + "d0d6d6c5fe4a677d343cc433536bb717bae167dd": messages.EthereumTokenInfo( + symbol="ADT", + decimals=9, + address=unhexlify("d0d6d6c5fe4a677d343cc433536bb717bae167dd"), + chain_id=1, + name="adChain", + ), + "a33e729bf4fdeb868b534e1f20523463d9c46bee": messages.EthereumTokenInfo( + symbol="ICO", + decimals=10, + address=unhexlify("a33e729bf4fdeb868b534e1f20523463d9c46bee"), + chain_id=1, + name="ICO", + ), + "dac17f958d2ee523a2206206994597c13d831ec7": messages.EthereumTokenInfo( + symbol="USDT", + decimals=6, + address=unhexlify("dac17f958d2ee523a2206206994597c13d831ec7"), + chain_id=1, + name="Tether", + ), + }, + 8: { + "20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6": messages.EthereumTokenInfo( + symbol="SPHR", + decimals=8, + address=unhexlify("20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6"), + chain_id=8, + name="Sphere", + ), + }, +} + + +def get_reference_ethereum_network_info( + chain_id: Optional[int] = None, slip44: Optional[int] = None +) -> Optional[messages.EthereumNetworkInfo]: + if not (chain_id is None) != (slip44 is None): # not XOR + raise ValueError("chain_id and slip44 arguments are exclusive") + + # resolve network + if chain_id is not None: + return NETWORKS.get(chain_id) + else: # slip44 is not None + for _, network in NETWORKS.items(): + if network.slip44 == slip44: + return network + + return None + + +def get_reference_ethereum_token_info( + chain_id: int, token_address: str +) -> Optional[messages.EthereumTokenInfo]: + if token_address.startswith("0x"): + token_address = token_address[2:] + return TOKENS.get(chain_id, {}).get(token_address) + + +def _serialize_eth_info( + info: Union[messages.EthereumNetworkInfo, messages.EthereumTokenInfo], + data_type_num: messages.EthereumDefinitionType, + timestamp: Optional[int] = None, +) -> bytes: + ser = b"trzd1" + ser += data_type_num.to_bytes(1, "big") + if timestamp is not None: + ser += timestamp.to_bytes(4, "big") + else: + # set data version to max to avoid "outdated" definition errors + ser += b"\xff" * 4 + + # serialize message + buf = io.BytesIO() + protobuf.dump_message(buf, info) + msg = buf.getvalue() + # write the length of encoded protobuf message + ser += len(msg).to_bytes(2, "big") + ser += msg + + # add Merkle tree proof length and signature + hash = sha256(b"\x00" + ser).digest() + ser += b"\x00" + ser += ed25519.SigningKey(DEFINITIONS_DEV_PRIVATE_KEY).sign(hash) + + return ser + + +def get_encoded_network_definition( + chain_id: Optional[int] = None, + slip44: Optional[int] = None, + timestamp: Optional[int] = None, +) -> Optional[bytes]: + info = get_reference_ethereum_network_info(chain_id, slip44) + + if info is None: + return None + + return _serialize_eth_info(info, messages.EthereumDefinitionType.NETWORK, timestamp) + + +def get_encoded_token_definition( + chain_id: int, + token_address: str, + timestamp: Optional[int] = None, +) -> Optional[bytes]: + info = get_reference_ethereum_token_info(chain_id, token_address) + + if info is None: + return None + + return _serialize_eth_info(info, messages.EthereumDefinitionType.TOKEN, timestamp) diff --git a/tests/device_tests/ethereum/test_getaddress.py b/tests/device_tests/ethereum/test_getaddress.py index 8c51c4bcbc..b116f9adb6 100644 --- a/tests/device_tests/ethereum/test_getaddress.py +++ b/tests/device_tests/ethereum/test_getaddress.py @@ -21,7 +21,8 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.exceptions import TrezorFailure from trezorlib.tools import UH_, parse_path -from ...common import COMMON_FIXTURES_DIR, parametrize_using_common_fixtures +from ...common import parametrize_using_common_fixtures +from .ethereum_common import get_encoded_network_definition pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum] @@ -35,11 +36,8 @@ def test_getaddress(client: Client, parameters, result): "slip44", encoded_network_slip44 ) - encoded_network = ethereum.get_definition_from_zip( - COMMON_FIXTURES_DIR / "ethereum" / "definitions-latest.zip", - ethereum.get_network_definition_path( - slip44=encoded_network_slip44, - ), + encoded_network = get_encoded_network_definition( + slip44=encoded_network_slip44, ) assert ( ethereum.get_address(client, address_n, encoded_network=encoded_network) @@ -47,9 +45,18 @@ def test_getaddress(client: Client, parameters, result): ) -def test_getaddress_missing_extern_definitions(client: Client): - path = "m/44'/6060'/0'/0/0" # GoChain - address_n = parse_path(path) +@parametrize_using_common_fixtures("ethereum/getaddress.failed.json") +def test_getaddress_failed(client: Client, parameters, result): + address_n = parse_path(parameters["path"]) + encoded_network_slip44 = UH_(address_n[1]) + if "definitions" in parameters: + encoded_network_slip44 = parameters["definitions"].get( + "slip44", encoded_network_slip44 + ) - with pytest.raises(TrezorFailure, match=r"DataError:.*Forbidden key path"): - ethereum.get_address(client, address_n, encoded_network=None) + encoded_network = get_encoded_network_definition( + slip44=encoded_network_slip44, + ) + + with pytest.raises(TrezorFailure, match=result["error"]): + ethereum.get_address(client, address_n, encoded_network=encoded_network) diff --git a/tests/device_tests/ethereum/test_sign_typed_data.py b/tests/device_tests/ethereum/test_sign_typed_data.py index 1bb1d93f7d..5eaaa7d66b 100644 --- a/tests/device_tests/ethereum/test_sign_typed_data.py +++ b/tests/device_tests/ethereum/test_sign_typed_data.py @@ -21,12 +21,11 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.exceptions import TrezorFailure from trezorlib.tools import UH_, parse_path -from ...common import COMMON_FIXTURES_DIR, parametrize_using_common_fixtures +from ...common import parametrize_using_common_fixtures +from .ethereum_common import get_encoded_network_definition SHOW_MORE = (143, 167) -DEFS_ZIP_FILE_PATH = COMMON_FIXTURES_DIR / "ethereum" / "definitions-latest.zip" - pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum] @@ -42,11 +41,8 @@ def test_ethereum_sign_typed_data(client: Client, parameters, result): ) defs = ethereum.messages.EthereumDefinitions( - encoded_network=ethereum.get_definition_from_zip( - DEFS_ZIP_FILE_PATH, - ethereum.get_network_definition_path( - slip44=encoded_network_slip44, - ), + encoded_network=get_encoded_network_definition( + slip44=encoded_network_slip44, ) ) @@ -62,20 +58,28 @@ def test_ethereum_sign_typed_data(client: Client, parameters, result): @pytest.mark.skip_t1 -def test_ethereum_sign_typed_data_missing_extern_definitions(client: Client): - path = "m/44'/6060'/0'/0/0" # GoChain +@parametrize_using_common_fixtures("ethereum/sign_typed_data.failed.json") +def test_ethereum_sign_typed_data_failed(client: Client, parameters, result): + address_n = parse_path(parameters["path"]) + encoded_network_slip44 = UH_(address_n[1]) + if "definitions" in parameters: + encoded_network_slip44 = parameters["definitions"].get( + "slip44", encoded_network_slip44 + ) - with pytest.raises(TrezorFailure, match=r"DataError: Forbidden key path"): + defs = ethereum.messages.EthereumDefinitions( + encoded_network=get_encoded_network_definition( + slip44=encoded_network_slip44, + ) + ) + + with pytest.raises(TrezorFailure, match=result["error"]): ethereum.sign_typed_data( client, - parse_path(path), - { - "types": {"EIP712Domain": []}, - "primaryType": "EIP712Domain", - "message": {}, - "domain": {}, - }, - metamask_v4_compat=True, + address_n, + parameters["data"], + metamask_v4_compat=parameters["metamask_v4_compat"], + definitions=defs, ) @@ -90,11 +94,8 @@ def test_ethereum_sign_typed_data_blind(client: Client, parameters, result): "slip44", encoded_network_slip44 ) - encoded_network = ethereum.get_definition_from_zip( - DEFS_ZIP_FILE_PATH, - ethereum.get_network_definition_path( - slip44=encoded_network_slip44, - ), + encoded_network = get_encoded_network_definition( + slip44=encoded_network_slip44, ) ret = ethereum.sign_typed_data_hash( client, @@ -111,18 +112,29 @@ def test_ethereum_sign_typed_data_blind(client: Client, parameters, result): @pytest.mark.skip_t2 -def test_ethereum_sign_typed_data_blind_missing_extern_definitions(client: Client): - path = "m/44'/6060'/0'/0/0" # GoChain +@parametrize_using_common_fixtures("ethereum/sign_typed_data.failed.json") +def test_ethereum_sign_typed_data_blind_failed(client: Client, parameters, result): + address_n = parse_path(parameters["path"]) + encoded_network_slip44 = UH_(address_n[1]) + if "definitions" in parameters: + encoded_network_slip44 = parameters["definitions"].get( + "slip44", encoded_network_slip44 + ) - with pytest.raises(TrezorFailure, match=r"DataError:.*Forbidden key path"): + encoded_network = get_encoded_network_definition( + slip44=encoded_network_slip44, + ) + + with pytest.raises(TrezorFailure, match=result["error"]): ethereum.sign_typed_data_hash( client, - parse_path(path), - ethereum.decode_hex( - "0x6192106f129ce05c9075d319c1fa6ea9b3ae37cbd0c1ef92e2be7137bb07baa1" - ), - None, - encoded_network=None, + address_n, + ethereum.decode_hex(parameters["domain_separator_hash"]), + # message hash is empty for domain-only hashes + ethereum.decode_hex(parameters["message_hash"]) + if parameters["message_hash"] + else None, + encoded_network=encoded_network, ) @@ -209,11 +221,8 @@ def input_flow_cancel(client: Client): @pytest.mark.skip_t1 def test_ethereum_sign_typed_data_show_more_button(client: Client): defs = ethereum.messages.EthereumDefinitions( - encoded_network=ethereum.get_definition_from_zip( - DEFS_ZIP_FILE_PATH, - ethereum.get_network_definition_path( - slip44=60, - ), + encoded_network=get_encoded_network_definition( + slip44=60, ) ) @@ -232,11 +241,8 @@ def test_ethereum_sign_typed_data_show_more_button(client: Client): @pytest.mark.skip_t1 def test_ethereum_sign_typed_data_cancel(client: Client): defs = ethereum.messages.EthereumDefinitions( - encoded_network=ethereum.get_definition_from_zip( - DEFS_ZIP_FILE_PATH, - ethereum.get_network_definition_path( - slip44=60, - ), + encoded_network=get_encoded_network_definition( + slip44=60, ) ) diff --git a/tests/device_tests/ethereum/test_sign_verify_message.py b/tests/device_tests/ethereum/test_sign_verify_message.py index f069c5025d..ddb534dd98 100644 --- a/tests/device_tests/ethereum/test_sign_verify_message.py +++ b/tests/device_tests/ethereum/test_sign_verify_message.py @@ -21,7 +21,8 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.exceptions import TrezorFailure from trezorlib.tools import UH_, parse_path -from ...common import COMMON_FIXTURES_DIR, parametrize_using_common_fixtures +from ...common import parametrize_using_common_fixtures +from .ethereum_common import get_encoded_network_definition pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum] @@ -35,24 +36,32 @@ def test_signmessage(client: Client, parameters, result): "slip44", encoded_network_slip44 ) - encoded_network = ethereum.get_definition_from_zip( - COMMON_FIXTURES_DIR / "ethereum" / "definitions-latest.zip", - ethereum.get_network_definition_path( - slip44=encoded_network_slip44, - ), + encoded_network = get_encoded_network_definition( + slip44=encoded_network_slip44, ) + res = ethereum.sign_message(client, address_n, parameters["msg"], encoded_network) assert res.address == result["address"] assert res.signature.hex() == result["sig"] -def test_signmessage_missing_extern_definitions(client: Client): - path = "m/44'/6060'/0'/0/0" # GoChain - address_n = parse_path(path) - msg = "This is an example of a signed message." +@parametrize_using_common_fixtures("ethereum/signmessage.failed.json") +def test_signmessage_failed(client: Client, parameters, result): + address_n = parse_path(parameters["path"]) + encoded_network_slip44 = UH_(address_n[1]) + if "definitions" in parameters: + encoded_network_slip44 = parameters["definitions"].get( + "slip44", encoded_network_slip44 + ) - with pytest.raises(TrezorFailure, match=r"DataError:.*Forbidden key path"): - ethereum.sign_message(client, address_n, msg, encoded_network=None) + encoded_network = get_encoded_network_definition( + slip44=encoded_network_slip44, + ) + + with pytest.raises(TrezorFailure, match=result["error"]): + ethereum.sign_message( + client, address_n, parameters["msg"], encoded_network=encoded_network + ) @parametrize_using_common_fixtures("ethereum/verifymessage.json") diff --git a/tests/device_tests/ethereum/test_signtx.py b/tests/device_tests/ethereum/test_signtx.py index 8b7651889d..d5e1f5173c 100644 --- a/tests/device_tests/ethereum/test_signtx.py +++ b/tests/device_tests/ethereum/test_signtx.py @@ -14,7 +14,9 @@ # You should have received a copy of the License along with this library. # If not, see . -from functools import partial +import pathlib +import tempfile +from typing import Optional import pytest @@ -23,42 +25,69 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client, message_filters from trezorlib.exceptions import TrezorFailure from trezorlib.tools import parse_path -from ...common import COMMON_FIXTURES_DIR, parametrize_using_common_fixtures +from ...common import parametrize_using_common_fixtures +from .ethereum_common import ( + get_encoded_network_definition, + get_encoded_token_definition, +) TO_ADDR = "0x1d1c328764a41bda0492b66baa30c4a339ff85ef" SHOW_ALL = (143, 167) GO_BACK = (16, 220) -DEFS_ZIP_FILE_PATH = COMMON_FIXTURES_DIR / "ethereum" / "definitions-latest.zip" - pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum] +ONLINE_DEFS_ZIP_FILE = tempfile.NamedTemporaryFile() +with open(ONLINE_DEFS_ZIP_FILE.name, mode="w+b") as tf: + try: + tf.write( + ethereum.download_from_url( + ethereum.DEFS_BASE_URL + ethereum.DEFS_ZIP_FILENAME + ) + ) + except RuntimeError: + pass + def get_definitions( parameters: dict, + zip_file: Optional[pathlib.Path] = None, ) -> messages.EthereumDefinitions: chain_id = parameters["chain_id"] token_chain_id = parameters["chain_id"] token_address = parameters.get("to_address") + timestamp = None if "definitions" in parameters: chain_id = parameters["definitions"].get("chain_id", chain_id) token_chain_id = parameters["definitions"].get("token_chain_id", token_chain_id) token_address = parameters["definitions"].get("to_address", token_address) + timestamp = parameters["definitions"].get("timestamp") - encoded_network = ethereum.get_definition_from_zip( - DEFS_ZIP_FILE_PATH, - ethereum.get_network_definition_path( - chain_id, - ), - ) - encoded_token = ethereum.get_definition_from_zip( - DEFS_ZIP_FILE_PATH, - ethereum.get_token_definition_path( - token_chain_id, - token_address, - ), - ) + if zip_file: + encoded_network = ethereum.get_definition_from_zip( + zip_file=zip_file, + path_inside_zip=ethereum.get_network_definition_path( + chain_id=chain_id, + ), + ) + encoded_token = ethereum.get_definition_from_zip( + zip_file=zip_file, + path_inside_zip=ethereum.get_token_definition_path( + chain_id=token_chain_id, + token_address=token_address, + ), + ) + else: + encoded_network = get_encoded_network_definition( + chain_id=chain_id, + timestamp=timestamp, + ) + encoded_token = get_encoded_token_definition( + chain_id=token_chain_id, + token_address=token_address, + timestamp=timestamp, + ) return messages.EthereumDefinitions( encoded_network=encoded_network, encoded_token=encoded_token @@ -93,68 +122,54 @@ def test_signtx(client: Client, parameters, result): assert sig_v == result["sig_v"] -def test_signtx_missing_or_wrong_extern_definitions(client: Client): - chain_id = 8 # Ubiq - to_address = "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6" # Sphere +# TODO: remove xfail when definitions becomes available online +@pytest.mark.xfail(reason="Temporary until definitions becomes available online.") +@parametrize_using_common_fixtures( + "ethereum/sign_tx.json", + "ethereum/sign_tx_eip155.json", + "ethereum/sign_tx_definitions.json", +) +def test_signtx_online_definitions(client: Client, parameters, result): + with client: + sig_v, sig_r, sig_s = ethereum.sign_tx( + client, + n=parse_path(parameters["path"]), + nonce=int(parameters["nonce"], 16), + gas_price=int(parameters["gas_price"], 16), + gas_limit=int(parameters["gas_limit"], 16), + to=parameters["to_address"], + chain_id=parameters["chain_id"], + value=int(parameters["value"], 16), + tx_type=parameters["tx_type"], + data=bytes.fromhex(parameters["data"]), + definitions=get_definitions( + parameters, pathlib.Path(ONLINE_DEFS_ZIP_FILE.name) + ), + ) - sign_tx_call = partial( - ethereum.sign_tx, - client, - n=parse_path("m/44'/108'/0'/0/0"), - nonce=0, - gas_price=20, - gas_limit=20, - to=to_address, - chain_id=chain_id, - value=0, - data=b"a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - ) + expected_v = 2 * parameters["chain_id"] + 35 + assert sig_v in (expected_v, expected_v + 1) + assert sig_r.hex() == result["sig_r"] + assert sig_s.hex() == result["sig_s"] + assert sig_v == result["sig_v"] - definitions_vector = [ - get_definitions( - { - "chain_id": chain_id, - "to_address": to_address, - "definitions": { - "chain_id": -1, - }, - } - ), # missing network - get_definitions( - { - "chain_id": -1, - "to_address": "", - } - ), # missing network and token - get_definitions( - { - "chain_id": chain_id, - "to_address": to_address, - "definitions": { - "chain_id": 1, - }, - } - ), # wrong network - get_definitions( - { - "chain_id": 1, - "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd", - } - ), # wrong network and token - get_definitions( - { - "chain_id": chain_id, - "definitions": { - "token_chain_id": 1, - "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd", - }, - } - ), # wrong token - ] - for definitions in definitions_vector: - with pytest.raises(TrezorFailure, match=r"DataError"): - sign_tx_call(definitions=definitions) +@parametrize_using_common_fixtures("ethereum/sign_tx_definitions.failed.json") +def test_signtx_failed(client: Client, parameters, result): + with pytest.raises(TrezorFailure, match=result["error"]): + ethereum.sign_tx( + client, + n=parse_path(parameters["path"]), + nonce=int(parameters["nonce"], 16), + gas_price=int(parameters["gas_price"], 16), + gas_limit=int(parameters["gas_limit"], 16), + to=parameters["to_address"], + chain_id=parameters["chain_id"], + value=int(parameters["value"], 16), + tx_type=parameters["tx_type"], + data=bytes.fromhex(parameters["data"]), + definitions=get_definitions(parameters), + ) @parametrize_using_common_fixtures( @@ -182,69 +197,22 @@ def test_signtx_eip1559(client: Client, parameters, result): assert sig_v == result["sig_v"] -def test_signtx_eip1559_missing_or_wrong_extern_definitions(client: Client): - chain_id = 8 # Ubiq - to_address = "0x20e3dd746ddf519b23ffbbb6da7a5d33ea6349d6" # Sphere - - sign_tx_eip1559_call = partial( - ethereum.sign_tx_eip1559, - client, - n=parse_path("m/44'/108'/0'/0/0"), - nonce=0, - gas_limit=20, - max_gas_fee=20, - max_priority_fee=1, - to=to_address, - chain_id=chain_id, - value=0, - data=b"a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - ) - - definitions_vector = [ - get_definitions( - { - "chain_id": chain_id, - "to_address": to_address, - "definitions": { - "chain_id": -1, - }, - } - ), # missing network - get_definitions( - { - "chain_id": -1, - "to_address": "", - } - ), # missing network and token - get_definitions( - { - "chain_id": chain_id, - "to_address": to_address, - "definitions": { - "chain_id": 1, - }, - } - ), # wrong network - get_definitions( - { - "chain_id": 1, - "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd", - } - ), # wrong network and token - get_definitions( - { - "chain_id": chain_id, - "definitions": { - "token_chain_id": 1, - "to_address": "0xd0d6d6c5fe4a677d343cc433536bb717bae167dd", - }, - } - ), # wrong token - ] - - for definitions in definitions_vector: - with pytest.raises(TrezorFailure, match=r"DataError"): - sign_tx_eip1559_call(definitions=definitions) +@parametrize_using_common_fixtures("ethereum/sign_tx_eip1559_definitions.failed.json") +def test_signtx_eip1559_failed(client: Client, parameters, result): + with pytest.raises(TrezorFailure, match=result["error"]): + ethereum.sign_tx_eip1559( + client, + n=parse_path(parameters["path"]), + nonce=int(parameters["nonce"], 16), + gas_limit=int(parameters["gas_limit"], 16), + max_gas_fee=int(parameters["max_gas_fee"], 16), + max_priority_fee=int(parameters["max_priority_fee"], 16), + to=parameters["to_address"], + chain_id=parameters["chain_id"], + value=int(parameters["value"], 16), + data=bytes.fromhex(parameters["data"]), + definitions=get_definitions(parameters), + ) def test_sanity_checks(client: Client): diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 76ca0dcb09..5cb1926c96 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -359,7 +359,7 @@ "T1_ethereum-test_sign_typed_data.py::test_ethereum_sign_typed_data_blind[struct_list_non_v4]": "eb17b48c8495ae235345920089c1662f5aa36fbb6e90f0c0c4b270c7bb2f2ea8", "T1_ethereum-test_sign_typed_data.py::test_ethereum_sign_typed_data_blind[struct_list_v4]": "b0ce2ac7326df63ae12badf47a58db955fe2b17a327fcdc6eee1fd06375a25f7", "T1_ethereum-test_sign_typed_data.py::test_ethereum_sign_typed_data_blind[structs_arrays_v4]": "a38f3ef2571424b460cd7351cd7263c8fc19684041d57f32f5599588c89046bf", -"T1_ethereum-test_sign_typed_data.py::test_ethereum_sign_typed_data_blind_missing_extern_definitions": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", +"T1_ethereum-test_sign_typed_data.py::test_ethereum_sign_typed_data_blind_failed[missing_extern-610f2126": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_ethereum-test_sign_verify_message.py::test_signmessage[builtin_Ethereum_do_not_send_network]": "0f1e81d57996381781e8e3c5865875e6d48c29128d2de78f33556d96f29c2785", "T1_ethereum-test_sign_verify_message.py::test_signmessage[builtin_Ethereum_send_wrong_network]": "0f1e81d57996381781e8e3c5865875e6d48c29128d2de78f33556d96f29c2785", "T1_ethereum-test_sign_verify_message.py::test_signmessage[extern_GoChain_send_network]": "a72aad838b023cce57d15c42f0e55da11e30ca965e42cab2e5019d6636dde47d", @@ -371,7 +371,7 @@ "T1_ethereum-test_sign_verify_message.py::test_signmessage[parameters7-result7]": "06a642eaad886c01421973efa714c59eb6da8e60d06fc44cc42a5f959ec3dd27", "T1_ethereum-test_sign_verify_message.py::test_signmessage[parameters8-result8]": "a6b54a1244c1c457de732c2d27371475c02e451a4820f283912a5e3c62fe2842", "T1_ethereum-test_sign_verify_message.py::test_signmessage[parameters9-result9]": "858c5f14ecf61cbc85acb7d11f85b65a88c3d14f2591f185e1925092a977a7d6", -"T1_ethereum-test_sign_verify_message.py::test_signmessage_missing_extern_definitions": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", +"T1_ethereum-test_sign_verify_message.py::test_signmessage_failed[missing_extern_definition_GoChain]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_ethereum-test_sign_verify_message.py::test_verify[parameters0-result0]": "cdec0f79f2abbd90f4346494037f7bb4dd4dccc7c6739b497873d0c5603f2a26", "T1_ethereum-test_sign_verify_message.py::test_verify[parameters1-result1]": "9e51345ff0d3736f81a5c169768f9a80c555187eef2db3046f5ad12262990466", "T1_ethereum-test_sign_verify_message.py::test_verify[parameters2-result2]": "e2aff8a36dffd76b0e48256a5012ff191d0ce877a42cf31e1f25f7c3cd786d64", @@ -400,13 +400,10 @@ "T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_builtin_Tether_send_nothing]": "2ad8d58e1c04bfde465902c6edd2308a392a4afeffbeaf5e32e0ef22380acf75", "T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_builtin_Tether_send_token]": "2ad8d58e1c04bfde465902c6edd2308a392a4afeffbeaf5e32e0ef22380acf75", "T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_builtin_Tether_wrong_network]": "2ad8d58e1c04bfde465902c6edd2308a392a4afeffbeaf5e32e0ef22380acf75", -"T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_builtin_Tether_wrong_network_token]": "2ad8d58e1c04bfde465902c6edd2308a392a4afeffbeaf5e32e0ef22380acf75", -"T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_builtin_Tether_wrong_token]": "2ad8d58e1c04bfde465902c6edd2308a392a4afeffbeaf5e32e0ef22380acf75", "T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_extern_adChain_send_network]": "0143f7c4273964eb5eb8113c7e63674c033bebeca7ae619270b7ca77bb6aed66", "T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_extern_adChain_send_network_token]": "08e9da493a8ac1f4e7f05bbb8f7c2b2fd97481421899a170ac0ead26b0764a21", "T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_extern_adChain_send_nothing]": "0143f7c4273964eb5eb8113c7e63674c033bebeca7ae619270b7ca77bb6aed66", "T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_extern_adChain_send_token]": "08e9da493a8ac1f4e7f05bbb8f7c2b2fd97481421899a170ac0ead26b0764a21", -"T1_ethereum-test_signtx.py::test_signtx[builtin_Ethereum_extern_adChain_wrong_token]": "0143f7c4273964eb5eb8113c7e63674c033bebeca7ae619270b7ca77bb6aed66", "T1_ethereum-test_signtx.py::test_signtx[data_1]": "8b432aba21bc4344814cceaf693e114b9d3e3d6ceb83c3a6af7c3ed0f9b37449", "T1_ethereum-test_signtx.py::test_signtx[data_2_bigdata]": "445286b7501ca67dd16dafd7ea09c57cc4a37a642ae50f0c812d74353c37c017", "T1_ethereum-test_signtx.py::test_signtx[known_erc20_token]": "7ef8d3bbf6e299b41522769a893f72b04571fc7176bc93e5c1701a2b5721fb20",