diff --git a/PLUGIN_SPECIFICATION.md b/PLUGIN_SPECIFICATION.md index 64bee892..ee6e60c1 100644 --- a/PLUGIN_SPECIFICATION.md +++ b/PLUGIN_SPECIFICATION.md @@ -13,7 +13,7 @@ Smart contracts covered by this plugin are: | Lisk Mainnet | - | TokenClaim | `0xD7BE2Fd98BfD64c1dfCf6c013fC593eF09219994` | | Lisk Mainnet | - | Reward | `0xD35ca9577a9DADa7624a35EC10C2F55031f0Ab1f` | | Lisk Mainnet | - | Airdrop | `TODO` | -| Lisk Mainnet | - | Governor | `0x58a61b1807a7bDA541855DaAEAEe89b1DDA48568TODO` | +| Lisk Mainnet | - | Governor | `0x58a61b1807a7bDA541855DaAEAEe89b1DDA48568` | ## Functions diff --git a/tests/README.md b/tests/README.md index 4731a54f..b4f58bdd 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,15 +1,10 @@ # Tests -This directory contains non-regression tests for the Boilerplate plugin. +This directory contains non-regression tests for the Lisk App Plugin. The tests craft APDUs, send them to the application, and assert the RAPDUs results and the screen content. -You will need to adapt / enrich the test database with tests relevant to your plugin. -The presence of functional testing of your plugin and an associated CI is mandatory. The [Speculos](https://github.com/LedgerHQ/speculos) tool is used to emulate the device and its applications. The [Ragger](https://github.com/LedgerHQ/ragger) test framework (pytest based) is used to manage the Speculos instance and provide useful helper functions (APDU exchanges, screen navigation, etc). -Both framework are developed by Ledger, if you have trouble using them, we invite you to get in touch with us on our [Discord](https://developers.ledger.com/contact/). -For this reason, the usage of the Ragger framework for your tests is greatly recommended. - ## Binaries @@ -28,12 +23,10 @@ Example of the correct file tree with Ethereum compiled for all targets. The first method is to use the Ledger VSCode extension to automatically manage dependencies The second method is to go in the Ethereum project, compile the application, and dispatch the `build/` output directory. A third method is to re-use the Ethereum build used in the CI, and available as artifact. -For example in the [plugin-boilerplate CI](https://github.com/LedgerHQ/app-plugin-boilerplate/actions/workflows/build_and_functional_tests.yml). - ## Launching the tests -The plugin boilerplate app comes with functional tests +The Lisk App Plugin comes with functional tests ### macOS / Windows diff --git a/tests/abis/0x9fA3CA453EbfB7a6d1085153D83a2988eE822BD0.abi.json b/tests/abis/airdrop.abi.json similarity index 100% rename from tests/abis/0x9fA3CA453EbfB7a6d1085153D83a2988eE822BD0.abi.json rename to tests/abis/airdrop.abi.json diff --git a/tests/abis/0xD7BE2Fd98BfD64c1dfCf6c013fC593eF09219994.abi.json b/tests/abis/claim.abi.json similarity index 100% rename from tests/abis/0xD7BE2Fd98BfD64c1dfCf6c013fC593eF09219994.abi.json rename to tests/abis/claim.abi.json diff --git a/tests/abis/0x8179793eF169b6EE7aAa2A5EE37d9463457ee757.abi.json b/tests/abis/governor.abi.json similarity index 100% rename from tests/abis/0x8179793eF169b6EE7aAa2A5EE37d9463457ee757.abi.json rename to tests/abis/governor.abi.json diff --git a/tests/abis/0xD35ca9577a9DADa7624a35EC10C2F55031f0Ab1f.abi.json b/tests/abis/reward.abi.json similarity index 100% rename from tests/abis/0xD35ca9577a9DADa7624a35EC10C2F55031f0Ab1f.abi.json rename to tests/abis/reward.abi.json diff --git a/tests/test_airdrop.py b/tests/test_airdrop.py index f767b1b5..07808cd3 100644 --- a/tests/test_airdrop.py +++ b/tests/test_airdrop.py @@ -1,37 +1,12 @@ -from pathlib import Path -import json -import os -import datetime - -from hexbytes import HexBytes - from web3 import Web3 -from eth_typing import ChainId - -from ledger_app_clients.ethereum.client import EthAppClient -import ledger_app_clients.ethereum.response_parser as ResponseParser -from ledger_app_clients.ethereum.utils import get_selector_from_data, recover_transaction -from ragger.navigator import NavInsID - -from .utils import get_appname_from_makefile, DERIVATION_PATH - +from .utils import run_test, load_contract -ROOT_SCREENSHOT_PATH = Path(__file__).parent -ABIS_FOLDER = "%s/abis" % (os.path.dirname(__file__)) - -PLUGIN_NAME = get_appname_from_makefile() - -# TODO: Update address with abi -with open("%s/0x9fA3CA453EbfB7a6d1085153D83a2988eE822BD0.abi.json" % (ABIS_FOLDER)) as file: - contract = Web3().eth.contract( - abi=json.load(file), - # Get address from filename - address=bytes.fromhex(os.path.basename(file.name).split(".")[0].split("x")[-1]) - ) +contract = load_contract( + "9fA3CA453EbfB7a6d1085153D83a2988eE822BD0", + "airdrop" +) def test_claim_airdrop(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("claimAirdrop", [ bytes.fromhex("77abc2dd8ca5021dbb8e91e63f574b1e440764d2"), 4915507196, @@ -40,40 +15,4 @@ def test_claim_airdrop(backend, firmware, navigator, test_name, wallet_addr): ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) \ No newline at end of file diff --git a/tests/test_claim.py b/tests/test_claim.py index d734493f..e575a37f 100644 --- a/tests/test_claim.py +++ b/tests/test_claim.py @@ -1,37 +1,12 @@ -from pathlib import Path -import json -import os -import datetime - -from hexbytes import HexBytes - from web3 import Web3 -from eth_typing import ChainId - -from ledger_app_clients.ethereum.client import EthAppClient -import ledger_app_clients.ethereum.response_parser as ResponseParser -from ledger_app_clients.ethereum.utils import get_selector_from_data, recover_transaction -from ragger.navigator import NavInsID - -from .utils import get_appname_from_makefile, DERIVATION_PATH - - -ROOT_SCREENSHOT_PATH = Path(__file__).parent -ABIS_FOLDER = "%s/abis" % (os.path.dirname(__file__)) - -PLUGIN_NAME = get_appname_from_makefile() +from .utils import run_test, load_contract - -with open("%s/0xD7BE2Fd98BfD64c1dfCf6c013fC593eF09219994.abi.json" % (ABIS_FOLDER)) as file: - contract = Web3().eth.contract( - abi=json.load(file), - # Get address from filename - address=bytes.fromhex(os.path.basename(file.name).split(".")[0].split("x")[-1]) - ) +contract = load_contract( + "D7BE2Fd98BfD64c1dfCf6c013fC593eF09219994", + "claim" +) def test_claim_regular_account(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("claimRegularAccount", [ [ bytes.fromhex("9ca797d905e78f38685b61f62521632ef486bc6ce3e707d2af41b7fe146303c1"), @@ -50,47 +25,9 @@ def test_claim_regular_account(backend, firmware, navigator, test_name, wallet_a ) ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_claim_multisig_account(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("claimMultisigAccount", [ [ bytes.fromhex("f90c3db9f834e9a33086969313a1f5aa2288933e69a065cb829ab7524f113340"), @@ -116,40 +53,4 @@ def test_claim_multisig_account(backend, firmware, navigator, test_name, wallet_ ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) \ No newline at end of file diff --git a/tests/test_governor.py b/tests/test_governor.py index 90bb520e..2af75c64 100644 --- a/tests/test_governor.py +++ b/tests/test_governor.py @@ -1,130 +1,29 @@ -from pathlib import Path -import json -import os -import datetime - -from hexbytes import HexBytes - from web3 import Web3 -from eth_typing import ChainId - -from ledger_app_clients.ethereum.client import EthAppClient -import ledger_app_clients.ethereum.response_parser as ResponseParser -from ledger_app_clients.ethereum.utils import get_selector_from_data, recover_transaction -from ragger.navigator import NavInsID - -from .utils import get_appname_from_makefile, DERIVATION_PATH - - -ROOT_SCREENSHOT_PATH = Path(__file__).parent -ABIS_FOLDER = "%s/abis" % (os.path.dirname(__file__)) +from .utils import run_test, load_contract -PLUGIN_NAME = get_appname_from_makefile() - -# TODO: Update address with abi -with open("%s/0x8179793eF169b6EE7aAa2A5EE37d9463457ee757.abi.json" % (ABIS_FOLDER)) as file: - contract = Web3().eth.contract( - abi=json.load(file), - # Get address from filename - address=bytes.fromhex(os.path.basename(file.name).split(".")[0].split("x")[-1]) - ) +contract = load_contract( + "8179793eF169b6EE7aAa2A5EE37d9463457ee757", + "governor" +) def test_governor_cast_vote(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("castVote", [ 86511774, 1, ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_governor_cast_vote_with_reason(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("castVoteWithReason", [ 86511774, 1, "Cast vote with reason" ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_governor_propose_one_element(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("propose", [ [ bytes.fromhex("77abc2dd8ca5021dbb8e91e63f574b1e440764d2"), @@ -137,48 +36,10 @@ def test_governor_propose_one_element(backend, firmware, navigator, test_name, w ], "Test string" ]) - - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_governor_propose_two_elements(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("propose", [ [ bytes.fromhex("77abc2dd8ca5021dbb8e91e63f574b1e440764d2"), @@ -193,41 +54,5 @@ def test_governor_propose_two_elements(backend, firmware, navigator, test_name, ], "Test string" ]) - - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() \ No newline at end of file + + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) \ No newline at end of file diff --git a/tests/test_reward.py b/tests/test_reward.py index 400031b5..ead9a850 100644 --- a/tests/test_reward.py +++ b/tests/test_reward.py @@ -1,83 +1,20 @@ -from pathlib import Path -import json -import os -import datetime - -from hexbytes import HexBytes - from web3 import Web3 -from eth_typing import ChainId - -from ledger_app_clients.ethereum.client import EthAppClient -import ledger_app_clients.ethereum.response_parser as ResponseParser -from ledger_app_clients.ethereum.utils import get_selector_from_data, recover_transaction -from ragger.navigator import NavInsID - -from .utils import get_appname_from_makefile, DERIVATION_PATH - +from .utils import run_test, load_contract -ROOT_SCREENSHOT_PATH = Path(__file__).parent -ABIS_FOLDER = "%s/abis" % (os.path.dirname(__file__)) - -PLUGIN_NAME = get_appname_from_makefile() - - -with open("%s/0xD35ca9577a9DADa7624a35EC10C2F55031f0Ab1f.abi.json" % (ABIS_FOLDER)) as file: - contract = Web3().eth.contract( - abi=json.load(file), - # Get address from filename - address=bytes.fromhex(os.path.basename(file.name).split(".")[0].split("x")[-1]) - ) +contract = load_contract( + "D35ca9577a9DADa7624a35EC10C2F55031f0Ab1f", + "reward" +) def test_create_position(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("createPosition", [ 86511774, 100 ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_initiate_fast_unlock(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("initiateFastUnlock", [ [ 86511774, @@ -87,47 +24,9 @@ def test_initiate_fast_unlock(backend, firmware, navigator, test_name, wallet_ad ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_claim_rewards(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("claimRewards", [ [ 86511774, @@ -137,47 +36,9 @@ def test_claim_rewards(backend, firmware, navigator, test_name, wallet_addr): ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_pause_unlocking(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("pauseUnlocking", [ [ 86511774, @@ -187,47 +48,9 @@ def test_pause_unlocking(backend, firmware, navigator, test_name, wallet_addr): ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_resume_unlocking(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("resumeUnlockingCountdown", [ [ 86511774, @@ -237,47 +60,9 @@ def test_resume_unlocking(backend, firmware, navigator, test_name, wallet_addr): ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_increase_locking_amount(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("increaseLockingAmount", [ [ (2024, 2000000023003000000), @@ -285,47 +70,9 @@ def test_increase_locking_amount(backend, firmware, navigator, test_name, wallet ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_extend_duration(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("extendDuration", [ [ (2024, 6), @@ -333,47 +80,9 @@ def test_extend_duration(backend, firmware, navigator, test_name, wallet_addr): ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_delete_positions(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("deletePositions", [ [ 42344, @@ -383,134 +92,22 @@ def test_delete_positions(backend, firmware, navigator, test_name, wallet_addr): ] ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_add_unused_rewards(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("addUnusedRewards", [ 20000000000000000000, 12, 2 ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) def test_fund_staking_rewards(backend, firmware, navigator, test_name, wallet_addr): - client = EthAppClient(backend) - data = contract.encodeABI("fundStakingRewards", [ 300000000000000000000, 7, 6 ]) - # first setup the external plugin - client.set_external_plugin(PLUGIN_NAME, - contract.address, - # Extract function selector from the encoded data - get_selector_from_data(data)) - - tx_params = { - "nonce": 20, - "maxFeePerGas": Web3.to_wei(145, "gwei"), - "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), - "gas": 173290, - "to": contract.address, - "value": Web3.to_wei(0.1, "ether"), - "chainId": ChainId.ETH, - "data": data - } - - # send the transaction - with client.sign(DERIVATION_PATH, tx_params): - # Validate the on-screen request by performing the navigation appropriate for this device - if firmware.device.startswith("nano"): - navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, - [NavInsID.BOTH_CLICK], - "Accept", - ROOT_SCREENSHOT_PATH, - test_name) - else: - navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, - [NavInsID.USE_CASE_REVIEW_CONFIRM, - NavInsID.USE_CASE_STATUS_DISMISS], - "Hold to sign", - ROOT_SCREENSHOT_PATH, - test_name) - # verify signature - vrs = ResponseParser.signature(client.response().data) - addr = recover_transaction(tx_params, vrs) - assert addr == wallet_addr.get() + run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr) \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index 221ca848..0dce75b7 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,8 +1,15 @@ import os import re +import json from pathlib import Path from typing import Optional +from web3 import Web3 +from eth_typing import ChainId + +from ledger_app_clients.ethereum.utils import get_selector_from_data, recover_transaction +from ragger.navigator import NavInsID + from ledger_app_clients.ethereum.client import EthAppClient import ledger_app_clients.ethereum.response_parser as ResponseParser @@ -15,6 +22,8 @@ default_strip_parameter = " \t\n\r\x0b\x0c" +ROOT_SCREENSHOT_PATH = Path(__file__).parent +ABIS_FOLDER = "%s/abis" % (os.path.dirname(__file__)) def get_appname_from_makefile() -> str: ''' @@ -32,6 +41,58 @@ def get_appname_from_makefile() -> str: return APPNAME +PLUGIN_NAME = get_appname_from_makefile() + +def load_contract(address, abi_file): + with open("%s/%s.abi.json" % (ABIS_FOLDER, abi_file)) as file: + return Web3().eth.contract( + abi=json.load(file), + # Get address from filename + address=bytes.fromhex( + address + ) + ) + +def run_test(contract, data, backend, firmware, navigator, test_name, wallet_addr, value=0): + client = EthAppClient(backend) + + # first setup the external plugin + client.set_external_plugin(PLUGIN_NAME, + contract.address, + # Extract function selector from the encoded data + get_selector_from_data(data)) + + tx_params = { + "nonce": 20, + "maxFeePerGas": Web3.to_wei(145, "gwei"), + "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"), + "gas": 173290, + "to": contract.address, + "value": value, + "chainId": ChainId.ETH, + "data": data + } + + # send the transaction + with client.sign(DERIVATION_PATH, tx_params): + # Validate the on-screen request by performing the navigation appropriate for this device + if firmware.is_nano: + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Accept", + ROOT_SCREENSHOT_PATH, + test_name) + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name) + # verify signature + vrs = ResponseParser.signature(client.response().data) + addr = recover_transaction(tx_params, vrs) + assert addr == wallet_addr.get() class WalletAddr: client: EthAppClient