mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-07 15:18:08 +00:00
feat(core): use protobuf messages for network and token definitions; style and typing fixes
This commit is contained in:
parent
62324c2de9
commit
c1f27e0a11
@ -20,7 +20,6 @@ from urllib3.util.retry import Retry
|
|||||||
|
|
||||||
from coin_info import (
|
from coin_info import (
|
||||||
Coin,
|
Coin,
|
||||||
CoinBuckets,
|
|
||||||
Coins,
|
Coins,
|
||||||
_load_builtin_erc20_tokens,
|
_load_builtin_erc20_tokens,
|
||||||
_load_builtin_ethereum_networks,
|
_load_builtin_ethereum_networks,
|
||||||
@ -389,7 +388,8 @@ def print_definitions_collision(
|
|||||||
Returns a tuple composed from the prompt result if prompted otherwise None and the default value."""
|
Returns a tuple composed from the prompt result if prompted otherwise None and the default value."""
|
||||||
if old_definitions:
|
if old_definitions:
|
||||||
old_defs_hash_no_metadata = [
|
old_defs_hash_no_metadata = [
|
||||||
hash_dict_on_keys(d, exclude_keys=["metadata"]) for d in old_definitions
|
hash_dict_on_keys(d, exclude_keys=["metadata", "coingecko_id"])
|
||||||
|
for d in old_definitions
|
||||||
]
|
]
|
||||||
|
|
||||||
default_index = None
|
default_index = None
|
||||||
@ -399,7 +399,7 @@ def print_definitions_collision(
|
|||||||
found = ""
|
found = ""
|
||||||
if (
|
if (
|
||||||
old_definitions
|
old_definitions
|
||||||
and hash_dict_on_keys(definition, exclude_keys=["metadata"])
|
and hash_dict_on_keys(definition, exclude_keys=["metadata", "coingecko_id"])
|
||||||
in old_defs_hash_no_metadata
|
in old_defs_hash_no_metadata
|
||||||
):
|
):
|
||||||
found = " (found in old definitions)"
|
found = " (found in old definitions)"
|
||||||
@ -566,6 +566,8 @@ def check_definitions_list(
|
|||||||
modified_definitions.remove((orig_def, new_def))
|
modified_definitions.remove((orig_def, new_def))
|
||||||
|
|
||||||
def any_in_top_100(*definitions) -> bool:
|
def any_in_top_100(*definitions) -> bool:
|
||||||
|
if top100_coingecko_ids is None:
|
||||||
|
return True
|
||||||
if definitions is not None:
|
if definitions is not None:
|
||||||
for d in definitions:
|
for d in definitions:
|
||||||
if d is not None and d.get("coingecko_id") in top100_coingecko_ids:
|
if d is not None and d.get("coingecko_id") in top100_coingecko_ids:
|
||||||
@ -625,10 +627,7 @@ def check_definitions_list(
|
|||||||
accept_change = True
|
accept_change = True
|
||||||
print_change = any_in_top_100(old_def, new_def)
|
print_change = any_in_top_100(old_def, new_def)
|
||||||
# if the change contains symbol change "--force" parameter must be used to be able to accept this change
|
# if the change contains symbol change "--force" parameter must be used to be able to accept this change
|
||||||
if (
|
if old_def.get("shortcut") != new_def.get("shortcut") and not force:
|
||||||
old_def.get("shortcut") != new_def.get("shortcut")
|
|
||||||
and not force
|
|
||||||
):
|
|
||||||
print(
|
print(
|
||||||
"\nERROR: Symbol change in this definition! To be able to approve this change re-run with `--force` argument."
|
"\nERROR: Symbol change in this definition! To be able to approve this change re-run with `--force` argument."
|
||||||
)
|
)
|
||||||
@ -677,16 +676,22 @@ def check_definitions_list(
|
|||||||
_set_definition_metadata(definition)
|
_set_definition_metadata(definition)
|
||||||
|
|
||||||
|
|
||||||
def _load_prepared_definitions(definitions_file: pathlib.Path) -> tuple[list[dict], list[dict]]:
|
def _load_prepared_definitions(
|
||||||
|
definitions_file: pathlib.Path,
|
||||||
|
) -> tuple[list[dict], list[dict]]:
|
||||||
if not definitions_file.is_file():
|
if not definitions_file.is_file():
|
||||||
click.ClickException(f"File {definitions_file} with prepared definitions does not exists or is not a file.")
|
click.ClickException(
|
||||||
|
f"File {definitions_file} with prepared definitions does not exists or is not a file."
|
||||||
|
)
|
||||||
|
|
||||||
prepared_definitions_data = load_json(definitions_file)
|
prepared_definitions_data = load_json(definitions_file)
|
||||||
try:
|
try:
|
||||||
networks_data = prepared_definitions_data["networks"]
|
networks_data = prepared_definitions_data["networks"]
|
||||||
tokens_data = prepared_definitions_data["tokens"]
|
tokens_data = prepared_definitions_data["tokens"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
click.ClickException(f"File with prepared definitions is not complete. Whole \"networks\" and/or \"tokens\" section are missing.")
|
click.ClickException(
|
||||||
|
'File with prepared definitions is not complete. Whole "networks" and/or "tokens" section are missing.'
|
||||||
|
)
|
||||||
|
|
||||||
networks: Coins = []
|
networks: Coins = []
|
||||||
for network_data in networks_data:
|
for network_data in networks_data:
|
||||||
@ -872,6 +877,15 @@ def prepare_definitions(
|
|||||||
cg_tokens = _load_erc20_tokens_from_coingecko(downloader, networks)
|
cg_tokens = _load_erc20_tokens_from_coingecko(downloader, networks)
|
||||||
repo_tokens = _load_erc20_tokens_from_repo(tokens_dir, networks)
|
repo_tokens = _load_erc20_tokens_from_repo(tokens_dir, networks)
|
||||||
|
|
||||||
|
# get data used in further processing now to be able to save cache before we do any
|
||||||
|
# token collision process and others
|
||||||
|
# get CoinGecko coin list
|
||||||
|
cg_coin_list = downloader.get_coingecko_coins_list()
|
||||||
|
# get top 100 coins
|
||||||
|
cg_top100 = downloader.get_coingecko_top100()
|
||||||
|
# save cache
|
||||||
|
downloader.save_cache()
|
||||||
|
|
||||||
# merge tokens
|
# merge tokens
|
||||||
tokens: List[Dict] = []
|
tokens: List[Dict] = []
|
||||||
cg_tokens_chain_id_and_address = []
|
cg_tokens_chain_id_and_address = []
|
||||||
@ -901,7 +915,6 @@ def prepare_definitions(
|
|||||||
|
|
||||||
# map coingecko ids to tokens
|
# map coingecko ids to tokens
|
||||||
tokens_by_chain_id_and_address = {(t["chain_id"], t["address"]): t for t in tokens}
|
tokens_by_chain_id_and_address = {(t["chain_id"], t["address"]): t for t in tokens}
|
||||||
cg_coin_list = downloader.get_coingecko_coins_list()
|
|
||||||
for coin in cg_coin_list:
|
for coin in cg_coin_list:
|
||||||
for platform_name, address in coin.get("platforms", dict()).items():
|
for platform_name, address in coin.get("platforms", dict()).items():
|
||||||
key = (coingecko_id_to_chain_id.get(platform_name), address)
|
key = (coingecko_id_to_chain_id.get(platform_name), address)
|
||||||
@ -909,10 +922,7 @@ def prepare_definitions(
|
|||||||
tokens_by_chain_id_and_address[key]["coingecko_id"] = coin["id"]
|
tokens_by_chain_id_and_address[key]["coingecko_id"] = coin["id"]
|
||||||
|
|
||||||
# load top 100 (by market cap) definitions from CoinGecko
|
# load top 100 (by market cap) definitions from CoinGecko
|
||||||
cg_top100_ids = [d["id"] for d in downloader.get_coingecko_top100()]
|
cg_top100_ids = [d["id"] for d in cg_top100]
|
||||||
|
|
||||||
# save cache
|
|
||||||
downloader.save_cache()
|
|
||||||
|
|
||||||
# check changes in definitions
|
# check changes in definitions
|
||||||
if old_defs is not None:
|
if old_defs is not None:
|
||||||
@ -970,9 +980,11 @@ def prepare_definitions(
|
|||||||
"--deffile",
|
"--deffile",
|
||||||
type=click.Path(resolve_path=True, dir_okay=False, path_type=pathlib.Path),
|
type=click.Path(resolve_path=True, dir_okay=False, path_type=pathlib.Path),
|
||||||
default="./definitions-latest.json",
|
default="./definitions-latest.json",
|
||||||
help="File where the prepared definitions are saved in json format."
|
help="File where the prepared definitions are saved in json format.",
|
||||||
)
|
)
|
||||||
def sign_definitions(outdir: pathlib.Path, privatekey: TextIO, deffile: pathlib.Path) -> None:
|
def sign_definitions(
|
||||||
|
outdir: pathlib.Path, privatekey: TextIO, deffile: pathlib.Path
|
||||||
|
) -> None:
|
||||||
"""Generate signed Ethereum definitions for python-trezor and others."""
|
"""Generate signed Ethereum definitions for python-trezor and others."""
|
||||||
hex_key = None
|
hex_key = None
|
||||||
if privatekey is None:
|
if privatekey is None:
|
||||||
@ -992,7 +1004,7 @@ def sign_definitions(outdir: pathlib.Path, privatekey: TextIO, deffile: pathlib.
|
|||||||
|
|
||||||
if complete_file_path.exists():
|
if complete_file_path.exists():
|
||||||
raise click.ClickException(
|
raise click.ClickException(
|
||||||
f"Definition \"{complete_file_path}\" already generated - attempt to generate another definition."
|
f'Definition "{complete_file_path}" already generated - attempt to generate another definition.'
|
||||||
)
|
)
|
||||||
|
|
||||||
directory.mkdir(parents=True, exist_ok=True)
|
directory.mkdir(parents=True, exist_ok=True)
|
||||||
@ -1039,7 +1051,8 @@ def sign_definitions(outdir: pathlib.Path, privatekey: TextIO, deffile: pathlib.
|
|||||||
definitions_by_serialization: dict[bytes, dict] = dict()
|
definitions_by_serialization: dict[bytes, dict] = dict()
|
||||||
for network in networks:
|
for network in networks:
|
||||||
ser = serialize_eth_info(
|
ser = serialize_eth_info(
|
||||||
eth_info_from_dict(network, EthereumNetworkInfo), EthereumDefinitionType.NETWORK
|
eth_info_from_dict(network, EthereumNetworkInfo),
|
||||||
|
EthereumDefinitionType.NETWORK,
|
||||||
)
|
)
|
||||||
network["serialized"] = ser
|
network["serialized"] = ser
|
||||||
definitions_by_serialization[ser] = network
|
definitions_by_serialization[ser] = network
|
||||||
@ -1052,8 +1065,8 @@ def sign_definitions(outdir: pathlib.Path, privatekey: TextIO, deffile: pathlib.
|
|||||||
|
|
||||||
# build Merkle tree
|
# build Merkle tree
|
||||||
mt = MerkleTree(
|
mt = MerkleTree(
|
||||||
[network["serialized"] for network in networks] +
|
[network["serialized"] for network in networks]
|
||||||
[token["serialized"] for token in tokens]
|
+ [token["serialized"] for token in tokens]
|
||||||
)
|
)
|
||||||
|
|
||||||
# sign tree root hash
|
# sign tree root hash
|
||||||
|
@ -6,12 +6,15 @@ except ImportError:
|
|||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
|
|
||||||
class Node():
|
class Node:
|
||||||
"""
|
"""
|
||||||
Single node of Merkle tree.
|
Single node of Merkle tree.
|
||||||
"""
|
"""
|
||||||
def __init__(self: "Node", left: Union[bytes, "Node"], right: Optional["Node"] = None) -> None:
|
|
||||||
self.is_leaf = (left is None) != (right is None) # XOR
|
def __init__(
|
||||||
|
self: "Node", left: Union[bytes, "Node"], right: Optional["Node"] = None
|
||||||
|
) -> None:
|
||||||
|
self.is_leaf = (left is None) != (right is None) # XOR
|
||||||
if self.is_leaf:
|
if self.is_leaf:
|
||||||
self.raw_value = left
|
self.raw_value = left
|
||||||
self.hash = None
|
self.hash = None
|
||||||
@ -43,11 +46,12 @@ class Node():
|
|||||||
self.right_child.add_to_proof(proof)
|
self.right_child.add_to_proof(proof)
|
||||||
|
|
||||||
|
|
||||||
class MerkleTree():
|
class MerkleTree:
|
||||||
"""
|
"""
|
||||||
Simple Merkle tree that implements the building of Merkle tree itself and generate proofs
|
Simple Merkle tree that implements the building of Merkle tree itself and generate proofs
|
||||||
for leaf nodes.
|
for leaf nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, values: list[bytes]) -> None:
|
def __init__(self, values: list[bytes]) -> None:
|
||||||
self.leaves = [Node(v) for v in values]
|
self.leaves = [Node(v) for v in values]
|
||||||
|
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
|
from typing import Any
|
||||||
from ubinascii import unhexlify
|
from ubinascii import unhexlify
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from apps.ethereum import tokens
|
|
||||||
|
|
||||||
from trezor import protobuf, wire
|
from trezor import protobuf, wire
|
||||||
from trezor.crypto.curve import ed25519
|
from trezor.crypto.curve import ed25519
|
||||||
from trezor.enums import EthereumDefinitionType
|
from trezor.enums import EthereumDefinitionType
|
||||||
from trezor.messages import EthereumNetworkInfo, EthereumTokenInfo
|
from trezor.messages import EthereumNetworkInfo, EthereumTokenInfo
|
||||||
|
|
||||||
|
from apps.ethereum import tokens
|
||||||
|
|
||||||
from . import helpers, networks
|
from . import helpers, networks
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from trezor.protobuf import MessageType
|
|
||||||
|
|
||||||
from .networks import NetworkInfo
|
|
||||||
from .tokens import TokenInfo
|
|
||||||
|
|
||||||
|
|
||||||
DEFINITIONS_PUBLIC_KEY = b""
|
DEFINITIONS_PUBLIC_KEY = b""
|
||||||
MIN_DATA_VERSION = 1
|
MIN_DATA_VERSION = 1
|
||||||
FORMAT_VERSION = "trzd1"
|
FORMAT_VERSION = "trzd1"
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
DEFINITIONS_DEV_PUBLIC_KEY = unhexlify("db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d")
|
DEFINITIONS_DEV_PUBLIC_KEY = unhexlify(
|
||||||
|
"db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EthereumDefinitionParser:
|
class EthereumDefinitionParser:
|
||||||
@ -31,15 +26,21 @@ class EthereumDefinitionParser:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# prefix
|
# prefix
|
||||||
self.format_version = definition_bytes[:8].rstrip(b'\0').decode("utf-8")
|
self.format_version = definition_bytes[:8].rstrip(b"\0").decode("utf-8")
|
||||||
self.definition_type: int = definition_bytes[8]
|
self.definition_type: int = definition_bytes[8]
|
||||||
self.data_version = int.from_bytes(definition_bytes[9:13], 'big')
|
self.data_version = int.from_bytes(definition_bytes[9:13], "big")
|
||||||
self.payload_length_in_bytes = int.from_bytes(definition_bytes[13:15], 'big')
|
self.payload_length_in_bytes = int.from_bytes(
|
||||||
|
definition_bytes[13:15], "big"
|
||||||
|
)
|
||||||
actual_position += 8 + 1 + 4 + 2
|
actual_position += 8 + 1 + 4 + 2
|
||||||
|
|
||||||
# payload
|
# payload
|
||||||
self.payload = definition_bytes[actual_position:(actual_position + self.payload_length_in_bytes)]
|
self.payload = definition_bytes[
|
||||||
self.payload_with_prefix = definition_bytes[:(actual_position + self.payload_length_in_bytes)]
|
actual_position : (actual_position + self.payload_length_in_bytes)
|
||||||
|
]
|
||||||
|
self.payload_with_prefix = definition_bytes[
|
||||||
|
: (actual_position + self.payload_length_in_bytes)
|
||||||
|
]
|
||||||
actual_position += self.payload_length_in_bytes
|
actual_position += self.payload_length_in_bytes
|
||||||
|
|
||||||
# suffix - Merkle tree proof and signed root hash
|
# suffix - Merkle tree proof and signed root hash
|
||||||
@ -47,16 +48,20 @@ class EthereumDefinitionParser:
|
|||||||
actual_position += 1
|
actual_position += 1
|
||||||
self.proof: list[bytes] = []
|
self.proof: list[bytes] = []
|
||||||
for _ in range(self.proof_length):
|
for _ in range(self.proof_length):
|
||||||
self.proof.append(definition_bytes[actual_position:(actual_position + 32)])
|
self.proof.append(
|
||||||
|
definition_bytes[actual_position : (actual_position + 32)]
|
||||||
|
)
|
||||||
actual_position += 32
|
actual_position += 32
|
||||||
self.signed_tree_root = definition_bytes[actual_position:(actual_position + 64)]
|
self.signed_tree_root = definition_bytes[
|
||||||
|
actual_position : (actual_position + 64)
|
||||||
|
]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise wire.DataError("Invalid Ethereum definition.")
|
raise wire.DataError("Invalid Ethereum definition.")
|
||||||
|
|
||||||
|
|
||||||
def decode_definition(
|
def decode_definition(
|
||||||
definition: bytes, expected_type: EthereumDefinitionType
|
definition: bytes, expected_type: EthereumDefinitionType
|
||||||
) -> NetworkInfo | TokenInfo:
|
) -> EthereumNetworkInfo | EthereumTokenInfo:
|
||||||
# check network definition
|
# check network definition
|
||||||
parsed_definition = EthereumDefinitionParser(definition)
|
parsed_definition = EthereumDefinitionParser(definition)
|
||||||
|
|
||||||
@ -75,6 +80,7 @@ def decode_definition(
|
|||||||
# at the end verify the signature - compute Merkle tree root hash using provided leaf data and proof
|
# at the end verify the signature - compute Merkle tree root hash using provided leaf data and proof
|
||||||
def compute_mt_root_hash(data: bytes, proof: list[bytes]) -> bytes:
|
def compute_mt_root_hash(data: bytes, proof: list[bytes]) -> bytes:
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
|
|
||||||
hash = sha256(b"\x00" + data).digest()
|
hash = sha256(b"\x00" + data).digest()
|
||||||
for p in proof:
|
for p in proof:
|
||||||
hash_a = min(hash, p)
|
hash_a = min(hash, p)
|
||||||
@ -84,13 +90,21 @@ def decode_definition(
|
|||||||
return hash
|
return hash
|
||||||
|
|
||||||
# verify Merkle proof
|
# verify Merkle proof
|
||||||
root_hash = compute_mt_root_hash(parsed_definition.payload_with_prefix, parsed_definition.proof)
|
root_hash = compute_mt_root_hash(
|
||||||
|
parsed_definition.payload_with_prefix, parsed_definition.proof
|
||||||
|
)
|
||||||
|
|
||||||
if not ed25519.verify(DEFINITIONS_PUBLIC_KEY, parsed_definition.signed_tree_root, root_hash):
|
if not ed25519.verify(
|
||||||
|
DEFINITIONS_PUBLIC_KEY, parsed_definition.signed_tree_root, root_hash
|
||||||
|
):
|
||||||
error_msg = wire.DataError("Ethereum definition signature is invalid.")
|
error_msg = wire.DataError("Ethereum definition signature is invalid.")
|
||||||
if __debug__:
|
if __debug__:
|
||||||
# check against dev key
|
# check against dev key
|
||||||
if not ed25519.verify(DEFINITIONS_DEV_PUBLIC_KEY, parsed_definition.signed_tree_root, root_hash):
|
if not ed25519.verify(
|
||||||
|
DEFINITIONS_DEV_PUBLIC_KEY,
|
||||||
|
parsed_definition.signed_tree_root,
|
||||||
|
root_hash,
|
||||||
|
):
|
||||||
raise error_msg
|
raise error_msg
|
||||||
else:
|
else:
|
||||||
raise error_msg
|
raise error_msg
|
||||||
@ -98,35 +112,15 @@ def decode_definition(
|
|||||||
# decode it if it's OK
|
# decode it if it's OK
|
||||||
if expected_type == EthereumDefinitionType.NETWORK:
|
if expected_type == EthereumDefinitionType.NETWORK:
|
||||||
info = protobuf.decode(parsed_definition.payload, EthereumNetworkInfo, True)
|
info = protobuf.decode(parsed_definition.payload, EthereumNetworkInfo, True)
|
||||||
|
|
||||||
# TODO: temporarily convert to internal class
|
|
||||||
if info is not None:
|
|
||||||
from .networks import NetworkInfo
|
|
||||||
info = NetworkInfo(
|
|
||||||
chain_id=info.chain_id,
|
|
||||||
slip44=info.slip44,
|
|
||||||
shortcut=info.shortcut,
|
|
||||||
name=info.name,
|
|
||||||
rskip60=info.rskip60
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
info = protobuf.decode(parsed_definition.payload, EthereumTokenInfo, True)
|
info = protobuf.decode(parsed_definition.payload, EthereumTokenInfo, True)
|
||||||
|
|
||||||
# TODO: temporarily convert to internal class
|
|
||||||
if info is not None:
|
|
||||||
from .tokens import TokenInfo
|
|
||||||
info = TokenInfo(
|
|
||||||
symbol=info.symbol,
|
|
||||||
decimals=info.decimals,
|
|
||||||
address=info.address,
|
|
||||||
chain_id=info.chain_id,
|
|
||||||
name=info.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def _get_network_definiton(encoded_network_definition: bytes | None, ref_chain_id: int | None = None) -> NetworkInfo | None:
|
def _get_network_definiton(
|
||||||
|
encoded_network_definition: bytes | None, ref_chain_id: int | None = None
|
||||||
|
) -> EthereumNetworkInfo | None:
|
||||||
if encoded_network_definition is None and ref_chain_id is None:
|
if encoded_network_definition is None and ref_chain_id is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -134,23 +128,31 @@ def _get_network_definiton(encoded_network_definition: bytes | None, ref_chain_i
|
|||||||
# if we have a built-in definition, use it
|
# if we have a built-in definition, use it
|
||||||
network = networks.by_chain_id(ref_chain_id)
|
network = networks.by_chain_id(ref_chain_id)
|
||||||
if network is not None:
|
if network is not None:
|
||||||
return network
|
return network # type: EthereumNetworkInfo
|
||||||
|
|
||||||
if encoded_network_definition is not None:
|
if encoded_network_definition is not None:
|
||||||
# get definition if it was send
|
# get definition if it was send
|
||||||
network = decode_definition(encoded_network_definition, EthereumDefinitionType.NETWORK)
|
network = decode_definition(
|
||||||
|
encoded_network_definition, EthereumDefinitionType.NETWORK
|
||||||
|
)
|
||||||
|
|
||||||
# check referential chain_id with encoded chain_id
|
# check referential chain_id with encoded chain_id
|
||||||
if ref_chain_id is not None and network.chain_id != ref_chain_id:
|
if ref_chain_id is not None and network.chain_id != ref_chain_id:
|
||||||
raise wire.DataError("Invalid network definition - chain IDs not equal.")
|
raise wire.DataError("Invalid network definition - chain IDs not equal.")
|
||||||
|
|
||||||
return network
|
return network # type: ignore [Expression of type "EthereumNetworkInfo | EthereumTokenInfo" cannot be assigned to return type "EthereumNetworkInfo | None"]
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _get_token_definiton(encoded_token_definition: bytes | None, ref_chain_id: int | None = None, ref_address: bytes | None = None) -> TokenInfo:
|
def _get_token_definiton(
|
||||||
if encoded_token_definition is None and (ref_chain_id is None or ref_address is None):
|
encoded_token_definition: bytes | None,
|
||||||
|
ref_chain_id: int | None = None,
|
||||||
|
ref_address: bytes | None = None,
|
||||||
|
) -> EthereumTokenInfo:
|
||||||
|
if encoded_token_definition is None and (
|
||||||
|
ref_chain_id is None or ref_address is None
|
||||||
|
):
|
||||||
return tokens.UNKNOWN_TOKEN
|
return tokens.UNKNOWN_TOKEN
|
||||||
|
|
||||||
# if we have a built-in definition, use it
|
# if we have a built-in definition, use it
|
||||||
@ -161,12 +163,13 @@ def _get_token_definiton(encoded_token_definition: bytes | None, ref_chain_id: i
|
|||||||
|
|
||||||
if encoded_token_definition is not None:
|
if encoded_token_definition is not None:
|
||||||
# get definition if it was send
|
# get definition if it was send
|
||||||
token = decode_definition(encoded_token_definition, EthereumDefinitionType.TOKEN)
|
token: EthereumTokenInfo = decode_definition( # type: ignore [Expression of type "EthereumNetworkInfo | EthereumTokenInfo" cannot be assigned to declared type "EthereumTokenInfo"]
|
||||||
|
encoded_token_definition, EthereumDefinitionType.TOKEN
|
||||||
|
)
|
||||||
|
|
||||||
# check token against ref_chain_id and ref_address
|
# check token against ref_chain_id and ref_address
|
||||||
if (
|
if (ref_chain_id is None or token.chain_id == ref_chain_id) and (
|
||||||
(ref_chain_id is None or token.chain_id == ref_chain_id)
|
ref_address is None or token.address == ref_address
|
||||||
and (ref_address is None or token.address == ref_address)
|
|
||||||
):
|
):
|
||||||
return token
|
return token
|
||||||
|
|
||||||
@ -175,6 +178,7 @@ def _get_token_definiton(encoded_token_definition: bytes | None, ref_chain_id: i
|
|||||||
|
|
||||||
class EthereumDefinitions:
|
class EthereumDefinitions:
|
||||||
"""Class that holds Ethereum definitions - network and tokens. Prefers built-in definitions over encoded ones."""
|
"""Class that holds Ethereum definitions - network and tokens. Prefers built-in definitions over encoded ones."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
encoded_network_definition: bytes | None = None,
|
encoded_network_definition: bytes | None = None,
|
||||||
@ -183,20 +187,22 @@ class EthereumDefinitions:
|
|||||||
ref_token_address: bytes | None = None,
|
ref_token_address: bytes | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.network = _get_network_definiton(encoded_network_definition, ref_chain_id)
|
self.network = _get_network_definiton(encoded_network_definition, ref_chain_id)
|
||||||
self.token_dict: dict[bytes, TokenInfo] = dict()
|
self.token_dict: dict[bytes, EthereumTokenInfo] = dict()
|
||||||
|
|
||||||
# if we have some network, we can try to get token
|
# if we have some network, we can try to get token
|
||||||
if self.network is not None:
|
if self.network is not None:
|
||||||
token = _get_token_definiton(encoded_token_definition, self.network.chain_id, ref_token_address)
|
token = _get_token_definiton(
|
||||||
|
encoded_token_definition, self.network.chain_id, ref_token_address
|
||||||
|
)
|
||||||
if token is not tokens.UNKNOWN_TOKEN:
|
if token is not tokens.UNKNOWN_TOKEN:
|
||||||
self.token_dict[token.address] = token
|
self.token_dict[token.address] = token
|
||||||
|
|
||||||
|
|
||||||
def get_definitions_from_msg(msg: MessageType) -> EthereumDefinitions:
|
def get_definitions_from_msg(msg: Any) -> EthereumDefinitions:
|
||||||
encoded_network_definition: bytes | None = None
|
encoded_network_definition: bytes | None = None
|
||||||
encoded_token_definition: bytes | None = None
|
encoded_token_definition: bytes | None = None
|
||||||
chain_id: int | None = None
|
chain_id: int | None = None
|
||||||
token_address: str | None = None
|
token_address: bytes | None = None
|
||||||
|
|
||||||
# first try to get both definitions
|
# first try to get both definitions
|
||||||
try:
|
try:
|
||||||
@ -225,4 +231,6 @@ def get_definitions_from_msg(msg: MessageType) -> EthereumDefinitions:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return EthereumDefinitions(encoded_network_definition, encoded_token_definition, chain_id, token_address)
|
return EthereumDefinitions(
|
||||||
|
encoded_network_definition, encoded_token_definition, chain_id, token_address
|
||||||
|
)
|
||||||
|
@ -20,7 +20,10 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
@with_keychain_from_path_and_defs(*PATTERNS_ADDRESS)
|
@with_keychain_from_path_and_defs(*PATTERNS_ADDRESS)
|
||||||
async def get_address(
|
async def get_address(
|
||||||
ctx: Context, msg: EthereumGetAddress, keychain: Keychain, defs: definitions.EthereumDefinitions
|
ctx: Context,
|
||||||
|
msg: EthereumGetAddress,
|
||||||
|
keychain: Keychain,
|
||||||
|
defs: definitions.EthereumDefinitions,
|
||||||
) -> EthereumAddress:
|
) -> EthereumAddress:
|
||||||
from trezor.messages import EthereumAddress
|
from trezor.messages import EthereumAddress
|
||||||
from trezor.ui.layouts import show_address
|
from trezor.ui.layouts import show_address
|
||||||
@ -36,7 +39,7 @@ async def get_address(
|
|||||||
|
|
||||||
if len(msg.address_n) > 1: # path has slip44 network identifier
|
if len(msg.address_n) > 1: # path has slip44 network identifier
|
||||||
slip44 = msg.address_n[1] & 0x7FFF_FFFF
|
slip44 = msg.address_n[1] & 0x7FFF_FFFF
|
||||||
if slip44 == defs.network.slip44:
|
if defs.network is not None and slip44 == defs.network.slip44:
|
||||||
network = defs.network
|
network = defs.network
|
||||||
else:
|
else:
|
||||||
network = networks.by_slip44(slip44)
|
network = networks.by_slip44(slip44)
|
||||||
|
@ -5,10 +5,12 @@ from .networks import by_chain_id
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from trezor.messages import EthereumFieldType
|
from trezor.messages import EthereumFieldType
|
||||||
from .networks import NetworkInfo
|
from .networks import EthereumNetworkInfo
|
||||||
|
|
||||||
|
|
||||||
def address_from_bytes(address_bytes: bytes, network: NetworkInfo = by_chain_id(1)) -> str:
|
def address_from_bytes(
|
||||||
|
address_bytes: bytes, network: EthereumNetworkInfo | None = by_chain_id(1)
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Converts address in bytes to a checksummed string as defined
|
Converts address in bytes to a checksummed string as defined
|
||||||
in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
|
in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
|
||||||
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|||||||
from apps.common import paths
|
from apps.common import paths
|
||||||
from apps.common.keychain import get_keychain
|
from apps.common.keychain import get_keychain
|
||||||
|
|
||||||
from . import CURVE, networks, definitions
|
from . import CURVE, definitions, networks
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Awaitable, Callable, Iterable, TypeVar
|
from typing import Awaitable, Callable, Iterable, TypeVar
|
||||||
@ -15,20 +15,31 @@ if TYPE_CHECKING:
|
|||||||
from trezor.messages import (
|
from trezor.messages import (
|
||||||
EthereumGetAddress,
|
EthereumGetAddress,
|
||||||
EthereumGetPublicKey,
|
EthereumGetPublicKey,
|
||||||
|
EthereumNetworkInfo,
|
||||||
EthereumSignMessage,
|
EthereumSignMessage,
|
||||||
EthereumSignTx,
|
EthereumSignTx,
|
||||||
EthereumSignTxEIP1559,
|
EthereumSignTxEIP1559,
|
||||||
EthereumSignTypedData,
|
EthereumSignTypedData,
|
||||||
)
|
)
|
||||||
|
|
||||||
from apps.common.keychain import MsgIn as MsgInGeneric, MsgOut, Handler, HandlerWithKeychain
|
from apps.common.keychain import (
|
||||||
|
MsgIn as MsgInGeneric,
|
||||||
|
MsgOut,
|
||||||
|
Handler,
|
||||||
|
HandlerWithKeychain,
|
||||||
|
)
|
||||||
|
|
||||||
# messages for "with_keychain_from_path" decorator
|
# messages for "with_keychain_from_path" decorator
|
||||||
MsgInKeychainPath = TypeVar("MsgInKeychainPath", bound=EthereumGetPublicKey)
|
MsgInKeychainPath = TypeVar("MsgInKeychainPath", bound=EthereumGetPublicKey)
|
||||||
# messages for "with_keychain_from_path_and_defs" decorator
|
# messages for "with_keychain_from_path_and_defs" decorator
|
||||||
MsgInKeychainPathDefs = TypeVar("MsgInKeychainPathDefs", bound=EthereumGetAddress | EthereumSignMessage | EthereumSignTypedData)
|
MsgInKeychainPathDefs = TypeVar(
|
||||||
|
"MsgInKeychainPathDefs",
|
||||||
|
bound=EthereumGetAddress | EthereumSignMessage | EthereumSignTypedData,
|
||||||
|
)
|
||||||
# messages for "with_keychain_from_chain_id_and_defs" decorator
|
# messages for "with_keychain_from_chain_id_and_defs" decorator
|
||||||
MsgInKeychainChainIdDefs = TypeVar("MsgInKeychainChainIdDefs", bound=EthereumSignTx | EthereumSignTxEIP1559)
|
MsgInKeychainChainIdDefs = TypeVar(
|
||||||
|
"MsgInKeychainChainIdDefs", bound=EthereumSignTx | EthereumSignTxEIP1559
|
||||||
|
)
|
||||||
|
|
||||||
# TODO: check the types of messages
|
# TODO: check the types of messages
|
||||||
HandlerWithKeychainAndDefinitions = Callable[[Context, MsgInGeneric, Keychain, definitions.EthereumDefinitions], Awaitable[MsgOut]]
|
HandlerWithKeychainAndDefinitions = Callable[[Context, MsgInGeneric, Keychain, definitions.EthereumDefinitions], Awaitable[MsgOut]]
|
||||||
@ -48,7 +59,9 @@ PATTERNS_ADDRESS = (
|
|||||||
|
|
||||||
|
|
||||||
def _schemas_from_address_n(
|
def _schemas_from_address_n(
|
||||||
patterns: Iterable[str], address_n: paths.Bip32Path, network_info: networks.NetworkInfo | None
|
patterns: Iterable[str],
|
||||||
|
address_n: paths.Bip32Path,
|
||||||
|
network_info: EthereumNetworkInfo | None,
|
||||||
) -> Iterable[paths.PathSchema]:
|
) -> Iterable[paths.PathSchema]:
|
||||||
if len(address_n) < 2:
|
if len(address_n) < 2:
|
||||||
return ()
|
return ()
|
||||||
@ -104,7 +117,9 @@ def with_keychain_from_path_and_defs(
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def _schemas_from_chain_id(network_info: networks.NetworkInfo | None) -> Iterable[paths.PathSchema]:
|
def _schemas_from_chain_id(
|
||||||
|
network_info: EthereumNetworkInfo | None,
|
||||||
|
) -> Iterable[paths.PathSchema]:
|
||||||
slip44_id: tuple[int, ...]
|
slip44_id: tuple[int, ...]
|
||||||
if network_info is None:
|
if network_info is None:
|
||||||
# allow Ethereum or testnet paths for unknown networks
|
# allow Ethereum or testnet paths for unknown networks
|
||||||
|
@ -21,15 +21,15 @@ if TYPE_CHECKING:
|
|||||||
from trezor.wire import Context
|
from trezor.wire import Context
|
||||||
from . import tokens
|
from . import tokens
|
||||||
|
|
||||||
from . import tokens
|
from trezor.messages import EthereumNetworkInfo, EthereumTokenInfo
|
||||||
|
|
||||||
|
|
||||||
def require_confirm_tx(
|
def require_confirm_tx(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
to_bytes: bytes,
|
to_bytes: bytes,
|
||||||
value: int,
|
value: int,
|
||||||
network: networks.NetworkInfo,
|
network: EthereumNetworkInfo | None,
|
||||||
token: tokens.TokenInfo,
|
token: EthereumTokenInfo | None,
|
||||||
) -> Awaitable[None]:
|
) -> Awaitable[None]:
|
||||||
from .helpers import address_from_bytes
|
from .helpers import address_from_bytes
|
||||||
from trezor.ui.layouts import confirm_output
|
from trezor.ui.layouts import confirm_output
|
||||||
@ -53,8 +53,8 @@ async def require_confirm_fee(
|
|||||||
spending: int,
|
spending: int,
|
||||||
gas_price: int,
|
gas_price: int,
|
||||||
gas_limit: int,
|
gas_limit: int,
|
||||||
network: networks.NetworkInfo,
|
network: EthereumNetworkInfo | None,
|
||||||
token: tokens.TokenInfo,
|
token: EthereumTokenInfo | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_amount(
|
await confirm_amount(
|
||||||
ctx,
|
ctx,
|
||||||
@ -77,8 +77,8 @@ async def require_confirm_eip1559_fee(
|
|||||||
max_priority_fee: int,
|
max_priority_fee: int,
|
||||||
max_gas_fee: int,
|
max_gas_fee: int,
|
||||||
gas_limit: int,
|
gas_limit: int,
|
||||||
network: networks.NetworkInfo,
|
network: EthereumNetworkInfo | None,
|
||||||
token: tokens.TokenInfo,
|
token: EthereumTokenInfo | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_amount(
|
await confirm_amount(
|
||||||
ctx,
|
ctx,
|
||||||
@ -253,7 +253,9 @@ async def confirm_typed_value(
|
|||||||
|
|
||||||
|
|
||||||
def format_ethereum_amount(
|
def format_ethereum_amount(
|
||||||
value: int, token: tokens.TokenInfo | None, network_info: networks.NetworkInfo | None
|
value: int,
|
||||||
|
token: EthereumTokenInfo | None,
|
||||||
|
network_info: EthereumNetworkInfo | None,
|
||||||
) -> str:
|
) -> str:
|
||||||
from trezor.strings import format_amount
|
from trezor.strings import format_amount
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from apps.common.paths import HARDENED
|
from apps.common.paths import HARDENED
|
||||||
|
from trezor.messages import EthereumNetworkInfo
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
@ -25,11 +26,11 @@ if TYPE_CHECKING:
|
|||||||
UNKNOWN_NETWORK_SHORTCUT = "UNKN"
|
UNKNOWN_NETWORK_SHORTCUT = "UNKN"
|
||||||
|
|
||||||
|
|
||||||
def by_chain_id(chain_id: int) -> "NetworkInfo" | None:
|
def by_chain_id(chain_id: int) -> EthereumNetworkInfo | None:
|
||||||
for n in _networks_iterator():
|
for n in _networks_iterator():
|
||||||
n_chain_id = n[0]
|
n_chain_id = n[0]
|
||||||
if n_chain_id == chain_id:
|
if n_chain_id == chain_id:
|
||||||
return NetworkInfo(
|
return EthereumNetworkInfo(
|
||||||
chain_id=n[0],
|
chain_id=n[0],
|
||||||
slip44=n[1],
|
slip44=n[1],
|
||||||
shortcut=n[2],
|
shortcut=n[2],
|
||||||
@ -39,11 +40,11 @@ def by_chain_id(chain_id: int) -> "NetworkInfo" | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def by_slip44(slip44: int) -> "NetworkInfo" | None:
|
def by_slip44(slip44: int) -> EthereumNetworkInfo | None:
|
||||||
for n in _networks_iterator():
|
for n in _networks_iterator():
|
||||||
n_slip44 = n[1]
|
n_slip44 = n[1]
|
||||||
if n_slip44 == slip44:
|
if n_slip44 == slip44:
|
||||||
return NetworkInfo(
|
return EthereumNetworkInfo(
|
||||||
chain_id=n[0],
|
chain_id=n[0],
|
||||||
slip44=n[1],
|
slip44=n[1],
|
||||||
shortcut=n[2],
|
shortcut=n[2],
|
||||||
@ -59,17 +60,6 @@ def all_slip44_ids_hardened() -> Iterator[int]:
|
|||||||
yield n[1] | HARDENED
|
yield n[1] | HARDENED
|
||||||
|
|
||||||
|
|
||||||
class NetworkInfo:
|
|
||||||
def __init__(
|
|
||||||
self, chain_id: int, slip44: int, shortcut: str, name: str, rskip60: bool
|
|
||||||
) -> None:
|
|
||||||
self.chain_id = chain_id
|
|
||||||
self.slip44 = slip44
|
|
||||||
self.shortcut = shortcut
|
|
||||||
self.name = name
|
|
||||||
self.rskip60 = rskip60
|
|
||||||
|
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
def _networks_iterator() -> Iterator[NetworkInfoTuple]:
|
def _networks_iterator() -> Iterator[NetworkInfoTuple]:
|
||||||
yield (
|
yield (
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from apps.common.paths import HARDENED
|
from apps.common.paths import HARDENED
|
||||||
|
from trezor.messages import EthereumNetworkInfo
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
@ -25,11 +26,11 @@ if TYPE_CHECKING:
|
|||||||
UNKNOWN_NETWORK_SHORTCUT = "UNKN"
|
UNKNOWN_NETWORK_SHORTCUT = "UNKN"
|
||||||
|
|
||||||
|
|
||||||
def by_chain_id(chain_id: int) -> "NetworkInfo" | None:
|
def by_chain_id(chain_id: int) -> EthereumNetworkInfo | None:
|
||||||
for n in _networks_iterator():
|
for n in _networks_iterator():
|
||||||
n_chain_id = n[0]
|
n_chain_id = n[0]
|
||||||
if n_chain_id == chain_id:
|
if n_chain_id == chain_id:
|
||||||
return NetworkInfo(
|
return EthereumNetworkInfo(
|
||||||
chain_id=n[0],
|
chain_id=n[0],
|
||||||
slip44=n[1],
|
slip44=n[1],
|
||||||
shortcut=n[2],
|
shortcut=n[2],
|
||||||
@ -39,11 +40,11 @@ def by_chain_id(chain_id: int) -> "NetworkInfo" | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def by_slip44(slip44: int) -> "NetworkInfo" | None:
|
def by_slip44(slip44: int) -> EthereumNetworkInfo | None:
|
||||||
for n in _networks_iterator():
|
for n in _networks_iterator():
|
||||||
n_slip44 = n[1]
|
n_slip44 = n[1]
|
||||||
if n_slip44 == slip44:
|
if n_slip44 == slip44:
|
||||||
return NetworkInfo(
|
return EthereumNetworkInfo(
|
||||||
chain_id=n[0],
|
chain_id=n[0],
|
||||||
slip44=n[1],
|
slip44=n[1],
|
||||||
shortcut=n[2],
|
shortcut=n[2],
|
||||||
@ -59,17 +60,6 @@ def all_slip44_ids_hardened() -> Iterator[int]:
|
|||||||
yield n[1] | HARDENED
|
yield n[1] | HARDENED
|
||||||
|
|
||||||
|
|
||||||
class NetworkInfo:
|
|
||||||
def __init__(
|
|
||||||
self, chain_id: int, slip44: int, shortcut: str, name: str, rskip60: bool
|
|
||||||
) -> None:
|
|
||||||
self.chain_id = chain_id
|
|
||||||
self.slip44 = slip44
|
|
||||||
self.shortcut = shortcut
|
|
||||||
self.name = name
|
|
||||||
self.rskip60 = rskip60
|
|
||||||
|
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
def _networks_iterator() -> Iterator[NetworkInfoTuple]:
|
def _networks_iterator() -> Iterator[NetworkInfoTuple]:
|
||||||
% for n in supported_on("trezor2", eth):
|
% for n in supported_on("trezor2", eth):
|
||||||
|
@ -25,7 +25,10 @@ def message_digest(message: bytes) -> bytes:
|
|||||||
|
|
||||||
@with_keychain_from_path_and_defs(*PATTERNS_ADDRESS)
|
@with_keychain_from_path_and_defs(*PATTERNS_ADDRESS)
|
||||||
async def sign_message(
|
async def sign_message(
|
||||||
ctx: Context, msg: EthereumSignMessage, keychain: Keychain, defs: definitions.EthereumDefinitions
|
ctx: Context,
|
||||||
|
msg: EthereumSignMessage,
|
||||||
|
keychain: Keychain,
|
||||||
|
defs: definitions.EthereumDefinitions,
|
||||||
) -> EthereumMessageSignature:
|
) -> EthereumMessageSignature:
|
||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import secp256k1
|
||||||
from trezor.messages import EthereumMessageSignature
|
from trezor.messages import EthereumMessageSignature
|
||||||
|
@ -9,7 +9,7 @@ from .keychain import with_keychain_from_chain_id_and_defs
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from apps.common.keychain import Keychain
|
from apps.common.keychain import Keychain
|
||||||
from trezor.messages import EthereumSignTx, EthereumTxAck
|
from trezor.messages import EthereumSignTx, EthereumTxAck, EthereumTokenInfo
|
||||||
from trezor.wire import Context
|
from trezor.wire import Context
|
||||||
|
|
||||||
from .keychain import MsgInKeychainChainIdDefs
|
from .keychain import MsgInKeychainChainIdDefs
|
||||||
@ -45,7 +45,9 @@ async def sign_tx(
|
|||||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||||
|
|
||||||
# Handle ERC20s
|
# Handle ERC20s
|
||||||
token, address_bytes, recipient, value = await handle_erc20(ctx, msg, defs.token_dict)
|
token, address_bytes, recipient, value = await handle_erc20(
|
||||||
|
ctx, msg, defs.token_dict
|
||||||
|
)
|
||||||
|
|
||||||
data_total = msg.data_length
|
data_total = msg.data_length
|
||||||
|
|
||||||
@ -100,13 +102,14 @@ async def sign_tx(
|
|||||||
|
|
||||||
|
|
||||||
async def handle_erc20(
|
async def handle_erc20(
|
||||||
ctx: Context, msg: MsgInKeychainChainIdDefs, token_dict: dict[bytes, tokens.TokenInfo]
|
ctx: Context,
|
||||||
) -> tuple[tokens.TokenInfo | None, bytes, bytes, int]:
|
msg: MsgInKeychainChainIdDefs, # type: ignore [TypeVar "MsgInKeychainChainIdDefs" appears only once in generic function signature]
|
||||||
|
token_dict: dict[bytes, EthereumTokenInfo],
|
||||||
|
) -> tuple[EthereumTokenInfo | None, bytes, bytes, int]:
|
||||||
from .layout import require_confirm_unknown_token
|
from .layout import require_confirm_unknown_token
|
||||||
from . import tokens
|
from . import tokens
|
||||||
|
|
||||||
data_initial_chunk = msg.data_initial_chunk # local_cache_attribute
|
data_initial_chunk = msg.data_initial_chunk # local_cache_attribute
|
||||||
|
|
||||||
token = None
|
token = None
|
||||||
address_bytes = recipient = bytes_from_address(msg.to)
|
address_bytes = recipient = bytes_from_address(msg.to)
|
||||||
value = int.from_bytes(msg.value, "big")
|
value = int.from_bytes(msg.value, "big")
|
||||||
@ -185,7 +188,7 @@ def _sign_digest(
|
|||||||
return req
|
return req
|
||||||
|
|
||||||
|
|
||||||
def check_common_fields(msg: MsgInKeychainChainIdDefs) -> None:
|
def check_common_fields(msg: MsgInKeychainChainIdDefs) -> None: # type: ignore [TypeVar "MsgInKeychainChainIdDefs" appears only once in generic function signature]
|
||||||
data_length = msg.data_length # local_cache_attribute
|
data_length = msg.data_length # local_cache_attribute
|
||||||
|
|
||||||
if data_length > 0:
|
if data_length > 0:
|
||||||
|
@ -57,7 +57,9 @@ async def sign_tx_eip1559(
|
|||||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||||
|
|
||||||
# Handle ERC20s
|
# Handle ERC20s
|
||||||
token, address_bytes, recipient, value = await handle_erc20(ctx, msg, defs.token_dict)
|
token, address_bytes, recipient, value = await handle_erc20(
|
||||||
|
ctx, msg, defs.token_dict
|
||||||
|
)
|
||||||
|
|
||||||
data_total = msg.data_length
|
data_total = msg.data_length
|
||||||
|
|
||||||
|
@ -29,7 +29,10 @@ _MAX_VALUE_BYTE_SIZE = const(1024)
|
|||||||
|
|
||||||
@with_keychain_from_path_and_defs(*PATTERNS_ADDRESS)
|
@with_keychain_from_path_and_defs(*PATTERNS_ADDRESS)
|
||||||
async def sign_typed_data(
|
async def sign_typed_data(
|
||||||
ctx: Context, msg: EthereumSignTypedData, keychain: Keychain, defs: definitions.EthereumDefinitions
|
ctx: Context,
|
||||||
|
msg: EthereumSignTypedData,
|
||||||
|
keychain: Keychain,
|
||||||
|
defs: definitions.EthereumDefinitions,
|
||||||
) -> EthereumTypedDataSignature:
|
) -> EthereumTypedDataSignature:
|
||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import secp256k1
|
||||||
from apps.common import paths
|
from apps.common import paths
|
||||||
|
@ -35,6 +35,7 @@ class TokenInfo:
|
|||||||
|
|
||||||
UNKNOWN_TOKEN = TokenInfo("Wei UNKN", 0, b"", 0)
|
UNKNOWN_TOKEN = TokenInfo("Wei UNKN", 0, b"", 0)
|
||||||
|
|
||||||
|
# TODO: delete completely
|
||||||
def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
|
def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
|
||||||
for addr, symbol, decimal in _token_iterator(chain_id):
|
for addr, symbol, decimal in _token_iterator(chain_id):
|
||||||
if address == addr:
|
if address == addr:
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# of it has enough collision-resistance.)
|
# of it has enough collision-resistance.)
|
||||||
# (In the if-tree approach the address length did not have any effect whatsoever.)
|
# (In the if-tree approach the address length did not have any effect whatsoever.)
|
||||||
|
|
||||||
|
from trezor.messages import EthereumTokenInfo
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
|
||||||
<%
|
<%
|
||||||
@ -26,39 +27,38 @@ def group_tokens(tokens):
|
|||||||
return r
|
return r
|
||||||
%>\
|
%>\
|
||||||
|
|
||||||
class TokenInfo:
|
UNKNOWN_TOKEN = EthereumTokenInfo(
|
||||||
def __init__(
|
symbol="Wei UNKN",
|
||||||
self,
|
decimals=0,
|
||||||
symbol: str,
|
address=b"",
|
||||||
decimals: int,
|
chain_id=0,
|
||||||
address: bytes,
|
name="Unknown token",
|
||||||
chain_id: int,
|
)
|
||||||
name: str = None,
|
|
||||||
) -> None:
|
|
||||||
self.symbol = symbol
|
|
||||||
self.decimals = decimals
|
|
||||||
self.address = address
|
|
||||||
self.chain_id = chain_id
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
|
|
||||||
UNKNOWN_TOKEN = TokenInfo("Wei UNKN", 0, b"", 0)
|
|
||||||
|
|
||||||
def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
|
def token_by_chain_address(chain_id: int, address: bytes) -> EthereumTokenInfo:
|
||||||
for addr, symbol, decimal in _token_iterator(chain_id):
|
for addr, symbol, decimal, name in _token_iterator(chain_id):
|
||||||
if address == addr:
|
if address == addr:
|
||||||
return TokenInfo(symbol, decimal)
|
return EthereumTokenInfo(
|
||||||
|
symbol=symbol,
|
||||||
|
decimals=decimal,
|
||||||
|
address=address,
|
||||||
|
chain_id=chain_id,
|
||||||
|
name=name,
|
||||||
|
)
|
||||||
return UNKNOWN_TOKEN
|
return UNKNOWN_TOKEN
|
||||||
|
|
||||||
|
|
||||||
def _token_iterator(chain_id: int) -> Iterator[tuple[bytes, str, int]]:
|
def _token_iterator(chain_id: int) -> Iterator[tuple[bytes, str, int]]:
|
||||||
% for token_chain_id, tokens in group_tokens(supported_on("trezor2", erc20)).items():
|
% for token_chain_id, tokens in group_tokens(supported_on("trezor2", erc20)).items():
|
||||||
if chain_id == ${token_chain_id}:
|
if chain_id == ${token_chain_id}: # ${tokens[0].chain}
|
||||||
% for t in tokens:
|
% for t in tokens:
|
||||||
yield ( # address, symbol, decimals
|
yield ( # address, symbol, decimals, name
|
||||||
${black_repr(t.address_bytes)},
|
${black_repr(t.address_bytes)},
|
||||||
${black_repr(t.symbol)},
|
${black_repr(t.symbol)},
|
||||||
${t.decimals},
|
${t.decimals},
|
||||||
|
${black_repr(t.name.strip())},
|
||||||
)
|
)
|
||||||
% endfor
|
% endfor
|
||||||
% endfor
|
% endfor
|
||||||
|
@ -3,14 +3,14 @@ from trezor.utils import ensure
|
|||||||
from ubinascii import hexlify, unhexlify # noqa: F401
|
from ubinascii import hexlify, unhexlify # noqa: F401
|
||||||
|
|
||||||
from trezor import messages
|
from trezor import messages
|
||||||
from apps.ethereum import networks, tokens
|
from apps.ethereum import tokens
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_FORMAT_VERSION = 1
|
EXPECTED_FORMAT_VERSION = 1
|
||||||
EXPECTED_DATA_VERSION = 1663054984 # unix epoch time
|
EXPECTED_DATA_VERSION = 1663054984 # unix epoch time
|
||||||
|
|
||||||
class InfoWithDefinition():
|
class InfoWithDefinition():
|
||||||
def __init__(self, definition, info):
|
def __init__(self, definition: bytes | None, info: messages.EthereumNetworkInfo | messages.EthereumTokenInfo):
|
||||||
self.definition = definition
|
self.definition = definition
|
||||||
self.info = info
|
self.info = info
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ NETWORKS = {
|
|||||||
# Ethereum
|
# Ethereum
|
||||||
1: InfoWithDefinition(
|
1: InfoWithDefinition(
|
||||||
definition=None, # built-in definitions are not encoded
|
definition=None, # built-in definitions are not encoded
|
||||||
info=networks.NetworkInfo(
|
info=messages.EthereumNetworkInfo(
|
||||||
chain_id=1,
|
chain_id=1,
|
||||||
slip44=60,
|
slip44=60,
|
||||||
shortcut="ETH",
|
shortcut="ETH",
|
||||||
@ -32,7 +32,7 @@ NETWORKS = {
|
|||||||
# Rinkeby
|
# Rinkeby
|
||||||
4: InfoWithDefinition(
|
4: InfoWithDefinition(
|
||||||
definition=unhexlify("74727a643100000000632034880015080410011a047452494e220752696e6b65627928000e8cc47ed4e657d9a9b98e1dd02164320c54a9724e17f91d1d79f6760169582c98ec70ca6f4e94d27e574175c59d2ae04e0cd30b65fb19acd8d2c5fb90bcb7db96f6102e4182c0cef5f412ac3c5fa94f9505b4df2633a0f7bdffa309588d722415624adeb8f329b1572ff9dfc81fbc86e61f1fcb2369f51ba85ea765c908ac254ba996f842a6277583f8d02f149c78bc0eeb8f3d41240403f85785dc3a3925ea768d76aae12342c8a24de223c1ea75e5f07f6b94b8f22189413631eed3c9a362b4501f68b645aa487b9d159a8161404a218507641453ebf045cec56710bb7d873e102777695b56903766e1af16f95576ec4f41874bdaf80cec02ee067d30e721515564d4f30fa74a6c61eb784ea65cc881ead7af2ffac02d5bf1fe1a756918fe37b74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
definition=unhexlify("74727a643100000000632034880015080410011a047452494e220752696e6b65627928000e8cc47ed4e657d9a9b98e1dd02164320c54a9724e17f91d1d79f6760169582c98ec70ca6f4e94d27e574175c59d2ae04e0cd30b65fb19acd8d2c5fb90bcb7db96f6102e4182c0cef5f412ac3c5fa94f9505b4df2633a0f7bdffa309588d722415624adeb8f329b1572ff9dfc81fbc86e61f1fcb2369f51ba85ea765c908ac254ba996f842a6277583f8d02f149c78bc0eeb8f3d41240403f85785dc3a3925ea768d76aae12342c8a24de223c1ea75e5f07f6b94b8f22189413631eed3c9a362b4501f68b645aa487b9d159a8161404a218507641453ebf045cec56710bb7d873e102777695b56903766e1af16f95576ec4f41874bdaf80cec02ee067d30e721515564d4f30fa74a6c61eb784ea65cc881ead7af2ffac02d5bf1fe1a756918fe37b74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
||||||
info=networks.NetworkInfo(
|
info=messages.EthereumNetworkInfo(
|
||||||
chain_id=4,
|
chain_id=4,
|
||||||
slip44=1,
|
slip44=1,
|
||||||
shortcut="tRIN",
|
shortcut="tRIN",
|
||||||
@ -43,7 +43,7 @@ NETWORKS = {
|
|||||||
# Ubiq
|
# Ubiq
|
||||||
8: InfoWithDefinition(
|
8: InfoWithDefinition(
|
||||||
definition=unhexlify("74727a6431000000006320348800110808106c1a0355425122045562697128000e5641d82e3622b4e6addd4354efd933cf15947d1d608a60d324d1156b5a4999f70c41beb85bd866aa3059123447dfeef2e1b6c009b66ac8d04ebbca854ad30049edbbb2fbfda3bfedc6fdb4a76f1db8a4f210bd89d3c3ec1761157b0ec2b13e2f624adeb8f329b1572ff9dfc81fbc86e61f1fcb2369f51ba85ea765c908ac254ba996f842a6277583f8d02f149c78bc0eeb8f3d41240403f85785dc3a3925ea768d76aae12342c8a24de223c1ea75e5f07f6b94b8f22189413631eed3c9a362b4501f68b645aa487b9d159a8161404a218507641453ebf045cec56710bb7d873e102777695b56903766e1af16f95576ec4f41874bdaf80cec02ee067d30e721515564d4f30fa74a6c61eb784ea65cc881ead7af2ffac02d5bf1fe1a756918fe37b74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
definition=unhexlify("74727a6431000000006320348800110808106c1a0355425122045562697128000e5641d82e3622b4e6addd4354efd933cf15947d1d608a60d324d1156b5a4999f70c41beb85bd866aa3059123447dfeef2e1b6c009b66ac8d04ebbca854ad30049edbbb2fbfda3bfedc6fdb4a76f1db8a4f210bd89d3c3ec1761157b0ec2b13e2f624adeb8f329b1572ff9dfc81fbc86e61f1fcb2369f51ba85ea765c908ac254ba996f842a6277583f8d02f149c78bc0eeb8f3d41240403f85785dc3a3925ea768d76aae12342c8a24de223c1ea75e5f07f6b94b8f22189413631eed3c9a362b4501f68b645aa487b9d159a8161404a218507641453ebf045cec56710bb7d873e102777695b56903766e1af16f95576ec4f41874bdaf80cec02ee067d30e721515564d4f30fa74a6c61eb784ea65cc881ead7af2ffac02d5bf1fe1a756918fe37b74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
||||||
info=networks.NetworkInfo(
|
info=messages.EthereumNetworkInfo(
|
||||||
chain_id=8,
|
chain_id=8,
|
||||||
slip44=108,
|
slip44=108,
|
||||||
shortcut="UBQ",
|
shortcut="UBQ",
|
||||||
@ -54,7 +54,7 @@ NETWORKS = {
|
|||||||
# Ethereum Classic
|
# Ethereum Classic
|
||||||
61: InfoWithDefinition(
|
61: InfoWithDefinition(
|
||||||
definition=unhexlify("74727a64310000000063203488001d083d103d1a034554432210457468657265756d20436c617373696328000e6b891a57fe4c38c54b475f22f0d9242dd8ddab0b4f360bd86e37e2e8b79de5ef29237436351f7bc924cd110716b5adde7c28c03d76ac83b091dbce1b5d7d0edbddb221bd894806f7ea1b195443176e06830a83c0204e33f19c51d2fccc3a9f80ac2cca38822db998ddf76778dada240d39b3c6193c6335d7c693dea90d19a41f86855375c2f48c18cdc012ccac771aa316d776c8721c2b1f6d5980808337dfdae13b5be07e3cbc3526119b88c5eb44be0b1dab1094a5ec5215b47daf91736d16501f68b645aa487b9d159a8161404a218507641453ebf045cec56710bb7d873e102777695b56903766e1af16f95576ec4f41874bdaf80cec02ee067d30e721515564d4f30fa74a6c61eb784ea65cc881ead7af2ffac02d5bf1fe1a756918fe37b74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
definition=unhexlify("74727a64310000000063203488001d083d103d1a034554432210457468657265756d20436c617373696328000e6b891a57fe4c38c54b475f22f0d9242dd8ddab0b4f360bd86e37e2e8b79de5ef29237436351f7bc924cd110716b5adde7c28c03d76ac83b091dbce1b5d7d0edbddb221bd894806f7ea1b195443176e06830a83c0204e33f19c51d2fccc3a9f80ac2cca38822db998ddf76778dada240d39b3c6193c6335d7c693dea90d19a41f86855375c2f48c18cdc012ccac771aa316d776c8721c2b1f6d5980808337dfdae13b5be07e3cbc3526119b88c5eb44be0b1dab1094a5ec5215b47daf91736d16501f68b645aa487b9d159a8161404a218507641453ebf045cec56710bb7d873e102777695b56903766e1af16f95576ec4f41874bdaf80cec02ee067d30e721515564d4f30fa74a6c61eb784ea65cc881ead7af2ffac02d5bf1fe1a756918fe37b74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
||||||
info=networks.NetworkInfo(
|
info=messages.EthereumNetworkInfo(
|
||||||
chain_id=61,
|
chain_id=61,
|
||||||
slip44=61,
|
slip44=61,
|
||||||
shortcut="ETC",
|
shortcut="ETC",
|
||||||
@ -80,21 +80,23 @@ TOKENS = {
|
|||||||
# AAVE
|
# AAVE
|
||||||
"7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9": InfoWithDefinition(
|
"7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9": InfoWithDefinition(
|
||||||
definition=None, # built-in definitions are not encoded
|
definition=None, # built-in definitions are not encoded
|
||||||
info=tokens.TokenInfo(
|
info=messages.EthereumTokenInfo(
|
||||||
symbol="AAVE",
|
symbol="AAVE",
|
||||||
decimals=18,
|
decimals=18,
|
||||||
address=unhexlify("7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9"),
|
address=unhexlify("7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9"),
|
||||||
chain_id=1,
|
chain_id=1,
|
||||||
|
name="Aave",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
# TrueAUD
|
# TrueAUD
|
||||||
"00006100f7090010005f1bd7ae6122c3c2cf0090": InfoWithDefinition(
|
"00006100f7090010005f1bd7ae6122c3c2cf0090": InfoWithDefinition(
|
||||||
definition=unhexlify("74727a6431000000016320348800290a045441554410121a1400006100f7090010005f1bd7ae6122c3c2cf009020012a07547275654155440e310dad13f7d3012903a9a457134c9f38c62c04370cb92c7a528838e30a032dffbceeaa2aa849e590c4e6dbc69b0ea5359f3527b95b56ab59a33dc584105b35ea7c06afc296cc1c1e58cc3d6b461631c4c770b9409837ab3d29bc1b666fb9cf5245c4c218b0e9521c185d102f596905ba860e6f56a0a8b394f943855c74eea6fcac87210a9988ac02803f4cc61cf78e7e2409175a75f4f3a82eb84b1f2d1ea8177d5dccd62949d80d7942105e22a452be01859fe816736e803b120fb9bcc0c1117180dbda19e1ad1aafb9b9f1555c75275820bf7c1e568bcb265bdc4dfdae0511782026e11a151f6894d11128327c8c42958c9ae900af970fec13a11ffdeba6ac10733ca55a906142e0b9130312e8e85606108612581aca9087c452f38f14185db74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
definition=unhexlify("74727a6431000000016320348800290a045441554410121a1400006100f7090010005f1bd7ae6122c3c2cf009020012a07547275654155440e310dad13f7d3012903a9a457134c9f38c62c04370cb92c7a528838e30a032dffbceeaa2aa849e590c4e6dbc69b0ea5359f3527b95b56ab59a33dc584105b35ea7c06afc296cc1c1e58cc3d6b461631c4c770b9409837ab3d29bc1b666fb9cf5245c4c218b0e9521c185d102f596905ba860e6f56a0a8b394f943855c74eea6fcac87210a9988ac02803f4cc61cf78e7e2409175a75f4f3a82eb84b1f2d1ea8177d5dccd62949d80d7942105e22a452be01859fe816736e803b120fb9bcc0c1117180dbda19e1ad1aafb9b9f1555c75275820bf7c1e568bcb265bdc4dfdae0511782026e11a151f6894d11128327c8c42958c9ae900af970fec13a11ffdeba6ac10733ca55a906142e0b9130312e8e85606108612581aca9087c452f38f14185db74828a24b640025cd79443ada60063e3034444fc49ed6055dbba6a09fa4484c42cb85abb49103dc8c781c8f190c4632e2dec30081770448021313955dbb49e8a02fd49b34d030280452fe0a5c3bcba4958bc287c67e12519be4f4aec7ab0c8e574e53a663f635f75508f23d92c77b2147f29feb79c38d0f793fba295aae605c7e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
||||||
info=tokens.TokenInfo(
|
info=messages.EthereumTokenInfo(
|
||||||
symbol="TAUD",
|
symbol="TAUD",
|
||||||
decimals=18,
|
decimals=18,
|
||||||
address=unhexlify("00006100f7090010005f1bd7ae6122c3c2cf0090"),
|
address=unhexlify("00006100f7090010005f1bd7ae6122c3c2cf0090"),
|
||||||
chain_id=1,
|
chain_id=1,
|
||||||
|
name="TrueAUD",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -102,44 +104,20 @@ TOKENS = {
|
|||||||
# Karma Token
|
# Karma Token
|
||||||
"275a5b346599b56917e7b1c9de019dcf9ead861a": InfoWithDefinition(
|
"275a5b346599b56917e7b1c9de019dcf9ead861a": InfoWithDefinition(
|
||||||
definition=unhexlify("74727a64310000000163203488002b0a024b4310121a14275a5b346599b56917e7b1c9de019dcf9ead861a20042a0b4b61726d6120546f6b656e0e2b3cb176ff5a2cf431620c1a7eee9aa297f5de36d29ae6d423166cf7391e41c5826c57f30b11421a4bf10f336f12050f6d959e02bfb17a8ce7ae15087d4f083124c0cebed2ce45b15b2608b1a8f0ee443e8c4f33111d880a6a3c09a77c627f82d68b62a1bd39975b2a2c86f196b9a3dcb62bdc3554fbf85b75331bc0d39f23a46f5ed91f208757d1136bb20b3618294fbfb0a826e9c09e392fe8109181bc6c28cad78db1987947f461bfc1042b88a91d6d61297d0cf194dfeea981b4515c2ed09dc2966671f5c715c64ceb25e53e1df3c7234e3e0ddf0dcd54d40fde0c51903685f9dc7fa69c71184f17af852e74490ea7286e89a0aa4770629664f7dd8eab8c4e009ff4c24682f85f7e01d4e10ae5c06212d5a4f43bac2b4f0e79383666ef12054ddbf757809aa6b446d65f7fd1bdd76fb1d7770398bd17af50635027e680801d244bd7b4f14c57edc3cd961722315e076120bf1d35db8520edb812bfbb5bab8ff57cc2dc1b3d1f9d95b33dba5d759aef1123f2ef346b6328973fba204fd745e644c8e492f9a76c0019b2cf21715fba682b46b9c58013e0b0927e5272c808a67e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
definition=unhexlify("74727a64310000000163203488002b0a024b4310121a14275a5b346599b56917e7b1c9de019dcf9ead861a20042a0b4b61726d6120546f6b656e0e2b3cb176ff5a2cf431620c1a7eee9aa297f5de36d29ae6d423166cf7391e41c5826c57f30b11421a4bf10f336f12050f6d959e02bfb17a8ce7ae15087d4f083124c0cebed2ce45b15b2608b1a8f0ee443e8c4f33111d880a6a3c09a77c627f82d68b62a1bd39975b2a2c86f196b9a3dcb62bdc3554fbf85b75331bc0d39f23a46f5ed91f208757d1136bb20b3618294fbfb0a826e9c09e392fe8109181bc6c28cad78db1987947f461bfc1042b88a91d6d61297d0cf194dfeea981b4515c2ed09dc2966671f5c715c64ceb25e53e1df3c7234e3e0ddf0dcd54d40fde0c51903685f9dc7fa69c71184f17af852e74490ea7286e89a0aa4770629664f7dd8eab8c4e009ff4c24682f85f7e01d4e10ae5c06212d5a4f43bac2b4f0e79383666ef12054ddbf757809aa6b446d65f7fd1bdd76fb1d7770398bd17af50635027e680801d244bd7b4f14c57edc3cd961722315e076120bf1d35db8520edb812bfbb5bab8ff57cc2dc1b3d1f9d95b33dba5d759aef1123f2ef346b6328973fba204fd745e644c8e492f9a76c0019b2cf21715fba682b46b9c58013e0b0927e5272c808a67e8226523edefc6ad1eefe088e5b8376028bf90116ece4fb876510b4ae1c89686dbcaacbbac8225baba429ca376fafac50f4bd1ff4ce1c61dd53318d0718bf513ea6f770cce81e07a653622e4dbd03bdaa570bfe43219eb0d4fab725c9a8da04"),
|
||||||
info=tokens.TokenInfo(
|
info=messages.EthereumTokenInfo(
|
||||||
symbol="KC",
|
symbol="KC",
|
||||||
decimals=18,
|
decimals=18,
|
||||||
address=unhexlify("275a5b346599b56917e7b1c9de019dcf9ead861a"),
|
address=unhexlify("275a5b346599b56917e7b1c9de019dcf9ead861a"),
|
||||||
chain_id=4,
|
chain_id=4,
|
||||||
|
name="Karma Token",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def equalNetworkInfo(n1: networks.NetworkInfo, n2: networks.NetworkInfo, msg: str = '') -> bool:
|
def construct_network_info(chain_id: int = 0, slip44: int = 0, shortcut: str = "", name: str = "", rskip60: bool = False) -> messages.EthereumNetworkInfo:
|
||||||
ensure(
|
return messages.EthereumNetworkInfo(
|
||||||
cond=(
|
|
||||||
n1.chain_id == n2.chain_id
|
|
||||||
and n1.slip44 == n2.slip44
|
|
||||||
and n1.shortcut == n2.shortcut
|
|
||||||
and n1.name == n2.name
|
|
||||||
and n1.rskip60 == n2.rskip60
|
|
||||||
),
|
|
||||||
msg=msg,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def equalTokenInfo(t1: tokens.TokenInfo, t2: tokens.TokenInfo, msg: str = '') -> bool:
|
|
||||||
ensure(
|
|
||||||
cond=(
|
|
||||||
t1.symbol == t2.symbol
|
|
||||||
and t1.decimals == t2.decimals
|
|
||||||
and t1.address == t2.address
|
|
||||||
and t1.chain_id == t2.chain_id
|
|
||||||
),
|
|
||||||
msg=msg,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def construct_network_info(chain_id: int = 0, slip44: int = 0, shortcut: str = "", name: str = "", rskip60: bool = False) -> networks.NetworkInfo:
|
|
||||||
return networks.NetworkInfo(
|
|
||||||
chain_id=chain_id,
|
chain_id=chain_id,
|
||||||
slip44=slip44,
|
slip44=slip44,
|
||||||
shortcut=shortcut,
|
shortcut=shortcut,
|
||||||
@ -154,8 +132,8 @@ def construct_token_info(
|
|||||||
address: bytes = b'',
|
address: bytes = b'',
|
||||||
chain_id: int = 0,
|
chain_id: int = 0,
|
||||||
name: str = "",
|
name: str = "",
|
||||||
) -> tokens.TokenInfo:
|
) -> messages.EthereumTokenInfo:
|
||||||
return tokens.TokenInfo(
|
return messages.EthereumTokenInfo(
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
decimals=decimals,
|
decimals=decimals,
|
||||||
address=address,
|
address=address,
|
||||||
@ -202,14 +180,14 @@ def get_ethereum_encoded_definition(chain_id: int | None = None, slip44: int | N
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def builtin_networks_iterator() -> Iterator[networks.NetworkInfo]:
|
def builtin_networks_iterator() -> Iterator[messages.EthereumNetworkInfo]:
|
||||||
"""Mockup function replaces original function from core/src/apps/ethereum/networks.py used to get built-in network definitions."""
|
"""Mockup function replaces original function from core/src/apps/ethereum/networks.py used to get built-in network definitions."""
|
||||||
for _, network in NETWORKS.items():
|
for _, network in NETWORKS.items():
|
||||||
if network.definition is None:
|
if network.definition is None:
|
||||||
yield network.info
|
yield network.info
|
||||||
|
|
||||||
|
|
||||||
def builtin_token_by_chain_address(chain_id: int, address: bytes) -> tokens.TokenInfo:
|
def builtin_token_by_chain_address(chain_id: int, address: bytes) -> messages.EthereumTokenInfo:
|
||||||
"""Mockup function replaces original function from core/src/apps/ethereum/tokens.py used to get built-in token definitions."""
|
"""Mockup function replaces original function from core/src/apps/ethereum/tokens.py used to get built-in token definitions."""
|
||||||
address_str = hexlify(address).decode('hex')
|
address_str = hexlify(address).decode('hex')
|
||||||
try:
|
try:
|
||||||
|
@ -5,11 +5,14 @@ from ubinascii import hexlify # noqa: F401
|
|||||||
if not utils.BITCOIN_ONLY:
|
if not utils.BITCOIN_ONLY:
|
||||||
import apps.ethereum.definitions as dfs
|
import apps.ethereum.definitions as dfs
|
||||||
|
|
||||||
|
from apps.ethereum import networks
|
||||||
from ethereum_common import *
|
from ethereum_common import *
|
||||||
from trezor import protobuf
|
from trezor import protobuf
|
||||||
from trezor.enums import EthereumDefinitionType
|
from trezor.enums import EthereumDefinitionType
|
||||||
from trezor.messages import (
|
from trezor.messages import (
|
||||||
EthereumEncodedDefinitions,
|
EthereumEncodedDefinitions,
|
||||||
|
EthereumNetworkInfo,
|
||||||
|
EthereumTokenInfo,
|
||||||
EthereumGetAddress,
|
EthereumGetAddress,
|
||||||
EthereumGetPublicKey,
|
EthereumGetPublicKey,
|
||||||
EthereumSignMessage,
|
EthereumSignMessage,
|
||||||
@ -59,10 +62,6 @@ class TestEthereumDefinitionParser(unittest.TestCase):
|
|||||||
|
|
||||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||||
class TestDecodeDefinition(unittest.TestCase):
|
class TestDecodeDefinition(unittest.TestCase):
|
||||||
def setUp(self):
|
|
||||||
self.addTypeEqualityFunc(networks.NetworkInfo, equalNetworkInfo)
|
|
||||||
self.addTypeEqualityFunc(tokens.TokenInfo, equalTokenInfo)
|
|
||||||
|
|
||||||
# successful decode network
|
# successful decode network
|
||||||
def test_network_definition(self):
|
def test_network_definition(self):
|
||||||
rinkeby_network = get_ethereum_network_info_with_definition(chain_id=4)
|
rinkeby_network = get_ethereum_network_info_with_definition(chain_id=4)
|
||||||
@ -112,7 +111,6 @@ class TestDecodeDefinition(unittest.TestCase):
|
|||||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||||
class TestGetNetworkDefiniton(unittest.TestCase):
|
class TestGetNetworkDefiniton(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.addTypeEqualityFunc(networks.NetworkInfo, equalNetworkInfo)
|
|
||||||
# use mockup function for built-in networks
|
# use mockup function for built-in networks
|
||||||
networks._networks_iterator = builtin_networks_iterator
|
networks._networks_iterator = builtin_networks_iterator
|
||||||
|
|
||||||
@ -153,7 +151,6 @@ class TestGetNetworkDefiniton(unittest.TestCase):
|
|||||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||||
class TestGetTokenDefiniton(unittest.TestCase):
|
class TestGetTokenDefiniton(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.addTypeEqualityFunc(tokens.TokenInfo, equalTokenInfo)
|
|
||||||
# use mockup function for built-in tokens
|
# use mockup function for built-in tokens
|
||||||
tokens.token_by_chain_address = builtin_token_by_chain_address
|
tokens.token_by_chain_address = builtin_token_by_chain_address
|
||||||
|
|
||||||
@ -200,8 +197,6 @@ class TestGetTokenDefiniton(unittest.TestCase):
|
|||||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||||
class TestEthereumDefinitions(unittest.TestCase):
|
class TestEthereumDefinitions(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.addTypeEqualityFunc(networks.NetworkInfo, equalNetworkInfo)
|
|
||||||
self.addTypeEqualityFunc(tokens.TokenInfo, equalTokenInfo)
|
|
||||||
# use mockup functions for built-in definitions
|
# use mockup functions for built-in definitions
|
||||||
networks._networks_iterator = builtin_networks_iterator
|
networks._networks_iterator = builtin_networks_iterator
|
||||||
tokens.token_by_chain_address = builtin_token_by_chain_address
|
tokens.token_by_chain_address = builtin_token_by_chain_address
|
||||||
@ -212,8 +207,8 @@ class TestEthereumDefinitions(unittest.TestCase):
|
|||||||
token_definition: bytes | None,
|
token_definition: bytes | None,
|
||||||
ref_chain_id: int | None,
|
ref_chain_id: int | None,
|
||||||
ref_token_address: bytes | None,
|
ref_token_address: bytes | None,
|
||||||
network_info: networks.NetworkInfo | None,
|
network_info: EthereumNetworkInfo | None,
|
||||||
token_info: tokens.TokenInfo | None,
|
token_info: EthereumTokenInfo | None,
|
||||||
):
|
):
|
||||||
# get
|
# get
|
||||||
definitions = dfs.EthereumDefinitions(network_definition, token_definition, ref_chain_id, ref_token_address)
|
definitions = dfs.EthereumDefinitions(network_definition, token_definition, ref_chain_id, ref_token_address)
|
||||||
@ -267,8 +262,6 @@ class TestEthereumDefinitions(unittest.TestCase):
|
|||||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||||
class TestGetDefinitonsFromMsg(unittest.TestCase):
|
class TestGetDefinitonsFromMsg(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.addTypeEqualityFunc(networks.NetworkInfo, equalNetworkInfo)
|
|
||||||
self.addTypeEqualityFunc(tokens.TokenInfo, equalTokenInfo)
|
|
||||||
# use mockup functions for built-in definitions
|
# use mockup functions for built-in definitions
|
||||||
networks._networks_iterator = builtin_networks_iterator
|
networks._networks_iterator = builtin_networks_iterator
|
||||||
tokens.token_by_chain_address = builtin_token_by_chain_address
|
tokens.token_by_chain_address = builtin_token_by_chain_address
|
||||||
@ -276,8 +269,8 @@ class TestGetDefinitonsFromMsg(unittest.TestCase):
|
|||||||
def get_and_compare_ethereum_definitions(
|
def get_and_compare_ethereum_definitions(
|
||||||
self,
|
self,
|
||||||
msg: protobuf.MessageType,
|
msg: protobuf.MessageType,
|
||||||
network_info: networks.NetworkInfo | None,
|
network_info: EthereumNetworkInfo | None,
|
||||||
token_info: tokens.TokenInfo | None,
|
token_info: EthereumTokenInfo | None,
|
||||||
):
|
):
|
||||||
# get
|
# get
|
||||||
definitions = dfs.get_definitions_from_msg(msg)
|
definitions = dfs.get_definitions_from_msg(msg)
|
||||||
|
@ -3,7 +3,7 @@ from apps.common.paths import HARDENED
|
|||||||
|
|
||||||
if not utils.BITCOIN_ONLY:
|
if not utils.BITCOIN_ONLY:
|
||||||
from apps.ethereum.helpers import address_from_bytes
|
from apps.ethereum.helpers import address_from_bytes
|
||||||
from apps.ethereum.networks import NetworkInfo
|
from trezor.messages import EthereumNetworkInfo
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||||
@ -40,7 +40,7 @@ class TestEthereumGetAddress(unittest.TestCase):
|
|||||||
'0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB',
|
'0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB',
|
||||||
'0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB'
|
'0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB'
|
||||||
]
|
]
|
||||||
n = NetworkInfo(chain_id=30, slip44=1, shortcut='T', name='T', rskip60=True)
|
n = EthereumNetworkInfo(chain_id=30, slip44=1, shortcut='T', name='T', rskip60=True)
|
||||||
for s in rskip60_chain_30:
|
for s in rskip60_chain_30:
|
||||||
b = unhexlify(s[2:])
|
b = unhexlify(s[2:])
|
||||||
h = address_from_bytes(b, n)
|
h = address_from_bytes(b, n)
|
||||||
|
@ -19,14 +19,15 @@ import pathlib
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import tarfile
|
import tarfile
|
||||||
from io import BytesIO
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from io import BytesIO
|
||||||
from typing import (
|
from typing import (
|
||||||
|
NoReturn,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
|
BinaryIO,
|
||||||
Dict,
|
Dict,
|
||||||
List,
|
List,
|
||||||
NoReturn,
|
|
||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
Sequence,
|
||||||
TextIO,
|
TextIO,
|
||||||
@ -168,30 +169,37 @@ def _format_access_list(
|
|||||||
|
|
||||||
|
|
||||||
def _get_ethereum_definitions(
|
def _get_ethereum_definitions(
|
||||||
definitions_dir: pathlib.Path = None,
|
definitions_dir: pathlib.Path = None,
|
||||||
network_def_file: TextIO = None,
|
network_def_file: BinaryIO = None,
|
||||||
token_def_file: TextIO = None,
|
token_def_file: BinaryIO = None,
|
||||||
download_definitions: bool = False,
|
download_definitions: bool = False,
|
||||||
chain_id: Optional[int] = None,
|
chain_id: Optional[int] = None,
|
||||||
slip44_hardened: Optional[int] = None,
|
slip44_hardened: Optional[int] = None,
|
||||||
token_address: Optional[str] = None,
|
token_address: Optional[str] = None,
|
||||||
) -> ethereum.messages.EthereumEncodedDefinitions:
|
) -> ethereum.messages.EthereumEncodedDefinitions:
|
||||||
count_of_options_used = sum(
|
count_of_options_used = sum(
|
||||||
bool(o) for o in (
|
bool(o)
|
||||||
|
for o in (
|
||||||
definitions_dir,
|
definitions_dir,
|
||||||
(network_def_file or token_def_file),
|
(network_def_file or token_def_file),
|
||||||
download_definitions
|
download_definitions,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if count_of_options_used > 1:
|
if count_of_options_used > 1:
|
||||||
raise click.ClickException("More than one mutually exclusive option for definitions was used. See --help for more info.")
|
raise click.ClickException(
|
||||||
|
"More than one mutually exclusive option for definitions was used. See --help for more info."
|
||||||
|
)
|
||||||
|
|
||||||
defs = ethereum.messages.EthereumEncodedDefinitions()
|
defs = ethereum.messages.EthereumEncodedDefinitions()
|
||||||
if definitions_dir is not None:
|
if definitions_dir is not None:
|
||||||
if chain_id is not None or slip44_hardened is not None:
|
if chain_id is not None or slip44_hardened is not None:
|
||||||
defs.encoded_network = ethereum.network_definition_from_dir(definitions_dir, chain_id, slip44_hardened)
|
defs.encoded_network = ethereum.network_definition_from_dir(
|
||||||
|
definitions_dir, chain_id, slip44_hardened
|
||||||
|
)
|
||||||
if chain_id is not None and token_address is not None:
|
if chain_id is not None and token_address is not None:
|
||||||
defs.encoded_token = ethereum.token_definition_from_dir(definitions_dir, chain_id, token_address)
|
defs.encoded_token = ethereum.token_definition_from_dir(
|
||||||
|
definitions_dir, chain_id, token_address
|
||||||
|
)
|
||||||
elif network_def_file is not None or token_def_file is not None:
|
elif network_def_file is not None or token_def_file is not None:
|
||||||
if network_def_file is not None:
|
if network_def_file is not None:
|
||||||
with network_def_file:
|
with network_def_file:
|
||||||
@ -201,9 +209,13 @@ def _get_ethereum_definitions(
|
|||||||
defs.encoded_token = token_def_file.read()
|
defs.encoded_token = token_def_file.read()
|
||||||
elif download_definitions:
|
elif download_definitions:
|
||||||
if chain_id is not None or slip44_hardened is not None:
|
if chain_id is not None or slip44_hardened is not None:
|
||||||
defs.encoded_network = ethereum.download_network_definition(chain_id, slip44_hardened)
|
defs.encoded_network = ethereum.download_network_definition(
|
||||||
|
chain_id, slip44_hardened
|
||||||
|
)
|
||||||
if chain_id is not None and token_address is not None:
|
if chain_id is not None and token_address is not None:
|
||||||
defs.encoded_token = ethereum.download_token_definition(chain_id, token_address)
|
defs.encoded_token = ethereum.download_token_definition(
|
||||||
|
chain_id, token_address
|
||||||
|
)
|
||||||
|
|
||||||
return defs
|
return defs
|
||||||
|
|
||||||
@ -215,26 +227,28 @@ def _get_ethereum_definitions(
|
|||||||
|
|
||||||
definitions_dir_option = click.option(
|
definitions_dir_option = click.option(
|
||||||
"--definitions-dir",
|
"--definitions-dir",
|
||||||
type=click.Path(exists=True, file_okay=False, resolve_path=True, path_type=pathlib.Path),
|
type=click.Path(
|
||||||
help="Directory with stored definitions. Directory structure should be the same as it is in downloaded archive from " \
|
exists=True, file_okay=False, resolve_path=True, path_type=pathlib.Path
|
||||||
"`https:\\data.trezor.io\definitions\???`. Mutually exclusive with `--network-def`, `--token-def` and " \
|
),
|
||||||
"`--download-definitions`.", # TODO: add link?, replace this ur with function used to download defs
|
help="Directory with stored definitions. Directory structure should be the same as it is in downloaded archive from "
|
||||||
|
r"`https:\\data.trezor.io\definitions\???`. Mutually exclusive with `--network-def`, `--token-def` and "
|
||||||
|
"`--download-definitions`.", # TODO: add link?, replace this ur with function used to download defs
|
||||||
)
|
)
|
||||||
network_def_option = click.option(
|
network_def_option = click.option(
|
||||||
"--network-def",
|
"--network-def",
|
||||||
type=click.File(mode="rb"),
|
type=click.File(mode="rb"),
|
||||||
help="Binary file with network definition. Mutually exclusive with `--definitions-dir` and `--download-definitions`."
|
help="Binary file with network definition. Mutually exclusive with `--definitions-dir` and `--download-definitions`.",
|
||||||
)
|
)
|
||||||
token_def_options = click.option(
|
token_def_options = click.option(
|
||||||
"--token-def",
|
"--token-def",
|
||||||
type=click.File(mode="rb"),
|
type=click.File(mode="rb"),
|
||||||
help="Binary file with token definition. Mutually exclusive with `--definitions-dir` and `--download-definitions`."
|
help="Binary file with token definition. Mutually exclusive with `--definitions-dir` and `--download-definitions`.",
|
||||||
)
|
)
|
||||||
download_definitions_option = click.option(
|
download_definitions_option = click.option(
|
||||||
"--download-definitions",
|
"--download-definitions",
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Automatically download required definitions from `data.trezor.io\definitions` and use them. " \
|
help=r"Automatically download required definitions from `data.trezor.io\definitions` and use them. "
|
||||||
"Mutually exclusive with `--definitions-dir`, `--network-def` and `--token-def`."
|
"Mutually exclusive with `--definitions-dir`, `--network-def` and `--token-def`.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -244,14 +258,21 @@ def cli() -> None:
|
|||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option("-o", "--outdir", type=click.Path(resolve_path=True, file_okay=False, path_type=pathlib.Path), default="./definitions-latest")
|
@click.option(
|
||||||
|
"-o",
|
||||||
|
"--outdir",
|
||||||
|
type=click.Path(resolve_path=True, file_okay=False, path_type=pathlib.Path),
|
||||||
|
default="./definitions-latest",
|
||||||
|
)
|
||||||
@click.option("-u", "--unpack", is_flag=True)
|
@click.option("-u", "--unpack", is_flag=True)
|
||||||
def download_definitions(outdir: pathlib.Path, unpack: bool) -> str:
|
def download_definitions(outdir: pathlib.Path, unpack: bool) -> None:
|
||||||
"""Download all Ethereum network and token definitions and save them."""
|
"""Download all Ethereum network and token definitions and save them."""
|
||||||
archive_filename = "definitions.tar.gz"
|
archive_filename = "definitions.tar.gz"
|
||||||
|
|
||||||
# TODO: change once we know the urls
|
# TODO: change once we know the urls
|
||||||
archived_definitions = ethereum.download_from_url("https://data.trezor.io/eth_definitions/" + archive_filename)
|
archived_definitions = ethereum.download_from_url(
|
||||||
|
"https://data.trezor.io/eth_definitions/" + archive_filename
|
||||||
|
)
|
||||||
|
|
||||||
# unpack and/or save
|
# unpack and/or save
|
||||||
if unpack:
|
if unpack:
|
||||||
@ -271,7 +292,14 @@ def download_definitions(outdir: pathlib.Path, unpack: bool) -> str:
|
|||||||
@network_def_option
|
@network_def_option
|
||||||
@download_definitions_option
|
@download_definitions_option
|
||||||
@with_client
|
@with_client
|
||||||
def get_address(client: "TrezorClient", address: str, show_display: bool, definitions_dir: pathlib.Path, network_def: TextIO, download_definitions: bool) -> str:
|
def get_address(
|
||||||
|
client: "TrezorClient",
|
||||||
|
address: str,
|
||||||
|
show_display: bool,
|
||||||
|
definitions_dir: pathlib.Path,
|
||||||
|
network_def: BinaryIO,
|
||||||
|
download_definitions: bool,
|
||||||
|
) -> str:
|
||||||
"""Get Ethereum address in hex encoding."""
|
"""Get Ethereum address in hex encoding."""
|
||||||
address_n = tools.parse_path(address)
|
address_n = tools.parse_path(address)
|
||||||
defs = _get_ethereum_definitions(
|
defs = _get_ethereum_definitions(
|
||||||
@ -290,7 +318,14 @@ def get_address(client: "TrezorClient", address: str, show_display: bool, defini
|
|||||||
@network_def_option
|
@network_def_option
|
||||||
@download_definitions_option
|
@download_definitions_option
|
||||||
@with_client
|
@with_client
|
||||||
def get_public_node(client: "TrezorClient", address: str, show_display: bool, definitions_dir: pathlib.Path, network_def: TextIO, download_definitions: bool) -> dict:
|
def get_public_node(
|
||||||
|
client: "TrezorClient",
|
||||||
|
address: str,
|
||||||
|
show_display: bool,
|
||||||
|
definitions_dir: pathlib.Path,
|
||||||
|
network_def: BinaryIO,
|
||||||
|
download_definitions: bool,
|
||||||
|
) -> dict:
|
||||||
"""Get Ethereum public node of given path."""
|
"""Get Ethereum public node of given path."""
|
||||||
address_n = tools.parse_path(address)
|
address_n = tools.parse_path(address)
|
||||||
defs = _get_ethereum_definitions(
|
defs = _get_ethereum_definitions(
|
||||||
@ -299,7 +334,12 @@ def get_public_node(client: "TrezorClient", address: str, show_display: bool, de
|
|||||||
download_definitions=download_definitions,
|
download_definitions=download_definitions,
|
||||||
slip44_hardened=address_n[1],
|
slip44_hardened=address_n[1],
|
||||||
)
|
)
|
||||||
result = ethereum.get_public_node(client, address_n, show_display=show_display, encoded_network=defs.encoded_network)
|
result = ethereum.get_public_node(
|
||||||
|
client,
|
||||||
|
address_n,
|
||||||
|
show_display=show_display,
|
||||||
|
encoded_network=defs.encoded_network,
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"node": {
|
"node": {
|
||||||
"depth": result.node.depth,
|
"depth": result.node.depth,
|
||||||
@ -380,8 +420,8 @@ def sign_tx(
|
|||||||
access_list: List[ethereum.messages.EthereumAccessList],
|
access_list: List[ethereum.messages.EthereumAccessList],
|
||||||
eip2718_type: Optional[int],
|
eip2718_type: Optional[int],
|
||||||
definitions_dir: pathlib.Path,
|
definitions_dir: pathlib.Path,
|
||||||
network_def: TextIO,
|
network_def: BinaryIO,
|
||||||
token_def: TextIO,
|
token_def: BinaryIO,
|
||||||
download_definitions: bool,
|
download_definitions: bool,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Sign (and optionally publish) Ethereum transaction.
|
"""Sign (and optionally publish) Ethereum transaction.
|
||||||
@ -457,7 +497,7 @@ def sign_tx(
|
|||||||
token_def_file=token_def,
|
token_def_file=token_def,
|
||||||
download_definitions=download_definitions,
|
download_definitions=download_definitions,
|
||||||
chain_id=chain_id,
|
chain_id=chain_id,
|
||||||
token_address=to_address
|
token_address=to_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_eip1559:
|
if is_eip1559:
|
||||||
@ -547,7 +587,14 @@ def sign_tx(
|
|||||||
@network_def_option
|
@network_def_option
|
||||||
@download_definitions_option
|
@download_definitions_option
|
||||||
@with_client
|
@with_client
|
||||||
def sign_message(client: "TrezorClient", address: str, message: str, definitions_dir: pathlib.Path, network_def: TextIO, download_definitions: bool) -> Dict[str, str]:
|
def sign_message(
|
||||||
|
client: "TrezorClient",
|
||||||
|
address: str,
|
||||||
|
message: str,
|
||||||
|
definitions_dir: pathlib.Path,
|
||||||
|
network_def: BinaryIO,
|
||||||
|
download_definitions: bool,
|
||||||
|
) -> Dict[str, str]:
|
||||||
"""Sign message with Ethereum address."""
|
"""Sign message with Ethereum address."""
|
||||||
address_n = tools.parse_path(address)
|
address_n = tools.parse_path(address)
|
||||||
defs = _get_ethereum_definitions(
|
defs = _get_ethereum_definitions(
|
||||||
@ -578,7 +625,13 @@ def sign_message(client: "TrezorClient", address: str, message: str, definitions
|
|||||||
@download_definitions_option
|
@download_definitions_option
|
||||||
@with_client
|
@with_client
|
||||||
def sign_typed_data(
|
def sign_typed_data(
|
||||||
client: "TrezorClient", address: str, metamask_v4_compat: bool, file: TextIO, definitions_dir: pathlib.Path, network_def: TextIO, download_definitions: bool
|
client: "TrezorClient",
|
||||||
|
address: str,
|
||||||
|
metamask_v4_compat: bool,
|
||||||
|
file: TextIO,
|
||||||
|
definitions_dir: pathlib.Path,
|
||||||
|
network_def: BinaryIO,
|
||||||
|
download_definitions: bool,
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
"""Sign typed data (EIP-712) with Ethereum address.
|
"""Sign typed data (EIP-712) with Ethereum address.
|
||||||
|
|
||||||
@ -595,7 +648,11 @@ def sign_typed_data(
|
|||||||
slip44_hardened=address_n[1],
|
slip44_hardened=address_n[1],
|
||||||
)
|
)
|
||||||
ret = ethereum.sign_typed_data(
|
ret = ethereum.sign_typed_data(
|
||||||
client, address_n, data, metamask_v4_compat=metamask_v4_compat, encoded_network=defs.encoded_network
|
client,
|
||||||
|
address_n,
|
||||||
|
data,
|
||||||
|
metamask_v4_compat=metamask_v4_compat,
|
||||||
|
encoded_network=defs.encoded_network,
|
||||||
)
|
)
|
||||||
output = {
|
output = {
|
||||||
"address": ret.address,
|
"address": ret.address,
|
||||||
@ -613,7 +670,13 @@ def sign_typed_data(
|
|||||||
@download_definitions_option
|
@download_definitions_option
|
||||||
@with_client
|
@with_client
|
||||||
def verify_message(
|
def verify_message(
|
||||||
client: "TrezorClient", address: str, signature: str, message: str, definitions_dir: pathlib.Path, network_def: TextIO, download_definitions: bool
|
client: "TrezorClient",
|
||||||
|
address: str,
|
||||||
|
signature: str,
|
||||||
|
message: str,
|
||||||
|
definitions_dir: pathlib.Path,
|
||||||
|
network_def: BinaryIO,
|
||||||
|
download_definitions: bool,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Verify message signed with Ethereum address."""
|
"""Verify message signed with Ethereum address."""
|
||||||
chain_id = 1
|
chain_id = 1
|
||||||
@ -624,7 +687,9 @@ def verify_message(
|
|||||||
download_definitions=download_definitions,
|
download_definitions=download_definitions,
|
||||||
chain_id=chain_id,
|
chain_id=chain_id,
|
||||||
)
|
)
|
||||||
return ethereum.verify_message(client, address, signature_bytes, message, chain_id, defs.encoded_network)
|
return ethereum.verify_message(
|
||||||
|
client, address, signature_bytes, message, chain_id, defs.encoded_network
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
|
@ -14,12 +14,14 @@
|
|||||||
# You should have received a copy of the License along with this library.
|
# 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>.
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||||
|
|
||||||
from itertools import chain
|
import pathlib
|
||||||
import pathlib, re, requests
|
import re
|
||||||
from typing import TYPE_CHECKING, Any, AnyStr, Dict, List, Optional, TextIO, Tuple
|
from typing import TYPE_CHECKING, Any, AnyStr, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from . import exceptions, messages
|
from . import exceptions, messages
|
||||||
from .tools import expect, UH_, prepare_message_bytes, session
|
from .tools import UH_, expect, prepare_message_bytes, session
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .client import TrezorClient
|
from .client import TrezorClient
|
||||||
@ -28,11 +30,11 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
# TODO: change once we know the urls
|
# TODO: change once we know the urls
|
||||||
DEFS_BASE_URL="https://data.trezor.io/eth_definitions/{lookup_type}/{id}/{name}.dat"
|
DEFS_BASE_URL = "https://data.trezor.io/eth_definitions/{lookup_type}/{id}/{name}.dat"
|
||||||
DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE="by_chain_id"
|
DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE = "by_chain_id"
|
||||||
DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE="by_slip44"
|
DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE = "by_slip44"
|
||||||
DEFS_NETWORK_URI_NAME="network"
|
DEFS_NETWORK_URI_NAME = "network"
|
||||||
DEFS_TOKEN_URI_NAME="token_{hex_address}"
|
DEFS_TOKEN_URI_NAME = "token_{hex_address}"
|
||||||
|
|
||||||
|
|
||||||
def int_to_big_endian(value: int) -> bytes:
|
def int_to_big_endian(value: int) -> bytes:
|
||||||
@ -159,9 +161,13 @@ def download_from_url(url: str, error_msg: str = "") -> bytes:
|
|||||||
raise RuntimeError(f"{error_msg}{err}")
|
raise RuntimeError(f"{error_msg}{err}")
|
||||||
|
|
||||||
|
|
||||||
def download_network_definition(chain_id: Optional[int] = None, slip44_hardened: Optional[int] = None) -> Optional[bytes]:
|
def download_network_definition(
|
||||||
if not ((chain_id is None) != (slip44_hardened is None)): # not XOR
|
chain_id: Optional[int] = None, slip44_hardened: Optional[int] = None
|
||||||
raise RuntimeError(f"Exactly one of chain_id or slip44_hardened parameters are needed to load network definition from directory.")
|
) -> Optional[bytes]:
|
||||||
|
if not ((chain_id is None) != (slip44_hardened is None)): # not XOR
|
||||||
|
raise RuntimeError(
|
||||||
|
"Exactly one of chain_id or slip44_hardened parameters are needed to load network definition from directory."
|
||||||
|
)
|
||||||
|
|
||||||
if chain_id is not None:
|
if chain_id is not None:
|
||||||
url = DEFS_BASE_URL.format(
|
url = DEFS_BASE_URL.format(
|
||||||
@ -172,17 +178,21 @@ def download_network_definition(chain_id: Optional[int] = None, slip44_hardened:
|
|||||||
else:
|
else:
|
||||||
url = DEFS_BASE_URL.format(
|
url = DEFS_BASE_URL.format(
|
||||||
lookup_type=DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE,
|
lookup_type=DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE,
|
||||||
id=UH_(slip44_hardened),
|
id=UH_(slip44_hardened), # type: ignore [Argument of type "int | None" cannot be assigned to parameter "x" of type "int" in function "UH_"]
|
||||||
name=DEFS_NETWORK_URI_NAME,
|
name=DEFS_NETWORK_URI_NAME,
|
||||||
)
|
)
|
||||||
|
|
||||||
error_msg = f"While downloading network definition from \"{url}\" following HTTP error occured: "
|
error_msg = f'While downloading network definition from "{url}" following HTTP error occured: '
|
||||||
return download_from_url(url, error_msg)
|
return download_from_url(url, error_msg)
|
||||||
|
|
||||||
|
|
||||||
def download_token_definition(chain_id: Optional[int] = None, token_address: Optional[str] = None) -> Optional[bytes]:
|
def download_token_definition(
|
||||||
|
chain_id: Optional[int] = None, token_address: Optional[str] = None
|
||||||
|
) -> Optional[bytes]:
|
||||||
if chain_id is None or token_address is None:
|
if chain_id is None or token_address is None:
|
||||||
raise RuntimeError(f"Both chain_id and token_address parameters are needed to download token definition.")
|
raise RuntimeError(
|
||||||
|
"Both chain_id and token_address parameters are needed to download token definition."
|
||||||
|
)
|
||||||
|
|
||||||
url = DEFS_BASE_URL.format(
|
url = DEFS_BASE_URL.format(
|
||||||
lookup_type=DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE,
|
lookup_type=DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE,
|
||||||
@ -190,37 +200,64 @@ def download_token_definition(chain_id: Optional[int] = None, token_address: Opt
|
|||||||
name=DEFS_TOKEN_URI_NAME.format(hex_address=token_address),
|
name=DEFS_TOKEN_URI_NAME.format(hex_address=token_address),
|
||||||
)
|
)
|
||||||
|
|
||||||
error_msg = f"While downloading token definition from \"{url}\" following HTTP error occured: "
|
error_msg = f'While downloading token definition from "{url}" following HTTP error occured: '
|
||||||
return download_from_url(url, error_msg)
|
return download_from_url(url, error_msg)
|
||||||
|
|
||||||
|
|
||||||
def network_definition_from_dir(path: pathlib.Path, chain_id: Optional[int] = None, slip44_hardened: Optional[int] = None) -> Optional[bytes]:
|
def network_definition_from_dir(
|
||||||
if not ((chain_id is None) != (slip44_hardened is None)): # not XOR
|
path: pathlib.Path,
|
||||||
raise RuntimeError(f"Exactly one of chain_id or slip44_hardened parameters are needed to load network definition from directory.")
|
chain_id: Optional[int] = None,
|
||||||
|
slip44_hardened: Optional[int] = None,
|
||||||
|
) -> Optional[bytes]:
|
||||||
|
if not ((chain_id is None) != (slip44_hardened is None)): # not XOR
|
||||||
|
raise RuntimeError(
|
||||||
|
"Exactly one of chain_id or slip44_hardened parameters are needed to load network definition from directory."
|
||||||
|
)
|
||||||
|
|
||||||
def read_definition(path: pathlib.Path) -> Optional[bytes]:
|
def read_definition(path: pathlib.Path) -> Optional[bytes]:
|
||||||
if not path.exists() or not path.is_file():
|
if not path.exists() or not path.is_file():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
with open(path, mode="rb") as f:
|
with open(path, mode="rb") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
if chain_id is not None:
|
if chain_id is not None:
|
||||||
return read_definition(path / DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE / str(chain_id) / (DEFS_NETWORK_URI_NAME + ".dat"))
|
return read_definition(
|
||||||
|
path
|
||||||
|
/ DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE
|
||||||
|
/ str(chain_id)
|
||||||
|
/ (DEFS_NETWORK_URI_NAME + ".dat")
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return read_definition(path / DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE / str(UH_(slip44_hardened)) / (DEFS_NETWORK_URI_NAME + ".dat"))
|
return read_definition(
|
||||||
|
path
|
||||||
|
/ DEFS_NETWORK_BY_SLIP44_LOOKUP_TYPE
|
||||||
|
/ str(UH_(slip44_hardened)) # type: ignore [Argument of type "int | None" cannot be assigned to parameter "x" of type "int" in function "UH_"]
|
||||||
|
/ (DEFS_NETWORK_URI_NAME + ".dat")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def token_definition_from_dir(path: pathlib.Path, chain_id: Optional[int] = None, token_address: Optional[str] = None) -> Optional[bytes]:
|
def token_definition_from_dir(
|
||||||
|
path: pathlib.Path,
|
||||||
|
chain_id: Optional[int] = None,
|
||||||
|
token_address: Optional[str] = None,
|
||||||
|
) -> Optional[bytes]:
|
||||||
if chain_id is None or token_address is None:
|
if chain_id is None or token_address is None:
|
||||||
raise RuntimeError(f"Both chain_id and token_address parameters are needed to load token definition from directory.")
|
raise RuntimeError(
|
||||||
|
"Both chain_id and token_address parameters are needed to load token definition from directory."
|
||||||
|
)
|
||||||
|
|
||||||
path = path / DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE / str(chain_id) / (DEFS_TOKEN_URI_NAME.format(hex_address=token_address) + ".dat")
|
path = (
|
||||||
|
path
|
||||||
|
/ DEFS_NETWORK_BY_CHAINID_LOOKUP_TYPE
|
||||||
|
/ str(chain_id)
|
||||||
|
/ (DEFS_TOKEN_URI_NAME.format(hex_address=token_address) + ".dat")
|
||||||
|
)
|
||||||
if not path.exists() or not path.is_file():
|
if not path.exists() or not path.is_file():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
with open(path, mode="rb") as f:
|
with open(path, mode="rb") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
# ====== Client functions ====== #
|
# ====== Client functions ====== #
|
||||||
@ -228,7 +265,10 @@ def token_definition_from_dir(path: pathlib.Path, chain_id: Optional[int] = None
|
|||||||
|
|
||||||
@expect(messages.EthereumAddress, field="address", ret_type=str)
|
@expect(messages.EthereumAddress, field="address", ret_type=str)
|
||||||
def get_address(
|
def get_address(
|
||||||
client: "TrezorClient", n: "Address", show_display: bool = False, encoded_network: bytes = None
|
client: "TrezorClient",
|
||||||
|
n: "Address",
|
||||||
|
show_display: bool = False,
|
||||||
|
encoded_network: bytes = None,
|
||||||
) -> "MessageType":
|
) -> "MessageType":
|
||||||
return client.call(
|
return client.call(
|
||||||
messages.EthereumGetAddress(
|
messages.EthereumGetAddress(
|
||||||
@ -241,7 +281,10 @@ def get_address(
|
|||||||
|
|
||||||
@expect(messages.EthereumPublicKey)
|
@expect(messages.EthereumPublicKey)
|
||||||
def get_public_node(
|
def get_public_node(
|
||||||
client: "TrezorClient", n: "Address", show_display: bool = False, encoded_network: bytes = None
|
client: "TrezorClient",
|
||||||
|
n: "Address",
|
||||||
|
show_display: bool = False,
|
||||||
|
encoded_network: bytes = None,
|
||||||
) -> "MessageType":
|
) -> "MessageType":
|
||||||
return client.call(
|
return client.call(
|
||||||
messages.EthereumGetPublicKey(
|
messages.EthereumGetPublicKey(
|
||||||
@ -446,7 +489,12 @@ def sign_typed_data(
|
|||||||
|
|
||||||
|
|
||||||
def verify_message(
|
def verify_message(
|
||||||
client: "TrezorClient", address: str, signature: bytes, message: AnyStr, chain_id: int = 1, encoded_network: bytes = None
|
client: "TrezorClient",
|
||||||
|
address: str,
|
||||||
|
signature: bytes,
|
||||||
|
message: AnyStr,
|
||||||
|
chain_id: int = 1,
|
||||||
|
encoded_network: bytes = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
try:
|
try:
|
||||||
resp = client.call(
|
resp = client.call(
|
||||||
|
Loading…
Reference in New Issue
Block a user