mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-23 15:08:19 +00:00
feat(tests, python): update tests
This commit is contained in:
parent
afce401fa2
commit
ebaaa021e9
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
20
common/tests/fixtures/ethereum/getaddress.failed.json
vendored
Normal file
20
common/tests/fixtures/ethereum/getaddress.failed.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
132
common/tests/fixtures/ethereum/sign_tx_definitions.failed.json
vendored
Normal file
132
common/tests/fixtures/ethereum/sign_tx_definitions.failed.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
112
common/tests/fixtures/ethereum/sign_tx_eip1559_definitions.failed.json
vendored
Normal file
112
common/tests/fixtures/ethereum/sign_tx_eip1559_definitions.failed.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
32
common/tests/fixtures/ethereum/sign_typed_data.failed.json
vendored
Normal file
32
common/tests/fixtures/ethereum/sign_typed_data.failed.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
21
common/tests/fixtures/ethereum/signmessage.failed.json
vendored
Normal file
21
common/tests/fixtures/ethereum/signmessage.failed.json
vendored
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -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__:
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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"]
|
||||
|
216
python/tests/test_merkle_tree.py
Normal file
216
python/tests/test_merkle_tree.py
Normal file
@ -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 <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||
|
||||
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)
|
@ -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`.",
|
||||
)
|
||||
|
187
tests/device_tests/ethereum/ethereum_common.py
Normal file
187
tests/device_tests/ethereum/ethereum_common.py
Normal file
@ -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)
|
@ -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)
|
||||
|
@ -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,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -14,7 +14,9 @@
|
||||
# You should have received a copy of the License along with this library.
|
||||
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||
|
||||
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):
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user