From aedef2d69de31aaa7bf4af8043b669ee0b481f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vejpustek?= Date: Tue, 23 Jul 2024 14:43:51 +0200 Subject: [PATCH] refactor(core/tests): create template for solana [no changelog] --- ...test_apps.solana.predefined_transaction.py | 88 +++++++- ...apps.solana.predefined_transaction.py.mako | 210 ++++++++++++++++++ tools/build_solana_templates.py | 1 + 3 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 core/tests/test_apps.solana.predefined_transaction.py.mako diff --git a/core/tests/test_apps.solana.predefined_transaction.py b/core/tests/test_apps.solana.predefined_transaction.py index 9bf3fdfda..99b342f31 100644 --- a/core/tests/test_apps.solana.predefined_transaction.py +++ b/core/tests/test_apps.solana.predefined_transaction.py @@ -1,18 +1,94 @@ -from common import * # isort:skip +# generated from test_apps.solana.predefined_transaction.py.mako +# do not edit manually! +from common import Any, unittest, utils # isort:skip from trezor.crypto import base58 from apps.solana.predefined_transaction import is_predefined_token_transfer from apps.solana.transaction.instruction import Instruction -ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" -ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE = None SYSTEM_PROGRAM_ID = "11111111111111111111111111111111" -SYSTEM_PROGRAM_ID_INS_TRANSFER = 2 -TOKEN_2022_PROGRAM_ID = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" -TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED = 12 +STAKE_PROGRAM_ID = "Stake11111111111111111111111111111111111111" +COMPUTE_BUDGET_PROGRAM_ID = "ComputeBudget111111111111111111111111111111" TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" +TOKEN_2022_PROGRAM_ID = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" +ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" +MEMO_PROGRAM_ID = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" +MEMO_LEGACY_PROGRAM_ID = "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo" + +SYSTEM_PROGRAM_ID_INS_CREATE_ACCOUNT = 0 +SYSTEM_PROGRAM_ID_INS_ASSIGN = 1 +SYSTEM_PROGRAM_ID_INS_TRANSFER = 2 +SYSTEM_PROGRAM_ID_INS_CREATE_ACCOUNT_WITH_SEED = 3 +SYSTEM_PROGRAM_ID_INS_ADVANCE_NONCE_ACCOUNT = 4 +SYSTEM_PROGRAM_ID_INS_WITHDRAW_NONCE_ACCOUNT = 5 +SYSTEM_PROGRAM_ID_INS_INITIALIZE_NONCE_ACCOUNT = 6 +SYSTEM_PROGRAM_ID_INS_AUTHORIZE_NONCE_ACCOUNT = 7 +SYSTEM_PROGRAM_ID_INS_ALLOCATE = 8 +SYSTEM_PROGRAM_ID_INS_ALLOCATE_WITH_SEED = 9 +SYSTEM_PROGRAM_ID_INS_ASSIGN_WITH_SEED = 10 +SYSTEM_PROGRAM_ID_INS_TRANSFER_WITH_SEED = 11 +SYSTEM_PROGRAM_ID_INS_UPGRADE_NONCE_ACCOUNT = 12 +STAKE_PROGRAM_ID_INS_INITIALIZE = 0 +STAKE_PROGRAM_ID_INS_AUTHORIZE = 1 +STAKE_PROGRAM_ID_INS_DELEGATE_STAKE = 2 +STAKE_PROGRAM_ID_INS_SPLIT = 3 +STAKE_PROGRAM_ID_INS_WITHDRAW = 4 +STAKE_PROGRAM_ID_INS_DEACTIVATE = 5 +STAKE_PROGRAM_ID_INS_SET_LOCKUP = 6 +STAKE_PROGRAM_ID_INS_MERGE = 7 +STAKE_PROGRAM_ID_INS_AUTHORIZE_WITH_SEED = 8 +STAKE_PROGRAM_ID_INS_INITIALIZE_CHECKED = 9 +STAKE_PROGRAM_ID_INS_AUTHORIZE_CHECKED = 10 +STAKE_PROGRAM_ID_INS_AUTHORIZE_CHECKED_WITH_SEED = 11 +STAKE_PROGRAM_ID_INS_SET_LOCKUP_CHECKED = 12 +COMPUTE_BUDGET_PROGRAM_ID_INS_REQUEST_HEAP_FRAME = 1 +COMPUTE_BUDGET_PROGRAM_ID_INS_SET_COMPUTE_UNIT_LIMIT = 2 +COMPUTE_BUDGET_PROGRAM_ID_INS_SET_COMPUTE_UNIT_PRICE = 3 +TOKEN_PROGRAM_ID_INS_INITIALIZE_ACCOUNT = 1 +TOKEN_PROGRAM_ID_INS_INITIALIZE_MULTISIG = 2 +TOKEN_PROGRAM_ID_INS_TRANSFER = 3 +TOKEN_PROGRAM_ID_INS_APPROVE = 4 +TOKEN_PROGRAM_ID_INS_REVOKE = 5 +TOKEN_PROGRAM_ID_INS_SET_AUTHORITY = 6 +TOKEN_PROGRAM_ID_INS_MINT_TO = 7 +TOKEN_PROGRAM_ID_INS_BURN = 8 +TOKEN_PROGRAM_ID_INS_CLOSE_ACCOUNT = 9 +TOKEN_PROGRAM_ID_INS_FREEZE_ACCOUNT = 10 +TOKEN_PROGRAM_ID_INS_THAW_ACCOUNT = 11 TOKEN_PROGRAM_ID_INS_TRANSFER_CHECKED = 12 +TOKEN_PROGRAM_ID_INS_APPROVE_CHECKED = 13 +TOKEN_PROGRAM_ID_INS_MINT_TO_CHECKED = 14 +TOKEN_PROGRAM_ID_INS_BURN_CHECKED = 15 +TOKEN_PROGRAM_ID_INS_INITIALIZE_ACCOUNT_2 = 16 +TOKEN_PROGRAM_ID_INS_SYNC_NATIVE = 17 +TOKEN_PROGRAM_ID_INS_INITIALIZE_ACCOUNT_3 = 18 +TOKEN_PROGRAM_ID_INS_INITIALIZE_IMMUTABLE_OWNER = 22 +TOKEN_2022_PROGRAM_ID_INS_INITIALIZE_ACCOUNT = 1 +TOKEN_2022_PROGRAM_ID_INS_INITIALIZE_MULTISIG = 2 +TOKEN_2022_PROGRAM_ID_INS_TRANSFER = 3 +TOKEN_2022_PROGRAM_ID_INS_APPROVE = 4 +TOKEN_2022_PROGRAM_ID_INS_REVOKE = 5 +TOKEN_2022_PROGRAM_ID_INS_SET_AUTHORITY = 6 +TOKEN_2022_PROGRAM_ID_INS_MINT_TO = 7 +TOKEN_2022_PROGRAM_ID_INS_BURN = 8 +TOKEN_2022_PROGRAM_ID_INS_CLOSE_ACCOUNT = 9 +TOKEN_2022_PROGRAM_ID_INS_FREEZE_ACCOUNT = 10 +TOKEN_2022_PROGRAM_ID_INS_THAW_ACCOUNT = 11 +TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED = 12 +TOKEN_2022_PROGRAM_ID_INS_APPROVE_CHECKED = 13 +TOKEN_2022_PROGRAM_ID_INS_MINT_TO_CHECKED = 14 +TOKEN_2022_PROGRAM_ID_INS_BURN_CHECKED = 15 +TOKEN_2022_PROGRAM_ID_INS_INITIALIZE_ACCOUNT_2 = 16 +TOKEN_2022_PROGRAM_ID_INS_SYNC_NATIVE = 17 +TOKEN_2022_PROGRAM_ID_INS_INITIALIZE_ACCOUNT_3 = 18 +TOKEN_2022_PROGRAM_ID_INS_INITIALIZE_IMMUTABLE_OWNER = 22 +ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE = None +ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE_IDEMPOTENT = 1 +ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_RECOVER_NESTED = 2 +MEMO_PROGRAM_ID_INS_MEMO = None +MEMO_LEGACY_PROGRAM_ID_INS_MEMO = None + def create_mock_instruction( program_id: str, instruction_id: int, parsed_data: dict[str, Any] diff --git a/core/tests/test_apps.solana.predefined_transaction.py.mako b/core/tests/test_apps.solana.predefined_transaction.py.mako new file mode 100644 index 000000000..6eb95c6e2 --- /dev/null +++ b/core/tests/test_apps.solana.predefined_transaction.py.mako @@ -0,0 +1,210 @@ +# generated from test_apps.solana.predefined_transaction.py.mako +# do not edit manually! +<%def name="getProgramId(program)">${"_".join(program["name"].upper().split(" ") + ["ID"])}\ +<%def name="getInstructionIdText(program, instruction)">${"_".join([getProgramId(program)] + ["INS"] + instruction["name"].upper().split(" "))}\ +from common import Any, unittest, utils # isort:skip + +from trezor.crypto import base58 + +from apps.solana.predefined_transaction import is_predefined_token_transfer +from apps.solana.transaction.instruction import Instruction + +% for program in programs["programs"]: +${getProgramId(program)} = "${program["id"]}" +% endfor + +% for program in programs["programs"]: + % for instruction in program["instructions"]: +${getInstructionIdText(program, instruction)} = ${instruction["id"]} + % endfor +% endfor + +def create_mock_instruction( + program_id: str, instruction_id: int, parsed_data: dict[str, Any] +): + instruction = Instruction( + instruction_data=b"", + program_id=program_id, + accounts=[], + instruction_id=instruction_id, + property_templates=[], + accounts_template=[], + ui_properties=[], + ui_name="", + is_program_supported=True, + is_instruction_supported=True, + is_ui_hidden=False, + supports_multisig=False, + is_deprecated_warning=None, + ) + + instruction.parsed_data = parsed_data + return instruction + + +def create_transfer_token_instruction( + program_id=TOKEN_PROGRAM_ID, + instruction_id=TOKEN_PROGRAM_ID_INS_TRANSFER_CHECKED, + token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou", + destination_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx", + owner="14CCvQzQzHCVgZM3j9soPnXuJXh1RmCfwLVUcdfbZVBS", +): + return create_mock_instruction( + program_id, + instruction_id, + { + "token_mint": (base58.decode(token_mint),), + "destination_account": (base58.decode(destination_account),), + "owner": (base58.decode(owner),), + }, + ) + + +def create_create_token_account_instruction( + token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou", + associated_token_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx", + spl_token=TOKEN_PROGRAM_ID, +): + return create_mock_instruction( + ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, + ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE, + { + "token_mint": (base58.decode(token_mint),), + "associated_token_account": (base58.decode(associated_token_account),), + "spl_token": (base58.decode(spl_token),), + }, + ) + + +@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") +class TestSolanaPredefinedTransactions(unittest.TestCase): + def test_is_predefined_token_transfer(self): + # note: if there are multiple transfer instructions they are the same + # in the tests because that's the info the test cares about. In reality + # the instructions can differ in the destination account and amount. + valid_test_cases = [ + [create_transfer_token_instruction()], + [create_transfer_token_instruction(), create_transfer_token_instruction()], + [ + create_create_token_account_instruction(), + create_transfer_token_instruction(), + ], + [ + create_create_token_account_instruction(), + create_transfer_token_instruction(), + create_transfer_token_instruction(), + ], + [ + create_create_token_account_instruction( + spl_token=TOKEN_2022_PROGRAM_ID + ), + create_transfer_token_instruction( + program_id=TOKEN_2022_PROGRAM_ID, + instruction_id=TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED, + ), + create_transfer_token_instruction( + program_id=TOKEN_2022_PROGRAM_ID, + instruction_id=TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED, + ), + ], + ] + + invalid_test_cases = [ + # only create account + [ + create_create_token_account_instruction(), + ], + # there are other instructions + [ + create_transfer_token_instruction(), + create_mock_instruction( + SYSTEM_PROGRAM_ID, SYSTEM_PROGRAM_ID_INS_TRANSFER, {} + ), + ], + # multiple create account instructions + [ + create_create_token_account_instruction(), + create_create_token_account_instruction(), + create_transfer_token_instruction(), + ], + # transfer instructions program_id mismatch + [ + create_transfer_token_instruction( + program_id=TOKEN_PROGRAM_ID, + instruction_id=TOKEN_PROGRAM_ID_INS_TRANSFER_CHECKED, + ), + create_transfer_token_instruction( + program_id=TOKEN_2022_PROGRAM_ID, + instruction_id=TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED, + ), + ], + # transfer instructions token_mint mismatch + [ + create_transfer_token_instruction( + token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou" + ), + create_transfer_token_instruction( + token_mint="GZDphoFQJ9m7uRU7TdS8cVDGFvsiQbcaY3n5mdoQHmDj" + ), + ], + # transfer instructions destination mismatch + [ + create_transfer_token_instruction( + destination_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx" + ), + create_transfer_token_instruction( + destination_account="74pZnim7gywyschy4MGkW6eZURv1DBXqwHTCqLRk63wz" + ), + ], + # transfer instructions owner mismatch + [ + create_transfer_token_instruction( + owner="14CCvQzQzHCVgZM3j9soPnXuJXh1RmCfwLVUcdfbZVBS" + ), + create_transfer_token_instruction( + owner="BVRFH6vt5bNXub6WnnFRgaHFTcbkjBrf7x1troU1izGg" + ), + ], + # token program mismatch + [ + create_create_token_account_instruction( + spl_token=TOKEN_2022_PROGRAM_ID + ), + create_transfer_token_instruction( + program_id=TOKEN_PROGRAM_ID, + ), + ], + # create account token_mint mismatch + [ + create_create_token_account_instruction( + token_mint="GZDphoFQJ9m7uRU7TdS8cVDGFvsiQbcaY3n5mdoQHmDj" + ), + create_transfer_token_instruction( + token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou", + ), + ], + # create account associated_token_account mismatch + [ + create_create_token_account_instruction( + associated_token_account="74pZnim7gywyschy4MGkW6eZURv1DBXqwHTCqLRk63wz" + ), + create_transfer_token_instruction( + destination_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx", + ), + ], + # create account is not first + [ + create_transfer_token_instruction(), + create_create_token_account_instruction(), + ], + ] + + for instructions in valid_test_cases: + self.assertTrue(is_predefined_token_transfer(instructions)) + + for instructions in invalid_test_cases: + self.assertFalse(is_predefined_token_transfer(instructions)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/build_solana_templates.py b/tools/build_solana_templates.py index 12b734810..c8f27b12d 100755 --- a/tools/build_solana_templates.py +++ b/tools/build_solana_templates.py @@ -15,6 +15,7 @@ ROOT = HERE.parent.resolve() TEMPLATES = ( ROOT / "core" / "src" / "apps" / "solana" / "transaction" / "instructions.py.mako", ROOT / "tests" / "device_tests" / "solana" / "construct" / "instructions.py.mako", + ROOT / "core" / "tests" / "test_apps.solana.predefined_transaction.py.mako", ) PROGRAMS_JSON = ROOT / "common" / "defs" / "solana" / "programs.json"