from pathlib import Path from web3 import Web3 from ragger.error import ExceptionRAPDU from ragger.backend import BackendInterface from ragger.firmware import Firmware from ragger.navigator import Navigator, NavInsID from ragger.navigator.navigation_scenario import NavigateWithScenario from client.client import EthAppClient, StatusWord import client.response_parser as ResponseParser from client.settings import SettingID, settings_toggle from client.utils import recover_transaction # Values used across all tests CHAIN_ID = 1 ADDR = bytes.fromhex("0011223344556677889900112233445566778899") ADDR2 = bytes.fromhex("5a321744667052affa8386ed49e00ef223cbffc3") ADDR3 = bytes.fromhex("dac17f958d2ee523a2206206994597c13d831ec7") ADDR4 = bytes.fromhex("b2bb2b958afa2e96dab3f3ce7162b87daea39017") BIP32_PATH = "m/44'/60'/0'/0/0" BIP32_PATH2 = "m/44'/60'/1'/0/0" NONCE = 21 NONCE2 = 68 GAS_PRICE = 13 GAS_PRICE2 = 5 GAS_LIMIT = 21000 AMOUNT = 1.22 AMOUNT2 = 0.31415 def common(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, default_screenshot_path: Path, tx_params: dict, test_name: str = "", path: str = BIP32_PATH, confirm: bool = False): app_client = EthAppClient(backend) with app_client.get_public_addr(bip32_path=path, display=False): pass _, DEVICE_ADDR, _ = ResponseParser.pk_addr(app_client.response().data) with app_client.sign(path, tx_params): if not firmware.is_nano and confirm: navigator.navigate_and_compare(default_screenshot_path, f"{test_name}/confirm", [NavInsID.USE_CASE_CHOICE_CONFIRM], screen_change_after_last_instruction=False) if firmware.is_nano: end_text = "Accept" else: end_text = "Sign" scenario_navigator.review_approve(custom_screen_text=end_text, do_comparison=test_name!="") # verify signature vrs = ResponseParser.signature(app_client.response().data) addr = recover_transaction(tx_params, vrs) assert addr == DEVICE_ADDR def common_reject(backend: BackendInterface, scenario_navigator: NavigateWithScenario, tx_params: dict, path: str = BIP32_PATH): app_client = EthAppClient(backend) try: with app_client.sign(path, tx_params): scenario_navigator.review_reject() except ExceptionRAPDU as e: assert e.status == StatusWord.CONDITION_NOT_SATISFIED else: assert False # An exception should have been raised def common_fail(backend: BackendInterface, tx_params: dict, expected: StatusWord, path: str = BIP32_PATH): app_client = EthAppClient(backend) try: with app_client.sign(path, tx_params): pass except ExceptionRAPDU as e: assert e.status == expected else: assert False # An exception should have been raised def test_legacy(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, default_screenshot_path: Path): tx_params: dict = { "nonce": NONCE, "gasPrice": Web3.to_wei(GAS_PRICE, "gwei"), "gas": GAS_LIMIT, "to": ADDR, "value": Web3.to_wei(AMOUNT, "ether"), "chainId": CHAIN_ID } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params) # Transfer amount >= 2^87 Eth on Ethereum app should fail def test_legacy_send_error(backend: BackendInterface): tx_params: dict = { "nonce": 38, "gasPrice": 56775612312210000000001234554332, "gas": GAS_LIMIT, "to": ADDR3, "value": 12345678912345678912345678000000000000000000, "chainId": CHAIN_ID } common_fail(backend, tx_params, StatusWord.EXCEPTION_OVERFLOW, path=BIP32_PATH2) # Transfer bsc def test_legacy_send_bsc(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, test_name: str, default_screenshot_path: Path): tx_params: dict = { "nonce": 1, "gasPrice": Web3.to_wei(GAS_PRICE2, 'gwei'), "gas": GAS_LIMIT, "to": ADDR2, "value": Web3.to_wei(AMOUNT2, "ether"), "chainId": 56 } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params, test_name, BIP32_PATH2) # Transfer on network 112233445566 on Ethereum def test_legacy_chainid(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, test_name: str, default_screenshot_path: Path): tx_params: dict = { "nonce": NONCE2, "gasPrice": Web3.to_wei(GAS_PRICE, 'gwei'), "gas": GAS_LIMIT, "to": ADDR2, "value": Web3.to_wei(AMOUNT2, "ether"), "chainId": 112233445566 } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params, test_name, BIP32_PATH2) def test_1559(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, default_screenshot_path: Path): tx_params: dict = { "nonce": NONCE, "maxFeePerGas": Web3.to_wei(145, "gwei"), "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), "gas": GAS_LIMIT, "to": ADDR, "value": Web3.to_wei(AMOUNT, "ether"), "chainId": CHAIN_ID } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params) def test_sign_simple(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, test_name: str, default_screenshot_path: Path): tx_params: dict = { "nonce": NONCE2, "gasPrice": Web3.to_wei(GAS_PRICE, 'gwei'), "gas": GAS_LIMIT, "to": ADDR2, "value": Web3.to_wei(AMOUNT2, "ether"), "chainId": CHAIN_ID } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params, test_name, "m/44'/60'/1'/0/0") def test_sign_limit_nonce(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, test_name: str, default_screenshot_path: Path): tx_params: dict = { "nonce": 2**64-1, "gasPrice": 10, "gas": 50000, "to": ADDR2, "value": 0x08762, "chainId": CHAIN_ID } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params, test_name, "m/44'/60'/1'/0/0") def test_sign_nonce_display(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, test_name: str, default_screenshot_path: Path): settings_toggle(firmware, navigator, [SettingID.NONCE]) tx_params: dict = { "nonce": NONCE2, "gasPrice": Web3.to_wei(GAS_PRICE, 'gwei'), "gas": GAS_LIMIT, "to": ADDR2, "value": Web3.to_wei(AMOUNT2, "ether"), "chainId": CHAIN_ID } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params, test_name, "m/44'/60'/1'/0/0") def test_sign_reject(backend: BackendInterface, scenario_navigator: NavigateWithScenario): tx_params: dict = { "nonce": NONCE2, "gasPrice": Web3.to_wei(GAS_PRICE, 'gwei'), "gas": GAS_LIMIT, "to": ADDR2, "value": Web3.to_wei(AMOUNT2, "ether"), "chainId": CHAIN_ID } common_reject(backend, scenario_navigator, tx_params, "m/44'/60'/1'/0/0") def test_sign_error_transaction_type(backend: BackendInterface): tx_params: dict = { "type": 0, "nonce": 0, "gasPrice": 10, "gas": 50000, "to": ADDR2, "value": 0x19, "chainId": CHAIN_ID } app_client = EthAppClient(backend) try: with app_client.sign(BIP32_PATH2, tx_params): pass except TypeError: pass else: assert False # An exception should have been raised def test_sign_eip_2930(firmware: Firmware, backend: BackendInterface, navigator: Navigator, scenario_navigator: NavigateWithScenario, test_name: str, default_screenshot_path: Path): tx_params = { "nonce": NONCE, "gasPrice": Web3.to_wei(GAS_PRICE2, "gwei"), "gas": GAS_LIMIT, "to": ADDR4, "value": Web3.to_wei(0.01, "ether"), "chainId": 3, "accessList": [ { "address": "0x0000000000000000000000000000000000000001", "storageKeys": [ "0x0100000000000000000000000000000000000000000000000000000000000000" ] } ], } common(firmware, backend, navigator, scenario_navigator, default_screenshot_path, tx_params, test_name)