from enum import IntEnum, auto from typing import Optional from ragger.backend import BackendInterface from ragger.utils import RAPDU from .command_builder import CommandBuilder from .eip712 import EIP712FieldType from .tlv import format_tlv from pathlib import Path import keychain import rlp ROOT_SCREENSHOT_PATH = Path(__file__).parent.parent WEI_IN_ETH = 1e+18 class StatusWord(IntEnum): OK = 0x9000 ERROR_NO_INFO = 0x6a00 INVALID_DATA = 0x6a80 INSUFFICIENT_MEMORY = 0x6a84 INVALID_INS = 0x6d00 INVALID_P1_P2 = 0x6b00 CONDITION_NOT_SATISFIED = 0x6985 REF_DATA_NOT_FOUND = 0x6a88 class DOMAIN_NAME_TAG(IntEnum): STRUCTURE_TYPE = 0x01 STRUCTURE_VERSION = 0x02 CHALLENGE = 0x12 SIGNER_KEY_ID = 0x13 SIGNER_ALGO = 0x14 SIGNATURE = 0x15 DOMAIN_NAME = 0x20 COIN_TYPE = 0x21 ADDRESS = 0x22 class EthAppClient: def __init__(self, client: BackendInterface): self._client = client self._cmd_builder = CommandBuilder() def _send(self, payload: bytearray): return self._client.exchange_async_raw(payload) def response(self) -> RAPDU: return self._client._last_async_response def eip712_send_struct_def_struct_name(self, name: str): return self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)) def eip712_send_struct_def_struct_field(self, field_type: EIP712FieldType, type_name: str, type_size: int, array_levels: [], key_name: str): return self._send(self._cmd_builder.eip712_send_struct_def_struct_field( field_type, type_name, type_size, array_levels, key_name)) def eip712_send_struct_impl_root_struct(self, name: str): return self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)) def eip712_send_struct_impl_array(self, size: int): return self._send(self._cmd_builder.eip712_send_struct_impl_array(size)) def eip712_send_struct_impl_struct_field(self, raw_value: bytes): chunks = self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value) for chunk in chunks[:-1]: with self._send(chunk): pass return self._send(chunks[-1]) def eip712_sign_new(self, bip32_path: str, verbose: bool): return self._send(self._cmd_builder.eip712_sign_new(bip32_path)) def eip712_sign_legacy(self, bip32_path: str, domain_hash: bytes, message_hash: bytes): return self._send(self._cmd_builder.eip712_sign_legacy(bip32_path, domain_hash, message_hash)) def eip712_filtering_activate(self): return self._send(self._cmd_builder.eip712_filtering_activate()) def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes): return self._send(self._cmd_builder.eip712_filtering_message_info(name, filters_count, sig)) def eip712_filtering_show_field(self, name: str, sig: bytes): return self._send(self._cmd_builder.eip712_filtering_show_field(name, sig)) def send_fund(self, bip32_path: str, nonce: int, gas_price: int, gas_limit: int, to: bytes, amount: float, chain_id: int): data = list() data.append(nonce) data.append(gas_price) data.append(gas_limit) data.append(to) data.append(int(amount * WEI_IN_ETH)) data.append(bytes()) data.append(chain_id) data.append(bytes()) data.append(bytes()) chunks = self._cmd_builder.sign(bip32_path, rlp.encode(data)) for chunk in chunks[:-1]: with self._send(chunk): pass return self._send(chunks[-1]) def get_challenge(self): return self._send(self._cmd_builder.get_challenge()) def provide_domain_name(self, challenge: int, name: str, addr: bytes): payload = format_tlv(DOMAIN_NAME_TAG.STRUCTURE_TYPE, 3) # TrustedDomainName payload += format_tlv(DOMAIN_NAME_TAG.STRUCTURE_VERSION, 1) payload += format_tlv(DOMAIN_NAME_TAG.SIGNER_KEY_ID, 0) # test key payload += format_tlv(DOMAIN_NAME_TAG.SIGNER_ALGO, 1) # secp256k1 payload += format_tlv(DOMAIN_NAME_TAG.CHALLENGE, challenge) payload += format_tlv(DOMAIN_NAME_TAG.COIN_TYPE, 0x3c) # ETH in slip-44 payload += format_tlv(DOMAIN_NAME_TAG.DOMAIN_NAME, name) payload += format_tlv(DOMAIN_NAME_TAG.ADDRESS, addr) payload += format_tlv(DOMAIN_NAME_TAG.SIGNATURE, keychain.sign_data(keychain.Key.DOMAIN_NAME, payload)) chunks = self._cmd_builder.provide_domain_name(payload) for chunk in chunks[:-1]: with self._send(chunk): pass return self._send(chunks[-1])