From d0d4c8238fae12738b93fb6bf110961362d78821 Mon Sep 17 00:00:00 2001 From: Maxim Kupriianov Date: Mon, 12 Apr 2021 23:38:07 +0300 Subject: [PATCH 1/3] Implemented EIP-712 typed data signatures in Ethereum app. --- common/protob/messages-ethereum.proto | 40 ++ common/protob/messages.proto | 3 + core/src/apps/ethereum/__init__.py | 1 + core/src/apps/ethereum/abi.py | 308 +++++++++++++++ core/src/apps/ethereum/layout.py | 221 ++++++++++- core/src/apps/ethereum/sign_typed_data.py | 351 ++++++++++++++++++ .../trezor/messages/EthereumSignTypedData.py | 30 ++ .../trezor/messages/EthereumTypedDataAck.py | 39 ++ .../messages/EthereumTypedDataRequest.py | 36 ++ python/docs/OPTIONS.rst | 1 + python/src/trezorlib/cli/ethereum.py | 16 + python/src/trezorlib/ethereum.py | 271 ++++++++++++++ .../messages/EthereumSignTypedData.py | 30 ++ .../messages/EthereumTypedDataAck.py | 39 ++ .../messages/EthereumTypedDataRequest.py | 36 ++ python/src/trezorlib/messages/MessageType.py | 3 + 16 files changed, 1422 insertions(+), 3 deletions(-) create mode 100644 core/src/apps/ethereum/abi.py create mode 100644 core/src/apps/ethereum/sign_typed_data.py create mode 100644 core/src/trezor/messages/EthereumSignTypedData.py create mode 100644 core/src/trezor/messages/EthereumTypedDataAck.py create mode 100644 core/src/trezor/messages/EthereumTypedDataRequest.py create mode 100644 python/src/trezorlib/messages/EthereumSignTypedData.py create mode 100644 python/src/trezorlib/messages/EthereumTypedDataAck.py create mode 100644 python/src/trezorlib/messages/EthereumTypedDataRequest.py diff --git a/common/protob/messages-ethereum.proto b/common/protob/messages-ethereum.proto index 9fab13c2c54..7e8c67c5f8f 100644 --- a/common/protob/messages-ethereum.proto +++ b/common/protob/messages-ethereum.proto @@ -111,6 +111,46 @@ message EthereumMessageSignature { required string address = 3; // address used to sign the message } + +/** + * Request: Ask device to sign typed data + * @start + * @next EthereumTypedDataRequest + * @next Failure + */ +message EthereumSignTypedData { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional bool use_v4 = 2 [default=true]; // use EIP-712 (v4) for typed data signing +} + +/** + * Response: Device asks for more values from typed data, or returns the signature and address. + * If member_path is not empty, device awaits the information for this value. + * Otherwise, the signature field contain the computed transaction signature. + * @end + * @next EthereumTypedDataAck + * @next Failure + */ +message EthereumTypedDataRequest { + repeated uint32 member_path = 1; // type or value member path requested by device + optional bool expect_type = 2; // if true, then device expects type info, otherwise value + optional bytes signature = 3; // signature of the message + optional string address = 4; // address used to sign the message +} + +/** + * Request: Typed data value. + * If the member is a struct member_value should not be set. If the member is literal num_members should not be set. + * @next EthereumTypedDataRequest + */ +message EthereumTypedDataAck { + optional string member_name = 1; // if expect_type was true, should contain memeber name + optional string member_type = 2; // contains member type name + optional uint32 member_array_n = 3; // set to 0 when array is dynamic, None when member is not an array + optional uint32 member_children = 4; // if expect_type was true, should contain its children number + optional bytes member_value = 5; // if expect_type was false and value is not struct or array, should contain eth-abi encoded member value +} + /** * Request: Ask device to verify message * @start diff --git a/common/protob/messages.proto b/common/protob/messages.proto index c87f8a6b720..bc3243e2c13 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -150,6 +150,9 @@ enum MessageType { MessageType_EthereumSignMessage = 64 [(wire_in) = true]; MessageType_EthereumVerifyMessage = 65 [(wire_in) = true]; MessageType_EthereumMessageSignature = 66 [(wire_out) = true]; + MessageType_EthereumSignTypedData = 464 [(wire_in) = true]; + MessageType_EthereumTypedDataRequest = 465 [(wire_out) = true]; + MessageType_EthereumTypedDataAck = 466 [(wire_in) = true]; // NEM MessageType_NEMGetAddress = 67 [(wire_in) = true]; diff --git a/core/src/apps/ethereum/__init__.py b/core/src/apps/ethereum/__init__.py index 1ce060d24ea..9c2fa1c5345 100644 --- a/core/src/apps/ethereum/__init__.py +++ b/core/src/apps/ethereum/__init__.py @@ -9,4 +9,5 @@ def boot() -> None: wire.add(MessageType.EthereumGetPublicKey, __name__, "get_public_key") wire.add(MessageType.EthereumSignTx, __name__, "sign_tx") wire.add(MessageType.EthereumSignMessage, __name__, "sign_message") + wire.add(MessageType.EthereumSignTypedData, __name__, "sign_typed_data") wire.add(MessageType.EthereumVerifyMessage, __name__, "verify_message") diff --git a/core/src/apps/ethereum/abi.py b/core/src/apps/ethereum/abi.py new file mode 100644 index 00000000000..63518995810 --- /dev/null +++ b/core/src/apps/ethereum/abi.py @@ -0,0 +1,308 @@ + +def abi_encode_single(type_name, arg) -> bytes: + if type_name is "address": + return abi_encode_single("uint160", parse_number(arg)) + elif type_name is "bool": + return abi_encode_single("uint256", 1 if arg is True else 0) + elif type_name is "string": + return abi_encode_single("bytes", bytes(arg, encoding="utf8")) + elif is_array(type_name): + # this part handles fixed-length ([2]) and variable length ([]) arrays + if not isinstance(arg, list): + raise ValueError("not an array") + + size = parse_array_n(type_name) + if not (size is "dynamic") and not (size is 0) and len(arg) > size: + raise ValueError("elements exceed array size: %d" % size) + + ret = [] + type_name = type_name[:type_name.rindex('[')] + for item in arg: + ret.append(abi_encode_single(type_name, item)) + + if size is "dynamic": + ret = [abi_encode_single("uint256", len(arg))] + ret + + return b"".join(ret) + elif type_name is "bytes": + ret = bytearray(0) + ret.extend(abi_encode_single("uint256", len(arg))) + ret.extend(arg) + + if not (len(arg) % 32 is 0): + zeros_padding = bytearray(32 - (len(arg) % 32)) + ret = b"".join([ret, zeros_padding]) + + return ret + elif type_name.startswith("bytes"): + size = parse_type_n(type_name) + if size < 1 or size > 32: + raise ValueError("invalid bytes width: %d" % size) + if not (isinstance(arg, bytes) or isinstance(arg, bytearray)): + raise ValueError("arg for bytes is not bytes") + + return bytearray(set_length_right(arg, 32)) + elif type_name.startswith("uint"): + size = parse_type_n(type_name) + + if (not size % 8 is 0) or (size < 8) or (size > 256): + raise ValueError("invalid uint width: %d" % size) + + num = parse_number(arg) + if num < 0: + raise ValueError("supplied uint is negative") + + return num.to_bytes(length=32, byteorder="big") + elif type_name.startswith("int"): + size = parse_type_n(type_name) + + if (not size % 8 is 0) or (size < 8) or (size > 256): + raise ValueError("invalid int width: %d" % size) + + num = parse_number(arg) + return num.to_bytes(length=32, byteorder="big", signed=True) + + raise ValueError("unsupported or invalid type: %s" % type_name) + + +def abi_encode(types: list, values: list) -> bytearray: + output = [] + data = [] + head_len = 0 + + for type_name in types: + if is_array(type_name): + size = parse_array_n(type_name) + if not (size is "dynamic"): + head_len += 32 * size + else: + head_len += 32 + else: + head_len += 32 + + for i in range(0, len(types)): + type_name = types[i] + value = values[i] + buf = abi_encode_single(type_name, value) + + if isinstance(buf, bytes): + raise ValueError("encoded {} with {} as bytes! HALT".format(type_name, value)) + + # use the head/tail method for storing dynamic data + if is_dynamic(type_name): + output.append(abi_encode_single("uint256", head_len)) + data.append(buf) + head_len += len(buf) + else: + output.append(buf) + + res = bytearray(0) + for x in output + data: + res.extend(x) + + return res + + +def abi_decode(types: list, data: list, packed: bool = True) -> []: + ret = [] + offset = 0 + + for i in range(0, len(types)): + parsed_type = parse_type(types[i]) + ret.append(abi_decode_single(parsed_type["name"], data[i], packed, offset)) + offset += parsed_type["memory_usage"] + + return ret + + +def abi_decode_single(parsed_type: str, data: bytes, packed: bool = True, offset: int = 0): + if isinstance(parsed_type, str): + parsed_type = parse_type(parsed_type) + + type_name = parsed_type["name"] + raw_type = parsed_type["raw_type"] + + print("abi_decode_single", parsed_type, "data:", data, "offset:", offset) + print("type_name", type_name, "raw_type", raw_type) + + if type_name == "address": + value = abi_decode_single(raw_type, data, packed, offset) + return "0x%s" % value.to_bytes(20, "big").hex() + + elif type_name == "bool": + value = abi_decode_single(raw_type, data, packed, offset) + if "%d" % value == "1": + return True + elif "%d" % value == "0": + return False + else: + raise ValueError("cannot decode bool value from {}".format(value)) + + elif type_name == "string": + value = abi_decode_single(raw_type, data, packed, offset) + return value.decode("utf8") + + elif parsed_type["is_array"]: + # this part handles fixed-length arrays ([2]) and variable length ([]) arrays + ret = [] + size = parsed_type["size"] + + if size == "dynamic": + offset = abi_decode_single("uint256", data, packed, offset) + size = abi_decode_single("uint256", data, packed, offset) + offset += 32 + + sub_array = parsed_type["sub_array"] + for i in range(0, size): + decoded = abi_decode_single(sub_array, data, packed, offset) + ret.append(decoded) + offset += sub_array["memory_usage"] + + return ret + + elif type_name == "bytes": + if packed: + return data + + offset = abi_decode_single("uint256", data, packed, offset) + size = abi_decode_single("uint256", data, packed, offset) + return data[offset + 32: offset + 32 + size] + + elif type_name.startswith("bytes"): + return data[offset: offset + parsed_type["size"]] + + elif type_name.startswith("uint"): + print("INT FROM_BYTES DATA", data, "offsetted:", data[offset: offset + 32]) + return int.from_bytes(data[offset: offset + 32], "big") + + elif type_name.startswith("int"): + return signed_int(data[offset: offset + 32], 256) + + raise ValueError("unsupported or invalid type: %s" % type_name) + + +def parse_type(type_name) -> dict: + """ + Parse the given type + + Returns dict containing the type itself, `memory_usage` and (including `size` and `sub_array` if applicable) + """ + ret = { + "name": type_name, + "is_array": False, + "raw_type": None, + "size": None, + "memory_usage": None, + "sub_array": None, + } + + if is_array(type_name): + size = parse_array_n(type_name) + sub_array = parse_type(typeof_array(type_name)) + ret["memory_usage"] = 32 if size == "dynamic" else sub_array["memory_usage"] * size, + ret["sub_array"] = sub_array + ret["is_array"] = True + return ret + else: + ret["memory_usage"] = 32 + + if type_name == "address": + ret["raw_type"] = "uint160" + elif type_name == "bool": + ret["raw_type"] = "uint256" + elif type_name == "string": + ret["raw_type"] = "bytes" + + if (type_name.startswith("bytes") and type_name != "bytes") or \ + type_name.startswith("uint") or type_name.startswith("int"): + + size = parse_type_n(type_name) + + if (type_name.startswith("bytes") and type_name != "bytes") and (size < 1 or size > 32): + raise ValueError("invalid bytes width: %d" % size) + + if (type_name.startswith("uint") or type_name.startswith("int")) and (size % 8 != 0 or size < 8 or size > 256): + raise ValueError("invalid int/uint width: %d" % size) + + ret["size"] = size + + return ret + + +def set_length_right(msg: bytes, length) -> bytes: + """ + Pads a `msg` with zeros till it has `length` bytes. + Truncates the end of input if its length exceeds `length`. + """ + if len(msg) < length: + buf = bytearray(length) + buf[:len(msg)] = msg + return buf + + return msg[:length] + + +def parse_type_n(type_name): + """Parse N from type""" + accum = [] + for c in type_name: + if c.isdigit(): + accum.append(c) + else: + accum = [] + + # join collected digits into a number + return int("".join(accum)) + + +def parse_number(arg): + if isinstance(arg, str): + return int(arg, 16) + elif isinstance(arg, int): + return arg + + raise ValueError("arg is not a number") + + +def is_array(type_name: str) -> bool: + if type_name: + return type_name[len(type_name) - 1] == ']' + + return False + + +def typeof_array(type_name) -> str: + return type_name[:type_name.rindex('[')] + + +def parse_array_n(type_name: str): + """Parse N in type[] where "type" can itself be an array type.""" + if type_name.endswith("[]"): + return "dynamic" + + start_idx = type_name.rindex('[')+1 + end_idx = len(type_name) - 1 + + return int(type_name[start_idx:end_idx]) + + +def is_dynamic(type_name: str) -> bool: + if type_name is "string": + return True + elif type_name is "bytes": + return True + elif is_array(type_name) and parse_array_n(type_name) is "dynamic": + return True + + return False + + +def signed_int(val, bits): + if type(val) is bytes: + val = int.from_bytes(val, "big") + elif type(val) is str: + val = int(val, 16) + if (val & (1 << (bits - 1))) != 0: + val = val - (1 << bits) + return val + diff --git a/core/src/apps/ethereum/layout.py b/core/src/apps/ethereum/layout.py index e4872d67391..cb9f7ae0b2c 100644 --- a/core/src/apps/ethereum/layout.py +++ b/core/src/apps/ethereum/layout.py @@ -4,13 +4,214 @@ from trezor.messages import ButtonRequestType from trezor.strings import format_amount from trezor.ui.components.tt.text import Text +from trezor.ui.components.tt.scroll import Paginated from trezor.utils import chunks -from apps.common.confirm import require_confirm, require_hold_to_confirm +from apps.common.confirm import confirm, require_confirm, require_hold_to_confirm from apps.common.layout import split_address from . import networks, tokens from .address import address_from_bytes +from .abi import abi_decode_single, is_array, typeof_array, parse_array_n + + +async def confirm_typed_domain_brief(ctx, domain_values: dict): + page = Text("Typed Data", ui.ICON_SEND, icon_color=ui.GREEN) + + domain_name = abi_decode_single("string", domain_values.get("name")) + domain_version = abi_decode_single("string", domain_values.get("version")) + + page.bold("%s" % domain_name) + page.normal("%s" % domain_version) + page.br() + page.mono("View EIP712Domain?") + + return await confirm(ctx, page, ButtonRequestType.Other) + + +async def require_confirm_typed_domain(ctx, domain_types: dict, domain_values: dict): + def make_field_page(title, field_name, type_name, field_value): + page = Text(title, ui.ICON_CONFIG, icon_color=ui.ORANGE_ICON) + page.bold("%s (%s)" % (field_name, type_name)) + page.mono(*split_data("{}".format(field_value), 17)) + return page + + pages = [] + for type_def in domain_types: + value = domain_values.get(type_def["name"]) + pages.append(make_field_page( + title="EIP712Domain %d/%d" % (len(pages)+1, len(domain_types)), + field_name=limit_str(type_def["name"]), + type_name=limit_str(type_def["type"]), + field_value=abi_decode_single(type_def["type"], value), + )) + + return await require_hold_to_confirm( + ctx, Paginated(pages), ButtonRequestType.ConfirmOutput + ) + + +TYPED_DATA_BRIEF_FIELDS = 3 + + +async def confirm_typed_data_brief(ctx, primary_type: str, fields: []): + page = Text(primary_type, ui.ICON_SEND, icon_color=ui.GREEN) + + limit = TYPED_DATA_BRIEF_FIELDS + for field in fields: + page.bold("%s" % limit_str(field["name"])) + limit -= 1 + if limit == 0: + break + + printed_num = (TYPED_DATA_BRIEF_FIELDS - limit) + if printed_num < len(fields): + page.mono("...and %d more." % (len(fields) - printed_num)) + + page.mono("View full message?") + + return await confirm(ctx, page, ButtonRequestType.Other) + + +async def require_confirm_typed_data(ctx, primary_type: str, data_types: dict, data_values: dict): + def make_type_page(root_name, field_name, current_array_offsets, current_field, total_fields): + array_offsets = "" + for offset in current_array_offsets: + array_offsets += "%d." % offset + + if len(array_offsets) > 0: + title = limit_str("%s.%s%s" % (root_name, array_offsets, field_name), 13) + else: + title = limit_str("%s.%s" % (root_name, field_name), 13) + + if len(array_offsets) == 0: + title += " %d/%d" % (current_field+1, total_fields) + + return Text(title, ui.ICON_CONFIG, icon_color=ui.ORANGE_ICON) + + async def confirm_struct(root_name, type_name: str, values: dict, array_offsets: list, hold: bool = False): + current_type = type_name + current_root_name = root_name + type_def = data_types[current_type] + + type_view_pages = [] + + for (field_idx, field) in enumerate(type_def): + current_type = field["type"] + current_value = values.get(field["name"]) + + if is_array(current_type): + array_preview_page = make_type_page( + root_name=current_root_name, + field_name=field["name"], + current_array_offsets=array_offsets, + current_field=field_idx, + total_fields=len(type_def), + ) + + array_view_page = make_type_page( + root_name=current_root_name, + field_name=field["name"], + current_array_offsets=array_offsets, + current_field=field_idx, + total_fields=len(type_def), + ) + + array_len = len(current_value) + array_preview_page.bold(limit_str(field["type"])) + array_preview_page.mono("Contains %d elem%s." % (array_len, "s" if array_len > 1 else "")) + array_preview_page.br() + array_preview_page.mono("View data?") + + array_view_page.bold(limit_str(field["type"])) + array_view_page.mono("Contains %d elem%s." % (array_len, "s" if array_len > 1 else "")) + type_view_pages.append(array_view_page) + + go_deeper = await confirm(ctx, array_preview_page, ButtonRequestType.ConfirmOutput) + if go_deeper: + for array_offset in range(0, len(current_value)): + await confirm_struct( + root_name=field["name"], + type_name=typeof_array(current_type), + values=current_value[array_offset], + array_offsets=array_offsets + [array_offset], + hold=False, + ) + continue + + continue + + type_view_page = make_type_page( + root_name=current_root_name, + field_name=field["name"], + current_array_offsets=array_offsets, + current_field=field_idx, + total_fields=len(type_def), + ) + if current_type in data_types: + type_preview_page = make_type_page( + root_name=current_root_name, + field_name=field["name"], + current_array_offsets=array_offsets, + current_field=field_idx, + total_fields=len(type_def), + ) + + fields_num = len(data_types[current_type]) + type_preview_page.bold(limit_str(current_type)) + type_preview_page.mono("Contains %d field%s." % (fields_num, "s" if fields_num > 1 else "")) + type_preview_page.br() + type_preview_page.mono("View data?") + + type_view_page.bold(limit_str(current_type)) + type_view_page.mono("Contains %d field%s." % (fields_num, "s" if fields_num > 1 else "")) + type_view_pages.append(type_view_page) + + go_deeper = await confirm(ctx, type_preview_page, ButtonRequestType.ConfirmOutput) + if go_deeper: + await confirm_struct( + root_name=field["name"], + type_name=current_type, + values=current_value, + array_offsets=[], + hold=False, + ) + + else: + type_view_page.bold(current_type) + value_decoded = abi_decode_single(current_type, current_value) + type_view_page.mono(*split_data(value_decoded, 17)) + type_view_pages.append(type_view_page) + + if hold: + return await require_hold_to_confirm( + ctx, + Paginated(type_view_pages) if len(type_view_pages) > 1 else type_view_pages[0], + ButtonRequestType.ConfirmOutput, + ) + return await require_confirm( + ctx, + Paginated(type_view_pages) if len(type_view_pages) > 1 else type_view_pages[0], + ButtonRequestType.ConfirmOutput, + ) + + await confirm_struct( + root_name=primary_type, + type_name=primary_type, + values=data_values, + array_offsets=[], + hold=True, + ) + + +async def require_confirm_typed_data_hash(ctx, primary_type: str, typed_data_hash: bytes): + text = Text("Sign typed data?", ui.ICON_CONFIG, icon_color=ui.GREEN, new_lines=False) + text.bold(limit_str(primary_type)) + text.mono(*split_data("0x%s" % hexlify(typed_data_hash).decode())) + + return await require_hold_to_confirm( + ctx, text, ButtonRequestType.ConfirmOutput + ) async def require_confirm_tx(ctx, to_bytes, value, chain_id, token=None, tx_type=None): @@ -48,8 +249,22 @@ async def require_confirm_unknown_token(ctx, address_bytes): await require_confirm(ctx, text, ButtonRequestType.SignTx) -def split_data(data): - return chunks(data, 18) +def split_data(data, width: int = 18): + return chunks(data, width) + + +def limit_str(s: str, limit: int = 16) -> str: + if len(s) <= limit+2: + return s + + return s[:limit] + ".." + + +def limit_left_str(s: str, limit: int = 16) -> str: + if len(s) <= limit+2: + return s + + return ".." + s[(len(s) - limit):] async def require_confirm_data(ctx, data, data_total): diff --git a/core/src/apps/ethereum/sign_typed_data.py b/core/src/apps/ethereum/sign_typed_data.py new file mode 100644 index 00000000000..ed11d6655cd --- /dev/null +++ b/core/src/apps/ethereum/sign_typed_data.py @@ -0,0 +1,351 @@ +from ubinascii import hexlify, unhexlify + +from trezor.crypto.curve import secp256k1 +from trezor.crypto.hashlib import sha3_256 +from trezor.messages.EthereumTypedDataAck import EthereumTypedDataAck +from trezor.messages.EthereumTypedDataRequest import EthereumTypedDataRequest + +from trezor.ui.components.tt.text import Text +from trezor.utils import HashWriter + +from apps.common import paths +from apps.common.confirm import confirm, require_confirm, require_hold_to_confirm + +from . import address +from .keychain import PATTERNS_ADDRESS, with_keychain_from_path +from .layout import ( + confirm_typed_domain_brief, + confirm_typed_data_brief, + require_confirm_typed_domain, + require_confirm_typed_data, + require_confirm_typed_data_hash, +) + +from .abi import abi_encode, is_array, typeof_array + + +def keccak256(message): + h = HashWriter(sha3_256(keccak=True)) + h.extend(message) + return h.get_digest() + + +@with_keychain_from_path(*PATTERNS_ADDRESS) +async def sign_typed_data(ctx, msg, keychain): + data_hash = await generate_typed_data_hash(ctx, msg.use_v4) + + await paths.validate_path(ctx, keychain, msg.address_n) + + node = keychain.derive(msg.address_n) + signature = secp256k1.sign( + node.private_key(), data_hash, False, secp256k1.CANONICAL_SIG_ETHEREUM + ) + + sig = EthereumTypedDataRequest() + sig.address = address.address_from_bytes(node.ethereum_pubkeyhash()) + sig.signature = signature[1:] + bytearray([signature[0]]) + return sig + + +async def generate_typed_data_hash(ctx, use_v4: bool = True) -> bytes: + """ + Generates typed data hash according to EIP-712 specification + https://eips.ethereum.org/EIPS/eip-712#specification + + use_v4 - a flag that enables compatibility with MetaMask's signTypedData_v4 method + """ + domain_types = await collect_domain_types(ctx) + domain_values = await collect_values(ctx, "EIP712Domain", domain_types, [0]) + primary_type, message_types = await collect_types(ctx) + message_values = await collect_values(ctx, primary_type, message_types) + + show_domain = await confirm_typed_domain_brief(ctx, domain_values) + if show_domain: + await require_confirm_typed_domain(ctx, domain_types["EIP712Domain"], domain_values) + + show_message = await confirm_typed_data_brief(ctx, primary_type, message_types[primary_type]) + if show_message: + await require_confirm_typed_data(ctx, primary_type, message_types, message_values) + + domain_separator = hash_struct("EIP712Domain", domain_values, domain_types, use_v4) + message_hash = hash_struct(primary_type, message_values, message_types, use_v4) + + if not show_message: + await require_confirm_typed_data_hash(ctx, primary_type, message_hash) + + return keccak256(b"\x19" + b"\x01" + domain_separator + message_hash) + + +async def collect_domain_types(ctx) -> dict: + """ + Collects domain types from the client + """ + root_type = await request_member_type(ctx, [0, 0]) + if root_type.member_type != "EIP712Domain": + raise ValueError("EIP712 domain not provided") + + children = [] + if root_type.member_children: + for i in range(0, root_type.member_children): + dep = await request_member_type(ctx, [0, 0, i]) + children.append({ + "type": dep.member_type, + "name": dep.member_name, + "children_num": dep.member_children, + "member_array_n": dep.member_array_n, + }) + + types = { + "EIP712Domain": children, + } + return types + + +async def collect_types(ctx, types: dict = None, member_path: list = None) -> (str, dict): + """ + Collects type definitions from the client + """ + if types is None: + types = {} + if member_path is None: + member_path = [1] + + primary_type = None + member_type_offset = 0 + while member_type_offset < 65536: + member_type_path = member_path + [member_type_offset] + member = await request_member_type(ctx, member_type_path) + type_name = member.member_type + if type_name is None: + break + elif member_type_offset is 0: + primary_type = type_name + + if not (type_name in types): + types[type_name] = [] + if member.member_children: + for i in range(0, member.member_children): + dep = await request_member_type(ctx, member_type_path + [i]) + types[type_name].append({ + "type": dep.member_type, + "name": dep.member_name, + "children_num": dep.member_children, + "member_array_n": dep.member_array_n, + }) + + member_type_offset += 1 + + return primary_type, types + + +def hash_struct(primary_type: str, data: dict, types: dict = None, use_v4: bool = True) -> bytes: + """ + Encodes and hashes an object using Keccak256 + """ + return keccak256(encode_data(primary_type, data, types, use_v4)) + + +def encode_data(primary_type: str, data: dict, types: dict = None, use_v4: bool = True) -> bytes: + """ + Encodes an object by encoding and concatenating each of its members + + primary_type - Root type + data - Object to encode + types - Type definitions + """ + encoded_types = ["bytes32"] + encoded_values = [hash_type(primary_type, types)] + + for field in types[primary_type]: + enc_type, enc_value = encode_field( + use_v4=use_v4, + in_array=False, + types=types, + name=field["name"], + type_name=field["type"], + value=data.get(field["name"]), + ) + encoded_types.append(enc_type) + encoded_values.append(enc_value) + + return abi_encode(encoded_types, encoded_values) + + +def encode_field(use_v4: bool, in_array: bool, types: dict, name: str, type_name: str, value) -> (str, bytes): + if type_name in types: + if value is None: + return "bytes32", bytes(32) + + if in_array and not use_v4: + return "bytes", encode_data(type_name, value, types) + + return "bytes32", hash_struct(type_name, value, types, use_v4) + + if value is None: + raise ValueError("missing value for field %s of type %s" % (name, type_name)) + + if type_name is "bytes32": + if not (isinstance(value, bytes) or isinstance(value, bytearray)): + raise ValueError("value for field %s (type %s) expected to be bytes or bytearray" % (name, type_name)) + + return "bytes32", keccak256(value) + + if type_name is "string": + if isinstance(value, str): + value = bytes(value, encoding="utf8") + return "bytes32", keccak256(value) + elif not (isinstance(value, bytes) or isinstance(value, bytearray)): + raise ValueError("value for field %s (type %s) expected to be bytes, bytearray or str" % (name, type_name)) + + return "bytes32", keccak256(value) + + if is_array(type_name): + parsed_type = typeof_array(type_name) + while is_array(parsed_type): + parsed_type = typeof_array(parsed_type) + + if parsed_type in types: + if not isinstance(value, list): + raise ValueError("value for field %s (type %s) expected to be a list" % (name, type_name)) + + type_value_pairs = map(lambda x: encode_field( + use_v4=use_v4, + in_array=True, + types=types, + name=name, + type_name=typeof_array(type_name), + value=x, + ), value) + + encoded_value = bytearray(0) + for [_, value] in type_value_pairs: + encoded_value.extend(value) + + return "bytes32", keccak256(encoded_value) + + return "bytes32", keccak256(value) + + # value is already abi-encoded + return "bytes32", value + + +async def collect_values(ctx, primary_type: str, types: dict = None, member_path: list = None) -> dict: + """ + Collects data values from the client + """ + if types is None: + types = {} + if member_path is None: + member_path = [1] + + values = {} + struct = types.get(primary_type) + + for fieldIdx in range(0, len(struct)): + field = struct[fieldIdx] + field_name = field["name"] + field_type = field["type"] + type_children = field["children_num"] + type_array = field["member_array_n"] + member_value_path = member_path + [fieldIdx] + + res = await request_member_value(ctx, member_value_path) + if res.member_type is None: + raise ValueError("value of %s in %s is not set in data" % (field_name, primary_type)) + + if not (res.member_value is None): + values[field_name] = res.member_value + elif type_children and type_children > 0: + values[field_name] = await collect_values(ctx, field_type, types, member_value_path) + elif not (type_array is None): + if type_array is 0: + # override with dynamic size we've just got from values + type_array = res.member_array_n + + values[field_name] = [] + for elemIdx in range(0, type_array): + elem = await collect_values(ctx, res.member_type, types, member_value_path + [elemIdx]) + values[field_name].append(elem) + else: + values[field_name] = None + + return values + + +def hash_type(primary_type, types) -> bytes: + """ + Encodes and hashes a type using Keccak256 + """ + return keccak256(encode_type(primary_type, types)) + + +def encode_type(primary_type: str, types: dict): + """ + Encodes the type of an object by encoding a comma delimited list of its members + + primary_type - Root type to encode + types - Type definitions + """ + result = b"" + + deps = find_typed_dependencies(primary_type, types) + deps = list(filter(lambda dep: dep != primary_type, deps)) + deps = [primary_type] + sorted(deps) + + for type_name in deps: + children = types.get(type_name) + if children is None: + raise ValueError("no type definition specified: %s" % type_name) + fields = ",".join(map(lambda field: "%s %s" % (field["type"], field["name"]), children)) + result += b"%s(%s)" % (type_name, fields) + + return result + + +def find_typed_dependencies(primary_type: str, types=None, results=None): + """ + Finds all types within a type definition object + + primary_type - Root type + types - Type definitions + results - Current set of accumulated types + """ + if results is None: + results = [] + if types is None: + types = {} + + if primary_type[len(primary_type)-1] == ']': + primary_type = primary_type[:primary_type.rindex('[')] + + if (primary_type in results) or (types.get(primary_type) is None): + return results + + results = results + [primary_type] + for field in types[primary_type]: + deps = find_typed_dependencies(field["type"], types, results) + for dep in deps: + if not (dep in results): + results = results + [dep] + + return results + + +async def request_member_type(ctx, member_path): + """ + Requests a type of member at `member_path` from the client + """ + req = EthereumTypedDataRequest() + req.member_path = member_path + req.expect_type = True + return await ctx.call(req, EthereumTypedDataAck) + + +async def request_member_value(ctx, member_path): + """ + Requests a value of member at `member_path` from the client + """ + req = EthereumTypedDataRequest() + req.member_path = member_path + req.expect_type = False + return await ctx.call(req, EthereumTypedDataAck) diff --git a/core/src/trezor/messages/EthereumSignTypedData.py b/core/src/trezor/messages/EthereumSignTypedData.py new file mode 100644 index 00000000000..3b55fb0458d --- /dev/null +++ b/core/src/trezor/messages/EthereumSignTypedData.py @@ -0,0 +1,30 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List, Optional # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class EthereumSignTypedData(p.MessageType): + MESSAGE_WIRE_TYPE = 464 + + def __init__( + self, + *, + address_n: Optional[List[int]] = None, + use_v4: bool = True, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.use_v4 = use_v4 + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('use_v4', p.BoolType, True), # default=true + } diff --git a/core/src/trezor/messages/EthereumTypedDataAck.py b/core/src/trezor/messages/EthereumTypedDataAck.py new file mode 100644 index 00000000000..0cb5f5fe0eb --- /dev/null +++ b/core/src/trezor/messages/EthereumTypedDataAck.py @@ -0,0 +1,39 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List, Optional # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class EthereumTypedDataAck(p.MessageType): + MESSAGE_WIRE_TYPE = 466 + + def __init__( + self, + *, + member_name: Optional[str] = None, + member_type: Optional[str] = None, + member_array_n: Optional[int] = None, + member_children: Optional[int] = None, + member_value: Optional[bytes] = None, + ) -> None: + self.member_name = member_name + self.member_type = member_type + self.member_array_n = member_array_n + self.member_children = member_children + self.member_value = member_value + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('member_name', p.UnicodeType, None), + 2: ('member_type', p.UnicodeType, None), + 3: ('member_array_n', p.UVarintType, None), + 4: ('member_children', p.UVarintType, None), + 5: ('member_value', p.BytesType, None), + } diff --git a/core/src/trezor/messages/EthereumTypedDataRequest.py b/core/src/trezor/messages/EthereumTypedDataRequest.py new file mode 100644 index 00000000000..fbd553ea13d --- /dev/null +++ b/core/src/trezor/messages/EthereumTypedDataRequest.py @@ -0,0 +1,36 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List, Optional # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class EthereumTypedDataRequest(p.MessageType): + MESSAGE_WIRE_TYPE = 465 + + def __init__( + self, + *, + member_path: Optional[List[int]] = None, + expect_type: Optional[bool] = None, + signature: Optional[bytes] = None, + address: Optional[str] = None, + ) -> None: + self.member_path = member_path if member_path is not None else [] + self.expect_type = expect_type + self.signature = signature + self.address = address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('member_path', p.UVarintType, p.FLAG_REPEATED), + 2: ('expect_type', p.BoolType, None), + 3: ('signature', p.BytesType, None), + 4: ('address', p.UnicodeType, None), + } diff --git a/python/docs/OPTIONS.rst b/python/docs/OPTIONS.rst index 83de6c6c87c..6c7f93856ec 100644 --- a/python/docs/OPTIONS.rst +++ b/python/docs/OPTIONS.rst @@ -253,6 +253,7 @@ Ethereum commands. get-address Get Ethereum address in hex encoding. get-public-node Get Ethereum public node of given path. sign-message Sign message with Ethereum address. + sign-typed-data Sign typed data (EIP-712) with Ethereum address. sign-tx Sign (and optionally publish) Ethereum transaction. verify-message Verify message signed with Ethereum address. diff --git a/python/src/trezorlib/cli/ethereum.py b/python/src/trezorlib/cli/ethereum.py index ff27b9fd0ab..0e43f117857 100644 --- a/python/src/trezorlib/cli/ethereum.py +++ b/python/src/trezorlib/cli/ethereum.py @@ -33,6 +33,7 @@ PATH_HELP = "BIP-32 path, e.g. m/44'/60'/0'/0/0" +TYPED_DATA_USE_V4 = "Specify to use EIP-712 (v4) for typed data signing" # fmt: off ETHER_UNITS = { @@ -307,6 +308,21 @@ def sign_message(client, address, message): return output +@cli.command() +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.option("--use-v4", type=bool, default=True, required=False, help=TYPED_DATA_USE_V4) +@click.argument("typed_data_json") +@with_client +def sign_typed_data(client, address, use_v4, typed_data_json): + """Sign typed data (EIP-712) with Ethereum address.""" + address_n = tools.parse_path(address) + ret = ethereum.sign_typed_data(client, address_n, use_v4, typed_data_json) + output = { + "address": ret.address, + "signature": "0x%s" % ret.signature.hex(), + } + return output + @cli.command() @click.argument("address") @click.argument("signature") diff --git a/python/src/trezorlib/ethereum.py b/python/src/trezorlib/ethereum.py index 76a6121c4fa..20baccf51bd 100644 --- a/python/src/trezorlib/ethereum.py +++ b/python/src/trezorlib/ethereum.py @@ -17,11 +17,157 @@ from . import exceptions, messages from .tools import expect, normalize_nfc, session +from eth_abi.packed import encode_single_packed +import json +import re def int_to_big_endian(value): return value.to_bytes((value.bit_length() + 7) // 8, "big") +type_name_re = re.compile("^\\w*") + + +def find_typed_dependencies(primary_type: str, types: dict, results: list = None): + """ + Finds all types within a type definition object + + primary_type - Root type + types - Type definitions + results - Current set of accumulated types + """ + if results is None: + results = [] + + m = type_name_re.match(primary_type) + if m: + primary_type = m.string[m.start():m.end()] + else: + raise ValueError("cannot parse primary type: %s" % primary_type) + + if (primary_type in results) or (types.get(primary_type) is None): + return results + + results = results + [primary_type] + for field in types[primary_type]: + deps = find_typed_dependencies(field["type"], types, results) + for dep in deps: + if not dep in results: + results = results + [dep] + + return results + +def encode_type(primary_type: str, types: dict): + """ + Encodes the type of an object by encoding a comma delimited list of its members + + primary_type - Root type to encode + types - Type definitions + """ + result = "" + result_indexed = {} + + deps = find_typed_dependencies(primary_type, types) + deps = list(filter(lambda dep: dep != primary_type, deps)) + deps = [primary_type] + sorted(deps) + + for type_name in deps: + children = types.get(type_name) + if children is None: + raise ValueError("no type definition specified: %s" % type_name) + fields = ",".join(map(lambda field: "%s %s" % (field["type"], field["name"]), children)) + result_indexed[type_name] = [field for (_, field) in enumerate(children)] + result += "%s(%s)" % (type_name, fields) + + return result, result_indexed + +allowed_typed_data_properties = ["types", "primaryType", "domain", "message"] + +def sanitize_typed_data(data: dict): + """ + Removes properties from a message object that are not defined per EIP-712 + + data - typed message object + """ + sanitized_data = {} + for key in allowed_typed_data_properties: + val = data.get(key) + if val is None: + continue + sanitized_data[key] = val + + if "types" in sanitized_data: + sanitized_data["types"] = { "EIP712Domain": [], **sanitized_data["types"] } + + return sanitized_data + +def is_array(type_name: str) -> bool: + if type_name: + return type_name[len(type_name) - 1] == ']' + + return False + + +def typeof_array(type_name) -> str: + return type_name[:type_name.rindex('[')] + + +def parse_number(arg): + if isinstance(arg, str): + return int(arg, 16) + elif isinstance(arg, int): + return arg + + raise ValueError("arg is not a number") + + +def parse_type_n(type_name): + """Parse N from type""" + accum = [] + for c in type_name: + if c.isdigit(): + accum.append(c) + else: + accum = [] + + # join collected digits into a number + return int("".join(accum)) + + +def parse_array_n(type_name: str): + """Parse N in type[] where "type" can itself be an array type.""" + if type_name.endswith("[]"): + return "dynamic" + + start_idx = type_name.rindex('[')+1 + end_idx = len(type_name) - 1 + + return int(type_name[start_idx:end_idx]) + +def encode_value(type_name: str, value) -> bytes: + if type_name.startswith("uint"): + size = parse_type_n(type_name) + + if (not size % 8 == 0) or (size < 8) or (size > 256): + raise ValueError("invalid uint width: %d" % size) + + value = parse_number(value) + if value.bit_length() > size: + raise ValueError("supplied uint exceeds width: %d > %d" % (value.bit_length(), size)) + if value < 0: + raise ValueError("supplied uint is negative") + elif type_name.startswith("int"): + size = parse_type_n(type_name) + + if (not size % 8 == 0) or (size < 8) or (size > 256): + raise ValueError("invalid int width: %d" % size) + + value = parse_number(value) + if value.bit_length() > size: + raise ValueError("supplied int exceeds width: %d > %d" % (value.bit_length(), size)) + + return encode_single_packed(type_name, value) + # ====== Client functions ====== # @@ -88,6 +234,131 @@ def sign_message(client, n, message): message = normalize_nfc(message) return client.call(messages.EthereumSignMessage(address_n=n, message=message)) +@expect(messages.EthereumTypedDataRequest) +def sign_typed_data(client, n, use_v4, data_string): + data = json.loads(data_string) + data = sanitize_typed_data(data) + + _, domain_types = encode_type("EIP712Domain", data["types"]) + _, message_types = encode_type(data["primaryType"], data["types"]) + + request = messages.EthereumSignTypedData(address_n=n, use_v4=use_v4) + response = client.call(request) + + message_types_keys = list(message_types.keys()) + while len(response.member_path) > 0: + root_index = response.member_path[0] + type_index = response.member_path[1] if len(response.member_path) > 1 else None + if root_index == 0: + if response.expect_type and (type_index > 0): + client.cancel() + raise ValueError("unexpected type_index when requesting domain type") + + member_typename = "EIP712Domain" + member_types = domain_types + member_data = data["domain"] + elif root_index == 1: + if response.expect_type: + # when device expects type, the path [1, x] points to element x in types linear layout + member_typename = message_types_keys[type_index] if type_index < len(message_types_keys) else None + else: + # when device expects value, the path [1, x] points to field x inside primaryType. + member_typename = data["primaryType"] + member_types = message_types + member_data = data["message"] + else: + client.cancel() + raise ValueError("unknown root") + + if response.expect_type: + member_name = None + for index in response.member_path[2:]: + member_def = member_types[member_typename][index] + member_name = member_def["name"] + member_typename = member_def["type"] + + request = messages.EthereumTypedDataAck( + member_name=member_name, + member_type=member_typename, + member_value=None, + ) + + if is_array(member_typename): + array_type = typeof_array(member_typename) + is_struct = array_type in member_types + if is_struct: + array_size = parse_array_n(member_typename) + if array_size == "dynamic": + request.member_array_n = 0 + else: + request.member_array_n = array_size + else: + is_struct = member_typename in member_types + if is_struct: + request.member_children = len(member_types[member_typename]) + + response = client.call(request) + + else: + array_size = None + for index in response.member_path[1:]: + if array_size is None: + member_def = member_types[member_typename][index] + member_data = member_data[member_def["name"]] + member_typename = member_def["type"] + + if is_array(member_typename): + array_size = parse_array_n(member_typename) + member_typename = typeof_array(member_typename) + else: + if array_size != "dynamic": + if index > array_size - 1: + raise ValueError("array offset out of bounds") + + # in array index offsets the array data, not type + member_data = member_data[index] + + # in array, there is an array + if is_array(member_typename): + array_size = parse_array_n(member_typename) + member_typename = typeof_array(member_typename) + else: + # looking at a plain type now + array_size = None + + request = messages.EthereumTypedDataAck( + member_type=member_typename, + ) + + if array_size: + # strip arrays from type to see if it's a struct + base_type = member_typename + while is_array(base_type): + base_type = typeof_array(base_type) + + # is it? + is_struct = base_type in member_types + if is_struct: + request.member_type = base_type + + if array_size == "dynamic": + request.member_array_n = len(member_data) + else: + request.member_array_n = array_size + else: + # primitive type, pass it as-is + request.member_type = member_typename + request.member_value = encode_value(member_typename, member_data) + else: + # not in array + is_struct = member_typename in member_types + request.member_type = member_typename + if not is_struct: + request.member_value = encode_value(member_typename, member_data) + + response = client.call(request) + + return response def verify_message(client, address, signature, message): message = normalize_nfc(message) diff --git a/python/src/trezorlib/messages/EthereumSignTypedData.py b/python/src/trezorlib/messages/EthereumSignTypedData.py new file mode 100644 index 00000000000..71f01f10745 --- /dev/null +++ b/python/src/trezorlib/messages/EthereumSignTypedData.py @@ -0,0 +1,30 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List, Optional # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class EthereumSignTypedData(p.MessageType): + MESSAGE_WIRE_TYPE = 464 + + def __init__( + self, + *, + address_n: Optional[List[int]] = None, + use_v4: bool = True, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.use_v4 = use_v4 + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('use_v4', p.BoolType, True), # default=true + } diff --git a/python/src/trezorlib/messages/EthereumTypedDataAck.py b/python/src/trezorlib/messages/EthereumTypedDataAck.py new file mode 100644 index 00000000000..a3c3b5d708a --- /dev/null +++ b/python/src/trezorlib/messages/EthereumTypedDataAck.py @@ -0,0 +1,39 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List, Optional # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class EthereumTypedDataAck(p.MessageType): + MESSAGE_WIRE_TYPE = 466 + + def __init__( + self, + *, + member_name: Optional[str] = None, + member_type: Optional[str] = None, + member_array_n: Optional[int] = None, + member_children: Optional[int] = None, + member_value: Optional[bytes] = None, + ) -> None: + self.member_name = member_name + self.member_type = member_type + self.member_array_n = member_array_n + self.member_children = member_children + self.member_value = member_value + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('member_name', p.UnicodeType, None), + 2: ('member_type', p.UnicodeType, None), + 3: ('member_array_n', p.UVarintType, None), + 4: ('member_children', p.UVarintType, None), + 5: ('member_value', p.BytesType, None), + } diff --git a/python/src/trezorlib/messages/EthereumTypedDataRequest.py b/python/src/trezorlib/messages/EthereumTypedDataRequest.py new file mode 100644 index 00000000000..1e593977944 --- /dev/null +++ b/python/src/trezorlib/messages/EthereumTypedDataRequest.py @@ -0,0 +1,36 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List, Optional # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class EthereumTypedDataRequest(p.MessageType): + MESSAGE_WIRE_TYPE = 465 + + def __init__( + self, + *, + member_path: Optional[List[int]] = None, + expect_type: Optional[bool] = None, + signature: Optional[bytes] = None, + address: Optional[str] = None, + ) -> None: + self.member_path = member_path if member_path is not None else [] + self.expect_type = expect_type + self.signature = signature + self.address = address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('member_path', p.UVarintType, p.FLAG_REPEATED), + 2: ('expect_type', p.BoolType, None), + 3: ('signature', p.BytesType, None), + 4: ('address', p.UnicodeType, None), + } diff --git a/python/src/trezorlib/messages/MessageType.py b/python/src/trezorlib/messages/MessageType.py index 689ca500b7f..578137bbd71 100644 --- a/python/src/trezorlib/messages/MessageType.py +++ b/python/src/trezorlib/messages/MessageType.py @@ -99,6 +99,9 @@ EthereumSignMessage: Literal[64] = 64 EthereumVerifyMessage: Literal[65] = 65 EthereumMessageSignature: Literal[66] = 66 +EthereumSignTypedData: Literal[464] = 464 +EthereumTypedDataRequest: Literal[465] = 465 +EthereumTypedDataAck: Literal[466] = 466 NEMGetAddress: Literal[67] = 67 NEMAddress: Literal[68] = 68 NEMSignTx: Literal[69] = 69 From 0135ed9c0bdb61d9586719b3c31fd205d9843bdd Mon Sep 17 00:00:00 2001 From: Maxim Kupriianov Date: Tue, 13 Apr 2021 00:08:36 +0300 Subject: [PATCH 2/3] make protobuf --- core/src/trezor/messages/MessageType.py | 3 + python/src/trezorlib/messages/__init__.py | 336 ++++++++++++++++++++++ 2 files changed, 339 insertions(+) diff --git a/core/src/trezor/messages/MessageType.py b/core/src/trezor/messages/MessageType.py index cdfacef271b..01d92c026f0 100644 --- a/core/src/trezor/messages/MessageType.py +++ b/core/src/trezor/messages/MessageType.py @@ -102,6 +102,9 @@ EthereumSignMessage: Literal[64] = 64 EthereumVerifyMessage: Literal[65] = 65 EthereumMessageSignature: Literal[66] = 66 + EthereumSignTypedData: Literal[464] = 464 + EthereumTypedDataRequest: Literal[465] = 465 + EthereumTypedDataAck: Literal[466] = 466 NEMGetAddress: Literal[67] = 67 NEMAddress: Literal[68] = 68 NEMSignTx: Literal[69] = 69 diff --git a/python/src/trezorlib/messages/__init__.py b/python/src/trezorlib/messages/__init__.py index 5a0f143d4eb..1a7788eed01 100644 --- a/python/src/trezorlib/messages/__init__.py +++ b/python/src/trezorlib/messages/__init__.py @@ -2,332 +2,668 @@ # fmt: off from .Address import Address +from .Address import Address +from .ApplyFlags import ApplyFlags from .ApplyFlags import ApplyFlags from .ApplySettings import ApplySettings +from .ApplySettings import ApplySettings from .AuthorizeCoinJoin import AuthorizeCoinJoin +from .AuthorizeCoinJoin import AuthorizeCoinJoin +from .BackupDevice import BackupDevice from .BackupDevice import BackupDevice from .BinanceAddress import BinanceAddress +from .BinanceAddress import BinanceAddress from .BinanceCancelMsg import BinanceCancelMsg +from .BinanceCancelMsg import BinanceCancelMsg +from .BinanceCoin import BinanceCoin from .BinanceCoin import BinanceCoin from .BinanceGetAddress import BinanceGetAddress +from .BinanceGetAddress import BinanceGetAddress from .BinanceGetPublicKey import BinanceGetPublicKey +from .BinanceGetPublicKey import BinanceGetPublicKey +from .BinanceInputOutput import BinanceInputOutput from .BinanceInputOutput import BinanceInputOutput from .BinanceOrderMsg import BinanceOrderMsg +from .BinanceOrderMsg import BinanceOrderMsg +from .BinancePublicKey import BinancePublicKey from .BinancePublicKey import BinancePublicKey from .BinanceSignTx import BinanceSignTx +from .BinanceSignTx import BinanceSignTx from .BinanceSignedTx import BinanceSignedTx +from .BinanceSignedTx import BinanceSignedTx +from .BinanceTransferMsg import BinanceTransferMsg from .BinanceTransferMsg import BinanceTransferMsg from .BinanceTxRequest import BinanceTxRequest +from .BinanceTxRequest import BinanceTxRequest +from .ButtonAck import ButtonAck from .ButtonAck import ButtonAck from .ButtonRequest import ButtonRequest +from .ButtonRequest import ButtonRequest from .Cancel import Cancel +from .Cancel import Cancel +from .CancelAuthorization import CancelAuthorization from .CancelAuthorization import CancelAuthorization from .CardanoAddress import CardanoAddress +from .CardanoAddress import CardanoAddress +from .CardanoAddressParametersType import CardanoAddressParametersType from .CardanoAddressParametersType import CardanoAddressParametersType from .CardanoAssetGroupType import CardanoAssetGroupType +from .CardanoAssetGroupType import CardanoAssetGroupType from .CardanoBlockchainPointerType import CardanoBlockchainPointerType +from .CardanoBlockchainPointerType import CardanoBlockchainPointerType +from .CardanoGetAddress import CardanoGetAddress from .CardanoGetAddress import CardanoGetAddress from .CardanoGetPublicKey import CardanoGetPublicKey +from .CardanoGetPublicKey import CardanoGetPublicKey from .CardanoPoolMetadataType import CardanoPoolMetadataType +from .CardanoPoolMetadataType import CardanoPoolMetadataType +from .CardanoPoolOwnerType import CardanoPoolOwnerType from .CardanoPoolOwnerType import CardanoPoolOwnerType from .CardanoPoolParametersType import CardanoPoolParametersType +from .CardanoPoolParametersType import CardanoPoolParametersType from .CardanoPoolRelayParametersType import CardanoPoolRelayParametersType +from .CardanoPoolRelayParametersType import CardanoPoolRelayParametersType +from .CardanoPublicKey import CardanoPublicKey from .CardanoPublicKey import CardanoPublicKey from .CardanoSignTx import CardanoSignTx +from .CardanoSignTx import CardanoSignTx +from .CardanoSignedTx import CardanoSignedTx from .CardanoSignedTx import CardanoSignedTx from .CardanoSignedTxChunk import CardanoSignedTxChunk +from .CardanoSignedTxChunk import CardanoSignedTxChunk from .CardanoSignedTxChunkAck import CardanoSignedTxChunkAck +from .CardanoSignedTxChunkAck import CardanoSignedTxChunkAck +from .CardanoTokenType import CardanoTokenType from .CardanoTokenType import CardanoTokenType from .CardanoTxCertificateType import CardanoTxCertificateType +from .CardanoTxCertificateType import CardanoTxCertificateType +from .CardanoTxInputType import CardanoTxInputType from .CardanoTxInputType import CardanoTxInputType from .CardanoTxOutputType import CardanoTxOutputType +from .CardanoTxOutputType import CardanoTxOutputType from .CardanoTxWithdrawalType import CardanoTxWithdrawalType +from .CardanoTxWithdrawalType import CardanoTxWithdrawalType +from .ChangePin import ChangePin from .ChangePin import ChangePin from .ChangeWipeCode import ChangeWipeCode +from .ChangeWipeCode import ChangeWipeCode +from .CipherKeyValue import CipherKeyValue from .CipherKeyValue import CipherKeyValue from .CipheredKeyValue import CipheredKeyValue +from .CipheredKeyValue import CipheredKeyValue from .CosiCommit import CosiCommit +from .CosiCommit import CosiCommit +from .CosiCommitment import CosiCommitment from .CosiCommitment import CosiCommitment from .CosiSign import CosiSign +from .CosiSign import CosiSign from .CosiSignature import CosiSignature +from .CosiSignature import CosiSignature +from .DebugLinkDecision import DebugLinkDecision from .DebugLinkDecision import DebugLinkDecision from .DebugLinkEraseSdCard import DebugLinkEraseSdCard +from .DebugLinkEraseSdCard import DebugLinkEraseSdCard from .DebugLinkFlashErase import DebugLinkFlashErase +from .DebugLinkFlashErase import DebugLinkFlashErase +from .DebugLinkGetState import DebugLinkGetState from .DebugLinkGetState import DebugLinkGetState from .DebugLinkLayout import DebugLinkLayout +from .DebugLinkLayout import DebugLinkLayout +from .DebugLinkLog import DebugLinkLog from .DebugLinkLog import DebugLinkLog from .DebugLinkMemory import DebugLinkMemory +from .DebugLinkMemory import DebugLinkMemory from .DebugLinkMemoryRead import DebugLinkMemoryRead +from .DebugLinkMemoryRead import DebugLinkMemoryRead +from .DebugLinkMemoryWrite import DebugLinkMemoryWrite from .DebugLinkMemoryWrite import DebugLinkMemoryWrite from .DebugLinkRecordScreen import DebugLinkRecordScreen +from .DebugLinkRecordScreen import DebugLinkRecordScreen +from .DebugLinkReseedRandom import DebugLinkReseedRandom from .DebugLinkReseedRandom import DebugLinkReseedRandom from .DebugLinkState import DebugLinkState +from .DebugLinkState import DebugLinkState from .DebugLinkStop import DebugLinkStop +from .DebugLinkStop import DebugLinkStop +from .DebugLinkWatchLayout import DebugLinkWatchLayout from .DebugLinkWatchLayout import DebugLinkWatchLayout from .DebugMoneroDiagAck import DebugMoneroDiagAck +from .DebugMoneroDiagAck import DebugMoneroDiagAck +from .DebugMoneroDiagRequest import DebugMoneroDiagRequest from .DebugMoneroDiagRequest import DebugMoneroDiagRequest from .Deprecated_PassphraseStateAck import Deprecated_PassphraseStateAck +from .Deprecated_PassphraseStateAck import Deprecated_PassphraseStateAck from .Deprecated_PassphraseStateRequest import Deprecated_PassphraseStateRequest +from .Deprecated_PassphraseStateRequest import Deprecated_PassphraseStateRequest +from .DoPreauthorized import DoPreauthorized from .DoPreauthorized import DoPreauthorized from .ECDHSessionKey import ECDHSessionKey +from .ECDHSessionKey import ECDHSessionKey +from .EndSession import EndSession from .EndSession import EndSession from .Entropy import Entropy +from .Entropy import Entropy from .EntropyAck import EntropyAck +from .EntropyAck import EntropyAck +from .EntropyRequest import EntropyRequest from .EntropyRequest import EntropyRequest from .EosActionBuyRam import EosActionBuyRam +from .EosActionBuyRam import EosActionBuyRam +from .EosActionBuyRamBytes import EosActionBuyRamBytes from .EosActionBuyRamBytes import EosActionBuyRamBytes from .EosActionCommon import EosActionCommon +from .EosActionCommon import EosActionCommon from .EosActionDelegate import EosActionDelegate +from .EosActionDelegate import EosActionDelegate +from .EosActionDeleteAuth import EosActionDeleteAuth from .EosActionDeleteAuth import EosActionDeleteAuth from .EosActionLinkAuth import EosActionLinkAuth +from .EosActionLinkAuth import EosActionLinkAuth +from .EosActionNewAccount import EosActionNewAccount from .EosActionNewAccount import EosActionNewAccount from .EosActionRefund import EosActionRefund +from .EosActionRefund import EosActionRefund from .EosActionSellRam import EosActionSellRam +from .EosActionSellRam import EosActionSellRam +from .EosActionTransfer import EosActionTransfer from .EosActionTransfer import EosActionTransfer from .EosActionUndelegate import EosActionUndelegate +from .EosActionUndelegate import EosActionUndelegate +from .EosActionUnknown import EosActionUnknown from .EosActionUnknown import EosActionUnknown from .EosActionUnlinkAuth import EosActionUnlinkAuth +from .EosActionUnlinkAuth import EosActionUnlinkAuth from .EosActionUpdateAuth import EosActionUpdateAuth +from .EosActionUpdateAuth import EosActionUpdateAuth +from .EosActionVoteProducer import EosActionVoteProducer from .EosActionVoteProducer import EosActionVoteProducer from .EosAsset import EosAsset +from .EosAsset import EosAsset from .EosAuthorization import EosAuthorization +from .EosAuthorization import EosAuthorization +from .EosAuthorizationAccount import EosAuthorizationAccount from .EosAuthorizationAccount import EosAuthorizationAccount from .EosAuthorizationKey import EosAuthorizationKey +from .EosAuthorizationKey import EosAuthorizationKey from .EosAuthorizationWait import EosAuthorizationWait +from .EosAuthorizationWait import EosAuthorizationWait +from .EosGetPublicKey import EosGetPublicKey from .EosGetPublicKey import EosGetPublicKey from .EosPermissionLevel import EosPermissionLevel +from .EosPermissionLevel import EosPermissionLevel +from .EosPublicKey import EosPublicKey from .EosPublicKey import EosPublicKey from .EosSignTx import EosSignTx +from .EosSignTx import EosSignTx from .EosSignedTx import EosSignedTx +from .EosSignedTx import EosSignedTx +from .EosTxActionAck import EosTxActionAck from .EosTxActionAck import EosTxActionAck from .EosTxActionRequest import EosTxActionRequest +from .EosTxActionRequest import EosTxActionRequest +from .EosTxHeader import EosTxHeader from .EosTxHeader import EosTxHeader from .EthereumAddress import EthereumAddress +from .EthereumAddress import EthereumAddress from .EthereumGetAddress import EthereumGetAddress +from .EthereumGetAddress import EthereumGetAddress +from .EthereumGetPublicKey import EthereumGetPublicKey from .EthereumGetPublicKey import EthereumGetPublicKey from .EthereumMessageSignature import EthereumMessageSignature +from .EthereumMessageSignature import EthereumMessageSignature +from .EthereumPublicKey import EthereumPublicKey from .EthereumPublicKey import EthereumPublicKey from .EthereumSignMessage import EthereumSignMessage +from .EthereumSignMessage import EthereumSignMessage from .EthereumSignTx import EthereumSignTx +from .EthereumSignTx import EthereumSignTx +from .EthereumSignTypedData import EthereumSignTypedData +from .EthereumSignTypedData import EthereumSignTypedData +from .EthereumTxAck import EthereumTxAck from .EthereumTxAck import EthereumTxAck from .EthereumTxRequest import EthereumTxRequest +from .EthereumTxRequest import EthereumTxRequest +from .EthereumTypedDataAck import EthereumTypedDataAck +from .EthereumTypedDataAck import EthereumTypedDataAck +from .EthereumTypedDataRequest import EthereumTypedDataRequest +from .EthereumTypedDataRequest import EthereumTypedDataRequest +from .EthereumVerifyMessage import EthereumVerifyMessage from .EthereumVerifyMessage import EthereumVerifyMessage from .Failure import Failure +from .Failure import Failure from .Features import Features +from .Features import Features +from .FirmwareErase import FirmwareErase from .FirmwareErase import FirmwareErase from .FirmwareRequest import FirmwareRequest +from .FirmwareRequest import FirmwareRequest +from .FirmwareUpload import FirmwareUpload from .FirmwareUpload import FirmwareUpload from .GetAddress import GetAddress +from .GetAddress import GetAddress from .GetECDHSessionKey import GetECDHSessionKey +from .GetECDHSessionKey import GetECDHSessionKey +from .GetEntropy import GetEntropy from .GetEntropy import GetEntropy from .GetFeatures import GetFeatures +from .GetFeatures import GetFeatures +from .GetNextU2FCounter import GetNextU2FCounter from .GetNextU2FCounter import GetNextU2FCounter from .GetOwnershipId import GetOwnershipId +from .GetOwnershipId import GetOwnershipId from .GetOwnershipProof import GetOwnershipProof +from .GetOwnershipProof import GetOwnershipProof +from .GetPublicKey import GetPublicKey from .GetPublicKey import GetPublicKey from .HDNodePathType import HDNodePathType +from .HDNodePathType import HDNodePathType +from .HDNodeType import HDNodeType from .HDNodeType import HDNodeType from .IdentityType import IdentityType +from .IdentityType import IdentityType from .Initialize import Initialize +from .Initialize import Initialize +from .LiskAddress import LiskAddress from .LiskAddress import LiskAddress from .LiskDelegateType import LiskDelegateType +from .LiskDelegateType import LiskDelegateType from .LiskGetAddress import LiskGetAddress +from .LiskGetAddress import LiskGetAddress +from .LiskGetPublicKey import LiskGetPublicKey from .LiskGetPublicKey import LiskGetPublicKey from .LiskMessageSignature import LiskMessageSignature +from .LiskMessageSignature import LiskMessageSignature from .LiskMultisignatureType import LiskMultisignatureType +from .LiskMultisignatureType import LiskMultisignatureType +from .LiskPublicKey import LiskPublicKey from .LiskPublicKey import LiskPublicKey from .LiskSignMessage import LiskSignMessage +from .LiskSignMessage import LiskSignMessage +from .LiskSignTx import LiskSignTx from .LiskSignTx import LiskSignTx from .LiskSignatureType import LiskSignatureType +from .LiskSignatureType import LiskSignatureType from .LiskSignedTx import LiskSignedTx +from .LiskSignedTx import LiskSignedTx +from .LiskTransactionAsset import LiskTransactionAsset from .LiskTransactionAsset import LiskTransactionAsset from .LiskTransactionCommon import LiskTransactionCommon +from .LiskTransactionCommon import LiskTransactionCommon +from .LiskVerifyMessage import LiskVerifyMessage from .LiskVerifyMessage import LiskVerifyMessage from .LoadDevice import LoadDevice +from .LoadDevice import LoadDevice from .LockDevice import LockDevice +from .LockDevice import LockDevice +from .MessageSignature import MessageSignature from .MessageSignature import MessageSignature from .MoneroAccountPublicAddress import MoneroAccountPublicAddress +from .MoneroAccountPublicAddress import MoneroAccountPublicAddress +from .MoneroAddress import MoneroAddress from .MoneroAddress import MoneroAddress from .MoneroExportedKeyImage import MoneroExportedKeyImage +from .MoneroExportedKeyImage import MoneroExportedKeyImage from .MoneroGetAddress import MoneroGetAddress +from .MoneroGetAddress import MoneroGetAddress +from .MoneroGetTxKeyAck import MoneroGetTxKeyAck from .MoneroGetTxKeyAck import MoneroGetTxKeyAck from .MoneroGetTxKeyRequest import MoneroGetTxKeyRequest +from .MoneroGetTxKeyRequest import MoneroGetTxKeyRequest +from .MoneroGetWatchKey import MoneroGetWatchKey from .MoneroGetWatchKey import MoneroGetWatchKey from .MoneroKeyImageExportInitAck import MoneroKeyImageExportInitAck +from .MoneroKeyImageExportInitAck import MoneroKeyImageExportInitAck from .MoneroKeyImageExportInitRequest import MoneroKeyImageExportInitRequest +from .MoneroKeyImageExportInitRequest import MoneroKeyImageExportInitRequest +from .MoneroKeyImageSyncFinalAck import MoneroKeyImageSyncFinalAck from .MoneroKeyImageSyncFinalAck import MoneroKeyImageSyncFinalAck from .MoneroKeyImageSyncFinalRequest import MoneroKeyImageSyncFinalRequest +from .MoneroKeyImageSyncFinalRequest import MoneroKeyImageSyncFinalRequest +from .MoneroKeyImageSyncStepAck import MoneroKeyImageSyncStepAck from .MoneroKeyImageSyncStepAck import MoneroKeyImageSyncStepAck from .MoneroKeyImageSyncStepRequest import MoneroKeyImageSyncStepRequest +from .MoneroKeyImageSyncStepRequest import MoneroKeyImageSyncStepRequest from .MoneroLiveRefreshFinalAck import MoneroLiveRefreshFinalAck +from .MoneroLiveRefreshFinalAck import MoneroLiveRefreshFinalAck +from .MoneroLiveRefreshFinalRequest import MoneroLiveRefreshFinalRequest from .MoneroLiveRefreshFinalRequest import MoneroLiveRefreshFinalRequest from .MoneroLiveRefreshStartAck import MoneroLiveRefreshStartAck +from .MoneroLiveRefreshStartAck import MoneroLiveRefreshStartAck +from .MoneroLiveRefreshStartRequest import MoneroLiveRefreshStartRequest from .MoneroLiveRefreshStartRequest import MoneroLiveRefreshStartRequest from .MoneroLiveRefreshStepAck import MoneroLiveRefreshStepAck +from .MoneroLiveRefreshStepAck import MoneroLiveRefreshStepAck from .MoneroLiveRefreshStepRequest import MoneroLiveRefreshStepRequest +from .MoneroLiveRefreshStepRequest import MoneroLiveRefreshStepRequest +from .MoneroMultisigKLRki import MoneroMultisigKLRki from .MoneroMultisigKLRki import MoneroMultisigKLRki from .MoneroOutputEntry import MoneroOutputEntry +from .MoneroOutputEntry import MoneroOutputEntry +from .MoneroRctKeyPublic import MoneroRctKeyPublic from .MoneroRctKeyPublic import MoneroRctKeyPublic from .MoneroRingCtSig import MoneroRingCtSig +from .MoneroRingCtSig import MoneroRingCtSig from .MoneroSubAddressIndicesList import MoneroSubAddressIndicesList +from .MoneroSubAddressIndicesList import MoneroSubAddressIndicesList +from .MoneroTransactionAllInputsSetAck import MoneroTransactionAllInputsSetAck from .MoneroTransactionAllInputsSetAck import MoneroTransactionAllInputsSetAck from .MoneroTransactionAllInputsSetRequest import MoneroTransactionAllInputsSetRequest +from .MoneroTransactionAllInputsSetRequest import MoneroTransactionAllInputsSetRequest from .MoneroTransactionAllOutSetAck import MoneroTransactionAllOutSetAck +from .MoneroTransactionAllOutSetAck import MoneroTransactionAllOutSetAck +from .MoneroTransactionAllOutSetRequest import MoneroTransactionAllOutSetRequest from .MoneroTransactionAllOutSetRequest import MoneroTransactionAllOutSetRequest from .MoneroTransactionData import MoneroTransactionData +from .MoneroTransactionData import MoneroTransactionData from .MoneroTransactionDestinationEntry import MoneroTransactionDestinationEntry +from .MoneroTransactionDestinationEntry import MoneroTransactionDestinationEntry +from .MoneroTransactionFinalAck import MoneroTransactionFinalAck from .MoneroTransactionFinalAck import MoneroTransactionFinalAck from .MoneroTransactionFinalRequest import MoneroTransactionFinalRequest +from .MoneroTransactionFinalRequest import MoneroTransactionFinalRequest +from .MoneroTransactionInitAck import MoneroTransactionInitAck from .MoneroTransactionInitAck import MoneroTransactionInitAck from .MoneroTransactionInitRequest import MoneroTransactionInitRequest +from .MoneroTransactionInitRequest import MoneroTransactionInitRequest from .MoneroTransactionInputViniAck import MoneroTransactionInputViniAck +from .MoneroTransactionInputViniAck import MoneroTransactionInputViniAck +from .MoneroTransactionInputViniRequest import MoneroTransactionInputViniRequest from .MoneroTransactionInputViniRequest import MoneroTransactionInputViniRequest from .MoneroTransactionInputsPermutationAck import MoneroTransactionInputsPermutationAck +from .MoneroTransactionInputsPermutationAck import MoneroTransactionInputsPermutationAck +from .MoneroTransactionInputsPermutationRequest import MoneroTransactionInputsPermutationRequest from .MoneroTransactionInputsPermutationRequest import MoneroTransactionInputsPermutationRequest from .MoneroTransactionRsigData import MoneroTransactionRsigData +from .MoneroTransactionRsigData import MoneroTransactionRsigData from .MoneroTransactionSetInputAck import MoneroTransactionSetInputAck +from .MoneroTransactionSetInputAck import MoneroTransactionSetInputAck +from .MoneroTransactionSetInputRequest import MoneroTransactionSetInputRequest from .MoneroTransactionSetInputRequest import MoneroTransactionSetInputRequest from .MoneroTransactionSetOutputAck import MoneroTransactionSetOutputAck +from .MoneroTransactionSetOutputAck import MoneroTransactionSetOutputAck +from .MoneroTransactionSetOutputRequest import MoneroTransactionSetOutputRequest from .MoneroTransactionSetOutputRequest import MoneroTransactionSetOutputRequest from .MoneroTransactionSignInputAck import MoneroTransactionSignInputAck +from .MoneroTransactionSignInputAck import MoneroTransactionSignInputAck from .MoneroTransactionSignInputRequest import MoneroTransactionSignInputRequest +from .MoneroTransactionSignInputRequest import MoneroTransactionSignInputRequest +from .MoneroTransactionSourceEntry import MoneroTransactionSourceEntry from .MoneroTransactionSourceEntry import MoneroTransactionSourceEntry from .MoneroTransferDetails import MoneroTransferDetails +from .MoneroTransferDetails import MoneroTransferDetails from .MoneroWatchKey import MoneroWatchKey +from .MoneroWatchKey import MoneroWatchKey +from .MultisigRedeemScriptType import MultisigRedeemScriptType from .MultisigRedeemScriptType import MultisigRedeemScriptType from .NEMAddress import NEMAddress +from .NEMAddress import NEMAddress from .NEMAggregateModification import NEMAggregateModification +from .NEMAggregateModification import NEMAggregateModification +from .NEMCosignatoryModification import NEMCosignatoryModification from .NEMCosignatoryModification import NEMCosignatoryModification from .NEMDecryptMessage import NEMDecryptMessage +from .NEMDecryptMessage import NEMDecryptMessage +from .NEMDecryptedMessage import NEMDecryptedMessage from .NEMDecryptedMessage import NEMDecryptedMessage from .NEMGetAddress import NEMGetAddress +from .NEMGetAddress import NEMGetAddress from .NEMImportanceTransfer import NEMImportanceTransfer +from .NEMImportanceTransfer import NEMImportanceTransfer +from .NEMMosaic import NEMMosaic from .NEMMosaic import NEMMosaic from .NEMMosaicCreation import NEMMosaicCreation +from .NEMMosaicCreation import NEMMosaicCreation +from .NEMMosaicDefinition import NEMMosaicDefinition from .NEMMosaicDefinition import NEMMosaicDefinition from .NEMMosaicSupplyChange import NEMMosaicSupplyChange +from .NEMMosaicSupplyChange import NEMMosaicSupplyChange from .NEMProvisionNamespace import NEMProvisionNamespace +from .NEMProvisionNamespace import NEMProvisionNamespace +from .NEMSignTx import NEMSignTx from .NEMSignTx import NEMSignTx from .NEMSignedTx import NEMSignedTx +from .NEMSignedTx import NEMSignedTx +from .NEMTransactionCommon import NEMTransactionCommon from .NEMTransactionCommon import NEMTransactionCommon from .NEMTransfer import NEMTransfer +from .NEMTransfer import NEMTransfer from .NextU2FCounter import NextU2FCounter +from .NextU2FCounter import NextU2FCounter +from .OwnershipId import OwnershipId from .OwnershipId import OwnershipId from .OwnershipProof import OwnershipProof +from .OwnershipProof import OwnershipProof from .PassphraseAck import PassphraseAck +from .PassphraseAck import PassphraseAck +from .PassphraseRequest import PassphraseRequest from .PassphraseRequest import PassphraseRequest from .PinMatrixAck import PinMatrixAck +from .PinMatrixAck import PinMatrixAck from .PinMatrixRequest import PinMatrixRequest +from .PinMatrixRequest import PinMatrixRequest +from .Ping import Ping from .Ping import Ping from .PreauthorizedRequest import PreauthorizedRequest +from .PreauthorizedRequest import PreauthorizedRequest +from .PrevInput import PrevInput from .PrevInput import PrevInput from .PrevOutput import PrevOutput +from .PrevOutput import PrevOutput from .PrevTx import PrevTx +from .PrevTx import PrevTx +from .PublicKey import PublicKey from .PublicKey import PublicKey from .RebootToBootloader import RebootToBootloader +from .RebootToBootloader import RebootToBootloader +from .RecoveryDevice import RecoveryDevice from .RecoveryDevice import RecoveryDevice from .ResetDevice import ResetDevice +from .ResetDevice import ResetDevice from .RippleAddress import RippleAddress +from .RippleAddress import RippleAddress +from .RippleGetAddress import RippleGetAddress from .RippleGetAddress import RippleGetAddress from .RipplePayment import RipplePayment +from .RipplePayment import RipplePayment +from .RippleSignTx import RippleSignTx from .RippleSignTx import RippleSignTx from .RippleSignedTx import RippleSignedTx +from .RippleSignedTx import RippleSignedTx from .SdProtect import SdProtect +from .SdProtect import SdProtect +from .SelfTest import SelfTest from .SelfTest import SelfTest from .SetU2FCounter import SetU2FCounter +from .SetU2FCounter import SetU2FCounter +from .SignIdentity import SignIdentity from .SignIdentity import SignIdentity from .SignMessage import SignMessage +from .SignMessage import SignMessage from .SignTx import SignTx +from .SignTx import SignTx +from .SignedIdentity import SignedIdentity from .SignedIdentity import SignedIdentity from .StellarAccountMergeOp import StellarAccountMergeOp +from .StellarAccountMergeOp import StellarAccountMergeOp +from .StellarAddress import StellarAddress from .StellarAddress import StellarAddress from .StellarAllowTrustOp import StellarAllowTrustOp +from .StellarAllowTrustOp import StellarAllowTrustOp from .StellarAssetType import StellarAssetType +from .StellarAssetType import StellarAssetType +from .StellarBumpSequenceOp import StellarBumpSequenceOp from .StellarBumpSequenceOp import StellarBumpSequenceOp from .StellarChangeTrustOp import StellarChangeTrustOp +from .StellarChangeTrustOp import StellarChangeTrustOp +from .StellarCreateAccountOp import StellarCreateAccountOp from .StellarCreateAccountOp import StellarCreateAccountOp from .StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp +from .StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp from .StellarGetAddress import StellarGetAddress +from .StellarGetAddress import StellarGetAddress +from .StellarManageDataOp import StellarManageDataOp from .StellarManageDataOp import StellarManageDataOp from .StellarManageOfferOp import StellarManageOfferOp +from .StellarManageOfferOp import StellarManageOfferOp +from .StellarPathPaymentOp import StellarPathPaymentOp from .StellarPathPaymentOp import StellarPathPaymentOp from .StellarPaymentOp import StellarPaymentOp +from .StellarPaymentOp import StellarPaymentOp from .StellarSetOptionsOp import StellarSetOptionsOp +from .StellarSetOptionsOp import StellarSetOptionsOp +from .StellarSignTx import StellarSignTx from .StellarSignTx import StellarSignTx from .StellarSignedTx import StellarSignedTx +from .StellarSignedTx import StellarSignedTx from .StellarTxOpRequest import StellarTxOpRequest +from .StellarTxOpRequest import StellarTxOpRequest +from .Success import Success from .Success import Success from .TezosAddress import TezosAddress +from .TezosAddress import TezosAddress from .TezosBallotOp import TezosBallotOp +from .TezosBallotOp import TezosBallotOp +from .TezosContractID import TezosContractID from .TezosContractID import TezosContractID from .TezosDelegationOp import TezosDelegationOp +from .TezosDelegationOp import TezosDelegationOp +from .TezosGetAddress import TezosGetAddress from .TezosGetAddress import TezosGetAddress from .TezosGetPublicKey import TezosGetPublicKey +from .TezosGetPublicKey import TezosGetPublicKey from .TezosManagerTransfer import TezosManagerTransfer +from .TezosManagerTransfer import TezosManagerTransfer +from .TezosOriginationOp import TezosOriginationOp from .TezosOriginationOp import TezosOriginationOp from .TezosParametersManager import TezosParametersManager +from .TezosParametersManager import TezosParametersManager +from .TezosProposalOp import TezosProposalOp from .TezosProposalOp import TezosProposalOp from .TezosPublicKey import TezosPublicKey +from .TezosPublicKey import TezosPublicKey from .TezosRevealOp import TezosRevealOp +from .TezosRevealOp import TezosRevealOp +from .TezosSignTx import TezosSignTx from .TezosSignTx import TezosSignTx from .TezosSignedTx import TezosSignedTx +from .TezosSignedTx import TezosSignedTx +from .TezosTransactionOp import TezosTransactionOp from .TezosTransactionOp import TezosTransactionOp from .TransactionType import TransactionType +from .TransactionType import TransactionType from .TxAck import TxAck +from .TxAck import TxAck +from .TxAckInput import TxAckInput from .TxAckInput import TxAckInput from .TxAckInputWrapper import TxAckInputWrapper +from .TxAckInputWrapper import TxAckInputWrapper +from .TxAckOutput import TxAckOutput from .TxAckOutput import TxAckOutput from .TxAckOutputWrapper import TxAckOutputWrapper +from .TxAckOutputWrapper import TxAckOutputWrapper from .TxAckPrevExtraData import TxAckPrevExtraData +from .TxAckPrevExtraData import TxAckPrevExtraData +from .TxAckPrevExtraDataWrapper import TxAckPrevExtraDataWrapper from .TxAckPrevExtraDataWrapper import TxAckPrevExtraDataWrapper from .TxAckPrevInput import TxAckPrevInput +from .TxAckPrevInput import TxAckPrevInput +from .TxAckPrevInputWrapper import TxAckPrevInputWrapper from .TxAckPrevInputWrapper import TxAckPrevInputWrapper from .TxAckPrevMeta import TxAckPrevMeta +from .TxAckPrevMeta import TxAckPrevMeta from .TxAckPrevOutput import TxAckPrevOutput +from .TxAckPrevOutput import TxAckPrevOutput +from .TxAckPrevOutputWrapper import TxAckPrevOutputWrapper from .TxAckPrevOutputWrapper import TxAckPrevOutputWrapper from .TxInput import TxInput +from .TxInput import TxInput +from .TxInputType import TxInputType from .TxInputType import TxInputType from .TxOutput import TxOutput +from .TxOutput import TxOutput from .TxOutputBinType import TxOutputBinType +from .TxOutputBinType import TxOutputBinType +from .TxOutputType import TxOutputType from .TxOutputType import TxOutputType from .TxRequest import TxRequest +from .TxRequest import TxRequest +from .TxRequestDetailsType import TxRequestDetailsType from .TxRequestDetailsType import TxRequestDetailsType from .TxRequestSerializedType import TxRequestSerializedType +from .TxRequestSerializedType import TxRequestSerializedType from .VerifyMessage import VerifyMessage +from .VerifyMessage import VerifyMessage +from .WebAuthnAddResidentCredential import WebAuthnAddResidentCredential from .WebAuthnAddResidentCredential import WebAuthnAddResidentCredential from .WebAuthnCredential import WebAuthnCredential +from .WebAuthnCredential import WebAuthnCredential from .WebAuthnCredentials import WebAuthnCredentials +from .WebAuthnCredentials import WebAuthnCredentials +from .WebAuthnListResidentCredentials import WebAuthnListResidentCredentials from .WebAuthnListResidentCredentials import WebAuthnListResidentCredentials from .WebAuthnRemoveResidentCredential import WebAuthnRemoveResidentCredential +from .WebAuthnRemoveResidentCredential import WebAuthnRemoveResidentCredential from .WipeDevice import WipeDevice +from .WipeDevice import WipeDevice +from .WordAck import WordAck from .WordAck import WordAck from .WordRequest import WordRequest +from .WordRequest import WordRequest +from . import AmountUnit from . import AmountUnit from . import BackupType +from . import BackupType from . import BinanceOrderSide +from . import BinanceOrderSide +from . import BinanceOrderType from . import BinanceOrderType from . import BinanceTimeInForce +from . import BinanceTimeInForce +from . import ButtonRequestType from . import ButtonRequestType from . import Capability +from . import Capability from . import CardanoAddressType +from . import CardanoAddressType +from . import CardanoCertificateType from . import CardanoCertificateType from . import CardanoPoolRelayType +from . import CardanoPoolRelayType +from . import DebugSwipeDirection from . import DebugSwipeDirection from . import DecredStakingSpendType +from . import DecredStakingSpendType from . import FailureType +from . import FailureType +from . import InputScriptType from . import InputScriptType from . import LiskTransactionType +from . import LiskTransactionType +from . import MessageType from . import MessageType from . import NEMImportanceTransferMode +from . import NEMImportanceTransferMode from . import NEMModificationType +from . import NEMModificationType +from . import NEMMosaicLevy from . import NEMMosaicLevy from . import NEMSupplyChangeType +from . import NEMSupplyChangeType +from . import OutputScriptType from . import OutputScriptType from . import PinMatrixRequestType +from . import PinMatrixRequestType from . import RecoveryDeviceType +from . import RecoveryDeviceType +from . import RequestType from . import RequestType from . import SafetyCheckLevel +from . import SafetyCheckLevel +from . import SdProtectOperationType from . import SdProtectOperationType from . import TezosBallotType +from . import TezosBallotType from . import TezosContractType +from . import TezosContractType +from . import WordRequestType from . import WordRequestType From 52ed7bf8cf4016a8cbcfe539557291c286d69f38 Mon Sep 17 00:00:00 2001 From: Maxim Kupriianov Date: Tue, 13 Apr 2021 14:17:40 +0300 Subject: [PATCH 3/3] Add eth_abi into pyproject deps --- poetry.lock | 143 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index bd0a650c28d..41902123479 100644 --- a/poetry.lock +++ b/poetry.lock @@ -151,6 +151,20 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "cytoolz" +version = "0.11.0" +description = "Cython implementation of Toolz: High performance functional utilities" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +toolz = ">=0.8.0" + +[package.extras] +cython = ["cython"] + [[package]] name = "dataclasses" version = "0.8" @@ -206,6 +220,79 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "eth-abi" +version = "2.1.1" +description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" +category = "main" +optional = false +python-versions = ">=3.6, <4" + +[package.dependencies] +eth-typing = ">=2.0.0,<3.0.0" +eth-utils = ">=1.2.0,<2.0.0" +parsimonious = ">=0.8.0,<0.9.0" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel", "twine", "ipython", "pytest (==4.4.1)", "pytest-pythonpath (>=0.7.1)", "pytest-xdist (==1.22.3)", "tox (>=2.9.1,<3)", "eth-hash", "hypothesis (>=3.6.1,<4)", "flake8 (==3.4.1)", "isort (>=4.2.15,<5)", "mypy (==0.701)", "pydocstyle (>=3.0.0,<4)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)", "towncrier (>=19.2.0,<20)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)", "towncrier (>=19.2.0,<20)"] +lint = ["flake8 (==3.4.1)", "isort (>=4.2.15,<5)", "mypy (==0.701)", "pydocstyle (>=3.0.0,<4)"] +test = ["pytest (==4.4.1)", "pytest-pythonpath (>=0.7.1)", "pytest-xdist (==1.22.3)", "tox (>=2.9.1,<3)", "eth-hash", "hypothesis (>=3.6.1,<4)"] +tools = ["hypothesis (>=3.6.1,<4)"] + +[[package]] +name = "eth-hash" +version = "0.3.1" +description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" +category = "main" +optional = false +python-versions = ">=3.5, <4" + +[package.dependencies] +eth-utils = ">=1,<2" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel", "twine", "ipython", "pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)", "flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.770)", "pydocstyle (>=5.0.0,<6)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<1)", "towncrier (>=19.2.0,<20)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<1)", "towncrier (>=19.2.0,<20)"] +lint = ["flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.770)", "pydocstyle (>=5.0.0,<6)"] +pycryptodome = ["pycryptodome (>=3.6.6,<4)"] +pysha3 = ["pysha3 (>=1.0.0,<2.0.0)"] +test = ["pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)"] + +[[package]] +name = "eth-typing" +version = "2.2.2" +description = "eth-typing: Common type annotations for ethereum python packages" +category = "main" +optional = false +python-versions = ">=3.5, <4" + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel", "twine", "ipython", "pytest (>=4.4,<4.5)", "pytest-xdist", "tox (>=2.9.1,<3)", "flake8 (==3.8.3)", "isort (>=4.2.15,<5)", "mypy (==0.782)", "pydocstyle (>=3.0.0,<4)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)"] +lint = ["flake8 (==3.8.3)", "isort (>=4.2.15,<5)", "mypy (==0.782)", "pydocstyle (>=3.0.0,<4)"] +test = ["pytest (>=4.4,<4.5)", "pytest-xdist", "tox (>=2.9.1,<3)"] + +[[package]] +name = "eth-utils" +version = "1.10.0" +description = "eth-utils: Common utility functions for python code that interacts with Ethereum" +category = "main" +optional = false +python-versions = ">=3.5,!=3.5.2,<4" + +[package.dependencies] +cytoolz = {version = ">=0.10.1,<1.0.0", markers = "implementation_name == \"cpython\""} +eth-hash = ">=0.3.1,<0.4.0" +eth-typing = ">=2.2.1,<3.0.0" +toolz = {version = ">0.8.2,<1", markers = "implementation_name == \"pypy\""} + +[package.extras] +dev = ["bumpversion (>=0.5.3,<1)", "pytest-watch (>=4.1.0,<5)", "wheel (>=0.30.0,<1.0.0)", "twine (>=1.13,<2)", "ipython", "hypothesis (>=4.43.0,<5.0.0)", "pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)", "black (>=18.6b4,<19)", "flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.720)", "pydocstyle (>=5.0.0,<6)", "pytest (>=3.4.1,<4.0.0)", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<2)", "towncrier (>=19.2.0,<20)"] +doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9,<2)", "towncrier (>=19.2.0,<20)"] +lint = ["black (>=18.6b4,<19)", "flake8 (==3.7.9)", "isort (>=4.2.15,<5)", "mypy (==0.720)", "pydocstyle (>=5.0.0,<6)", "pytest (>=3.4.1,<4.0.0)"] +test = ["hypothesis (>=4.43.0,<5.0.0)", "pytest (==5.4.1)", "pytest-xdist", "tox (==3.14.6)"] + [[package]] name = "fido2" version = "0.8.1" @@ -496,6 +583,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pyparsing = ">=2.0.2" +[[package]] +name = "parsimonious" +version = "0.8.1" +description = "(Soon to be) the fastest pure-Python PEG parser I could muster" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.9.0" + [[package]] name = "pathspec" version = "0.8.1" @@ -773,6 +871,14 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "toolz" +version = "0.11.1" +description = "List processing tools and functional utilities" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "tox" version = "3.21.4" @@ -910,7 +1016,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "1182233da1c699fa3f6fc47c397c064f05f14c02582a71e00983f2a05c8fdc92" +content-hash = "a1d9321c53f56f4ce0fd95524f3e4896e841cb50a7c2f0c6d0670ee8ea842b4e" [metadata.files] appdirs = [ @@ -1042,6 +1148,9 @@ cryptography = [ curve25519-donna = [ {file = "curve25519-donna-1.3.tar.gz", hash = "sha256:1818a9d5356a05c022cd504f44fe1d2f641a5c020f8a4c51b2294e02bd9c1bf0"}, ] +cytoolz = [ + {file = "cytoolz-0.11.0.tar.gz", hash = "sha256:c64f3590c3eb40e1548f0d3c6b2ccde70493d0b8dc6cc7f9f3fec0bb3dcd4222"}, +] dataclasses = [ {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, @@ -1064,6 +1173,22 @@ ecdsa = [ ed25519 = [ {file = "ed25519-1.5.tar.gz", hash = "sha256:02053ee019ceef0df97294be2d4d5a8fc120fc86e81e08bec1245fc0f9403358"}, ] +eth-abi = [ + {file = "eth_abi-2.1.1-py3-none-any.whl", hash = "sha256:78df5d2758247a8f0766a7cfcea4575bcfe568c34a33e6d05a72c328a9040444"}, + {file = "eth_abi-2.1.1.tar.gz", hash = "sha256:4bb1d87bb6605823379b07f6c02c8af45df01a27cc85bd6abb7cf1446ce7d188"}, +] +eth-hash = [ + {file = "eth-hash-0.3.1.tar.gz", hash = "sha256:aee46d9c43b98ac6d4ddf957cf75d4d0a5174ee814cc6b53dd6134dcedb459bf"}, + {file = "eth_hash-0.3.1-py3-none-any.whl", hash = "sha256:a3bc7f1c12eb086525999de7f83b9e7ad39740b31f0f4eccb17377ed70de24dd"}, +] +eth-typing = [ + {file = "eth-typing-2.2.2.tar.gz", hash = "sha256:97ba0f83da7cf1d3668f6ed54983f21168076c552762bf5e06d4a20921877f3f"}, + {file = "eth_typing-2.2.2-py3-none-any.whl", hash = "sha256:1140c7592321dbf10d6663c46f7e43eb0e6410b011b03f14b3df3eb1f76aa9bb"}, +] +eth-utils = [ + {file = "eth-utils-1.10.0.tar.gz", hash = "sha256:bf82762a46978714190b0370265a7148c954d3f0adaa31c6f085ea375e4c61af"}, + {file = "eth_utils-1.10.0-py3-none-any.whl", hash = "sha256:74240a8c6f652d085ed3c85f5f1654203d2f10ff9062f83b3bad0a12ff321c7a"}, +] fido2 = [ {file = "fido2-0.8.1.tar.gz", hash = "sha256:449068f6876f397c8bb96ebc6a75c81c2692f045126d3f13ece21d409acdf7c3"}, ] @@ -1124,6 +1249,7 @@ libusb1 = [ {file = "libusb1-1.9.1.tar.gz", hash = "sha256:d03ef15248c8b8ce440f6be4248eaadc074fc2dc5edd36c48e6e78eef3999292"}, ] mako = [ + {file = "Mako-1.1.4-py2.py3-none-any.whl", hash = "sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e"}, {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, ] markupsafe = [ @@ -1233,6 +1359,9 @@ packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] +parsimonious = [ + {file = "parsimonious-0.8.1.tar.gz", hash = "sha256:3add338892d580e0cb3b1a39e4a1b427ff9f687858fdd61097053742391a9f6b"}, +] pathspec = [ {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, @@ -1373,18 +1502,26 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, @@ -1466,6 +1603,10 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +toolz = [ + {file = "toolz-0.11.1-py3-none-any.whl", hash = "sha256:1bc473acbf1a1db4e72a1ce587be347450e8f08324908b8a266b486f408f04d5"}, + {file = "toolz-0.11.1.tar.gz", hash = "sha256:c7a47921f07822fe534fb1c01c9931ab335a4390c782bd28c6bcc7c2f71f3fbf"}, +] tox = [ {file = "tox-3.21.4-py2.py3-none-any.whl", hash = "sha256:65d0e90ceb816638a50d64f4b47b11da767b284c0addda2294cb3cd69bd72425"}, {file = "tox-3.21.4.tar.gz", hash = "sha256:cf7fef81a3a2434df4d7af2a6d1bf606d2970220addfbe7dea2615bd4bb2c252"}, diff --git a/pyproject.toml b/pyproject.toml index 220067543cb..17abdb063d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ autoflake = "*" flake8-requirements = ">=1.3.2" # common +eth_abi = "^2.1.1" demjson = "*" graphviz = "*"