diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index 6f6cdbc..2c33026 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -11,7 +11,14 @@ from .tlv import format_tlv WEI_IN_ETH = 1e+18 +GWEI_IN_ETH = 1e+9 +class TxData: + selector: bytes + parameters: list[bytes] + def __init__(self, selector: bytes, params: list[bytes]): + self.selector = selector + self.parameters = params class StatusWord(IntEnum): OK = 0x9000 @@ -96,31 +103,79 @@ class EthAppClient: 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[Union[int, bytes]] = 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)) + def _sign(self, bip32_path: str, raw_tx: bytes): + chunks = self._cmd_builder.sign(bip32_path, raw_tx) for chunk in chunks[:-1]: with self._send(chunk): pass return self._send(chunks[-1]) + def _data_to_payload(self, data: TxData) -> bytes: + payload = bytearray(data.selector) + for param in data.parameters: + payload += param.rjust(32, b'\x00') + return payload + + def _sign_common(self, + tx: list, + gas_price: float, + gas_limit: int, + destination: bytes, + amount: float, + data: TxData): + tx.append(int(gas_price * GWEI_IN_ETH)) + tx.append(gas_limit) + tx.append(destination) + if amount > 0: + tx.append(int(amount * WEI_IN_ETH)) + else: + tx.append(bytes()) + if data is not None: + tx.append(self._data_to_payload(data)) + else: + tx.append(bytes()) + return tx + + def sign_legacy(self, + bip32_path: str, + nonce: int, + gas_price: float, + gas_limit: int, + destination: bytes, + amount: float, + chain_id: int, + data: TxData = None): + tx = list() + tx.append(nonce) + tx = self._sign_common(tx, gas_price, gas_limit, destination, amount, data) + tx.append(chain_id) + tx.append(bytes()) + tx.append(bytes()) + return self._sign(bip32_path, rlp.encode(tx)) + + def sign_1559(self, + bip32_path: str, + chain_id: int, + nonce: int, + max_prio_gas_price: float, + max_gas_price: float, + gas_limit: int, + destination: bytes, + amount: float, + data: TxData = None, + access_list = list()): + tx = list() + tx.append(chain_id) + tx.append(nonce) + tx.append(int(max_prio_gas_price * GWEI_IN_ETH)) + tx = self._sign_common(tx, max_gas_price, gas_limit, destination, amount, data) + tx.append(access_list) + tx.append(False) + tx.append(bytes()) + tx.append(bytes()) + # prefix with transaction type + return self._sign(bip32_path, b'\x02' + rlp.encode(tx)) + def get_challenge(self): return self._send(self._cmd_builder.get_challenge()) diff --git a/tests/ragger/test_domain_name.py b/tests/ragger/test_domain_name.py index 05bcddf..8db6fca 100644 --- a/tests/ragger/test_domain_name.py +++ b/tests/ragger/test_domain_name.py @@ -20,7 +20,7 @@ KEY_ID = 1 ALGO_ID = 1 BIP32_PATH = "m/44'/60'/0'/0/0" NONCE = 21 -GAS_PRICE = 13000000000 +GAS_PRICE = 13 GAS_LIMIT = 21000 AMOUNT = 1.22 @@ -50,13 +50,13 @@ def test_send_fund(firmware: Firmware, with app_client.provide_domain_name(challenge, NAME, ADDR): pass - with app_client.send_fund(BIP32_PATH, - NONCE, - GAS_PRICE, - GAS_LIMIT, - ADDR, - AMOUNT, - CHAIN_ID): + with app_client.sign_legacy(BIP32_PATH, + NONCE, + GAS_PRICE, + GAS_LIMIT, + ADDR, + AMOUNT, + CHAIN_ID): moves = list() if firmware.device.startswith("nano"): moves += [ NavInsID.RIGHT_CLICK ] * 4 @@ -101,13 +101,13 @@ def test_send_fund_wrong_addr(firmware: Firmware, addr = bytearray(ADDR) addr.reverse() - with app_client.send_fund(BIP32_PATH, - NONCE, - GAS_PRICE, - GAS_LIMIT, - addr, - AMOUNT, - CHAIN_ID): + with app_client.sign_legacy(BIP32_PATH, + NONCE, + GAS_PRICE, + GAS_LIMIT, + addr, + AMOUNT, + CHAIN_ID): moves = list() if firmware.device.startswith("nano"): moves += [ NavInsID.RIGHT_CLICK ] * 4 @@ -130,13 +130,13 @@ def test_send_fund_non_mainnet(firmware: Firmware, with app_client.provide_domain_name(challenge, NAME, ADDR): pass - with app_client.send_fund(BIP32_PATH, - NONCE, - GAS_PRICE, - GAS_LIMIT, - ADDR, - AMOUNT, - 5): + with app_client.sign_legacy(BIP32_PATH, + NONCE, + GAS_PRICE, + GAS_LIMIT, + ADDR, + AMOUNT, + 5): moves = list() if firmware.device.startswith("nano"): moves += [ NavInsID.RIGHT_CLICK ] * 5 @@ -159,13 +159,13 @@ def test_send_fund_unknown_chain(firmware: Firmware, with app_client.provide_domain_name(challenge, NAME, ADDR): pass - with app_client.send_fund(BIP32_PATH, - NONCE, - GAS_PRICE, - GAS_LIMIT, - ADDR, - AMOUNT, - 9): + with app_client.sign_legacy(BIP32_PATH, + NONCE, + GAS_PRICE, + GAS_LIMIT, + ADDR, + AMOUNT, + 9): moves = list() if firmware.device.startswith("nano"): moves += [ NavInsID.RIGHT_CLICK ] * 5