diff --git a/common/defs/solana/programs.json b/common/defs/solana/programs.json index 1531806d965..97e2d9528a1 100644 --- a/common/defs/solana/programs.json +++ b/common/defs/solana/programs.json @@ -1623,7 +1623,8 @@ { "name": "mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "account_to_mint", @@ -1637,9 +1638,13 @@ } ], "ui_properties": [ + { + "account": "mint", + "display_name": "Mint token" + }, { "parameter": "amount", - "display_name": "Mint tokens" + "display_name": "Mint amount" }, { "account": "account_to_mint", @@ -1672,7 +1677,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "owner", @@ -1681,9 +1687,13 @@ } ], "ui_properties": [ + { + "account": "token_mint", + "display_name": "Burn token" + }, { "parameter": "amount", - "display_name": "Burn tokens" + "display_name": "Burn amount" }, { "account": "account_to_burn_from", @@ -1746,7 +1756,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "freeze_authority", @@ -1759,6 +1770,10 @@ "account": "account_to_freeze", "display_name": "Freeze account" }, + { + "account": "token_mint", + "display_name": "Token" + }, { "account": "freeze_authority", "display_name": "Owner" @@ -1779,7 +1794,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "freeze_authority", @@ -1792,6 +1808,10 @@ "account": "account_to_freeze", "display_name": "Thaw account" }, + { + "account": "token_mint", + "display_name": "Token" + }, { "account": "freeze_authority", "display_name": "Owner" @@ -1823,7 +1843,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "destination_account", @@ -1884,7 +1905,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "delegate", @@ -1940,7 +1962,8 @@ { "name": "mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "account_to_mint", @@ -1997,7 +2020,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "owner", @@ -2414,7 +2438,8 @@ { "name": "mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "account_to_mint", @@ -2428,9 +2453,13 @@ } ], "ui_properties": [ + { + "account": "mint", + "display_name": "Mint token" + }, { "parameter": "amount", - "display_name": "Mint tokens" + "display_name": "Mint amount" }, { "account": "account_to_mint", @@ -2463,7 +2492,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "owner", @@ -2472,9 +2502,13 @@ } ], "ui_properties": [ + { + "account": "token_mint", + "display_name": "Burn token" + }, { "parameter": "amount", - "display_name": "Burn tokens" + "display_name": "Burn amount" }, { "account": "account_to_burn_from", @@ -2537,7 +2571,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "freeze_authority", @@ -2550,6 +2585,10 @@ "account": "account_to_freeze", "display_name": "Freeze account" }, + { + "account": "token_mint", + "display_name": "Token" + }, { "account": "freeze_authority", "display_name": "Owner" @@ -2570,7 +2609,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "freeze_authority", @@ -2583,6 +2623,10 @@ "account": "account_to_freeze", "display_name": "Thaw account" }, + { + "account": "token_mint", + "display_name": "Token" + }, { "account": "freeze_authority", "display_name": "Owner" @@ -2614,7 +2658,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "destination_account", @@ -2675,7 +2720,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "delegate", @@ -2731,7 +2777,8 @@ { "name": "mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "account_to_mint", @@ -2788,7 +2835,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "owner", @@ -2965,7 +3013,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "system_program", @@ -3027,7 +3076,8 @@ { "name": "token_mint", "is_authority": false, - "optional": false + "optional": false, + "is_token_mint": true }, { "name": "system_program", diff --git a/common/protob/Makefile b/common/protob/Makefile index f8df2d2d5df..8c26b858d38 100644 --- a/common/protob/Makefile +++ b/common/protob/Makefile @@ -1,4 +1,4 @@ -check: messages.pb messages-binance.pb messages-bitcoin.pb messages-bootloader.pb messages-cardano.pb messages-common.pb messages-crypto.pb messages-debug.pb messages-ethereum.pb messages-management.pb messages-monero.pb messages-nem.pb messages-ripple.pb messages-stellar.pb messages-tezos.pb messages-eos.pb +check: messages.pb messages-binance.pb messages-bitcoin.pb messages-bootloader.pb messages-cardano.pb messages-common.pb messages-crypto.pb messages-debug.pb messages-ethereum.pb messages-management.pb messages-monero.pb messages-nem.pb messages-ripple.pb messages-stellar.pb messages-tezos.pb messages-eos.pb messages-solana.pb messages-definitions.pb %.pb: %.proto protoc -I/usr/include -I. $< -o $@ diff --git a/common/protob/messages-ethereum-definitions.proto b/common/protob/messages-definitions.proto similarity index 62% rename from common/protob/messages-ethereum-definitions.proto rename to common/protob/messages-definitions.proto index d770032db04..d503faa1d04 100644 --- a/common/protob/messages-ethereum-definitions.proto +++ b/common/protob/messages-definitions.proto @@ -1,18 +1,18 @@ syntax = "proto2"; -package hw.trezor.messages.ethereum_definitions; +package hw.trezor.messages.definitions; // Sugar for easier handling in Java option java_package = "com.satoshilabs.trezor.lib.protobuf"; -option java_outer_classname = "TrezorMessageEthereumDefinitions"; - +option java_outer_classname = "TrezorMessageDefinitions"; /** - * Ethereum definitions type enum. - * Used to check the encoded EthereumNetworkInfo or EthereumTokenInfo message. + * Definitions type enum. + * Used to check the encoded EthereumNetworkInfo/EthereumTokenInfo/SolanaTokenInfo message. */ - enum EthereumDefinitionType { - NETWORK = 0; - TOKEN = 1; +enum DefinitionType { + ETHEREUM_NETWORK = 0; + ETHEREUM_TOKEN = 1; + SOLANA_TOKEN = 2; } /** @@ -51,10 +51,16 @@ message EthereumTokenInfo { } /** - * Contains an encoded Ethereum network and/or token definition. See ethereum-definitions.md for details. + * Solana token definition. Used to (de)serialize the definition. + * + * Definition types should not be cross-parseable, i.e., it should not be possible to + * incorrectly parse different Solana definitions as each other. + * * @embed */ -message EthereumDefinitions { - optional bytes encoded_network = 1; // encoded Ethereum network - optional bytes encoded_token = 2; // encoded Ethereum token +message SolanaTokenInfo { + required bytes mint = 1; // token mint - unique token id + required string program_id = 2; // token program id, e.g. token program, token22 program + required string name = 3; + required string ticker = 4; } diff --git a/common/protob/messages-ethereum-eip712.proto b/common/protob/messages-ethereum-eip712.proto index a5c78d6e57b..63eb67b52d5 100644 --- a/common/protob/messages-ethereum-eip712.proto +++ b/common/protob/messages-ethereum-eip712.proto @@ -5,7 +5,7 @@ package hw.trezor.messages.ethereum_eip712; option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_outer_classname = "TrezorMessageEthereumEIP712"; -import "messages-ethereum-definitions.proto"; +import "messages-ethereum.proto"; // Separated from messages-ethereum.proto as it is not implemented on T1 side @@ -22,10 +22,10 @@ import "messages-ethereum-definitions.proto"; * @next Failure */ message EthereumSignTypedData { - repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node - required string primary_type = 2; // name of the root message struct - optional bool metamask_v4_compat = 3 [default=true]; // use MetaMask v4 (see https://github.com/MetaMask/eth-sig-util/issues/106) - optional ethereum_definitions.EthereumDefinitions definitions = 4; // network and/or token definitions + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required string primary_type = 2; // name of the root message struct + optional bool metamask_v4_compat = 3 [default=true]; // use MetaMask v4 (see https://github.com/MetaMask/eth-sig-util/issues/106) + optional ethereum.EthereumDefinitions definitions = 4; // network and/or token definitions } /** diff --git a/common/protob/messages-ethereum.proto b/common/protob/messages-ethereum.proto index ee4010bd083..a2014b6b092 100644 --- a/common/protob/messages-ethereum.proto +++ b/common/protob/messages-ethereum.proto @@ -6,7 +6,6 @@ option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_outer_classname = "TrezorMessageEthereum"; import "messages-common.proto"; -import "messages-ethereum-definitions.proto"; /** * Request: Ask device for public key corresponding to address_n path @@ -60,18 +59,18 @@ message EthereumAddress { * @next Failure */ message EthereumSignTx { - repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node - optional bytes nonce = 2 [default='']; // <=256 bit unsigned big endian - required bytes gas_price = 3; // <=256 bit unsigned big endian (in wei) - required bytes gas_limit = 4; // <=256 bit unsigned big endian - optional string to = 11 [default='']; // recipient address - optional bytes value = 6 [default='']; // <=256 bit unsigned big endian (in wei) - optional bytes data_initial_chunk = 7 [default='']; // The initial data chunk (<= 1024 bytes) - optional uint32 data_length = 8 [default=0]; // Length of transaction payload - required uint64 chain_id = 9; // Chain Id for EIP 155 - optional uint32 tx_type = 10; // Used for Wanchain - optional ethereum_definitions.EthereumDefinitions definitions = 12; // network and/or token definitions for tx - optional bool chunkify = 13; // display the address in chunks of 4 characters + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional bytes nonce = 2 [default='']; // <=256 bit unsigned big endian + required bytes gas_price = 3; // <=256 bit unsigned big endian (in wei) + required bytes gas_limit = 4; // <=256 bit unsigned big endian + optional string to = 11 [default='']; // recipient address + optional bytes value = 6 [default='']; // <=256 bit unsigned big endian (in wei) + optional bytes data_initial_chunk = 7 [default='']; // The initial data chunk (<= 1024 bytes) + optional uint32 data_length = 8 [default=0]; // Length of transaction payload + required uint64 chain_id = 9; // Chain Id for EIP 155 + optional uint32 tx_type = 10; // Used for Wanchain + optional EthereumDefinitions definitions = 12; // network and/or token definitions for tx + optional bool chunkify = 13; // display the address in chunks of 4 characters } /** @@ -82,19 +81,19 @@ message EthereumSignTx { * @next Failure */ message EthereumSignTxEIP1559 { - repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node - required bytes nonce = 2; // <=256 bit unsigned big endian - required bytes max_gas_fee = 3; // <=256 bit unsigned big endian (in wei) - required bytes max_priority_fee = 4; // <=256 bit unsigned big endian (in wei) - required bytes gas_limit = 5; // <=256 bit unsigned big endian - optional string to = 6 [default='']; // recipient address - required bytes value = 7; // <=256 bit unsigned big endian (in wei) - optional bytes data_initial_chunk = 8 [default='']; // The initial data chunk (<= 1024 bytes) - required uint32 data_length = 9; // Length of transaction payload - required uint64 chain_id = 10; // Chain Id for EIP 155 - repeated EthereumAccessList access_list = 11; // Access List - optional ethereum_definitions.EthereumDefinitions definitions = 12; // network and/or token definitions for tx - optional bool chunkify = 13; // display the address in chunks of 4 characters + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required bytes nonce = 2; // <=256 bit unsigned big endian + required bytes max_gas_fee = 3; // <=256 bit unsigned big endian (in wei) + required bytes max_priority_fee = 4; // <=256 bit unsigned big endian (in wei) + required bytes gas_limit = 5; // <=256 bit unsigned big endian + optional string to = 6 [default='']; // recipient address + required bytes value = 7; // <=256 bit unsigned big endian (in wei) + optional bytes data_initial_chunk = 8 [default='']; // The initial data chunk (<= 1024 bytes) + required uint32 data_length = 9; // Length of transaction payload + required uint64 chain_id = 10; // Chain Id for EIP 155 + repeated EthereumAccessList access_list = 11; // Access List + optional EthereumDefinitions definitions = 12; // network and/or token definitions for tx + optional bool chunkify = 13; // display the address in chunks of 4 characters message EthereumAccessList { required string address = 1; @@ -180,3 +179,12 @@ message EthereumTypedDataSignature { required bytes signature = 1; // signature of the typed data required string address = 2; // address used to sign the typed data } + +/** + * Contains an encoded network and/or token definition. See ethereum-definitions.md for details. + * @embed + */ +message EthereumDefinitions { + optional bytes encoded_network = 1; // encoded ethereum network + optional bytes encoded_token = 2; // encoded ethereum token +} diff --git a/common/protob/messages-solana.proto b/common/protob/messages-solana.proto index 2132a76d17b..0558c03efe5 100644 --- a/common/protob/messages-solana.proto +++ b/common/protob/messages-solana.proto @@ -55,6 +55,7 @@ message SolanaTxTokenAccountInfo { */ message SolanaTxAdditionalInfo { repeated SolanaTxTokenAccountInfo token_accounts_infos = 1; + optional SolanaDefinitions definitions = 2; // token definition } /** @@ -76,3 +77,11 @@ message SolanaSignTx { message SolanaTxSignature { required bytes signature = 1; // tx signature } + +/** + * Contains an encoded token definition. See solana-definitions.md for details. + * @embed + */ +message SolanaDefinitions { + optional bytes encoded_token = 1; // encoded solana token +} diff --git a/common/tests/fixtures/solana/sign_tx.predefined_transactions.json b/common/tests/fixtures/solana/sign_tx.predefined_transactions.json index d775e6a63c9..a7acc34cb86 100644 --- a/common/tests/fixtures/solana/sign_tx.predefined_transactions.json +++ b/common/tests/fixtures/solana/sign_tx.predefined_transactions.json @@ -57,6 +57,60 @@ "expected_signature": "f03cf48bd421011c5e04affeb52e8aac726d345871b74494acf59c507bda41ce4a3624fad0650312de32bb1b6afe5d22778bbb9eaabd0a16f24189181394290e" } }, + { + "description": "Transfer Token - predefined - with definitions", + "parameters": { + "address": "m/44'/501'/0'/0'", + "construct": { + "version": null, + "header": { + "signers": 1, + "readonly_signers": 0, + "readonly_non_signers": 2 + }, + "accounts": [ + "14CCvQzQzHCVgZM3j9soPnXuJXh1RmCfwLVUcdfbZVBS", + "74pZnim7gywyschy4MGkW6eZURv1DBXqwHTCqLRk63wz", + "92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx", + "GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou", + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + ], + "blockhash": "26kt5r94ZYdTZY27nAHqjropeDf7dPS19VkZJrta7duL", + "instructions": [ + { + "program_index": 4, + "accounts": { + "source_account": 1, + "token_mint": 3, + "destination_account": 2, + "owner": 0, + "multisig_signers": [] + }, + "data": { + "instruction_id": 12, + "amount": 11, + "decimals": 9 + } + } + ], + "luts": [] + }, + "additional_info": { + "token": "74727a643102ffffffff64000a20e303bf2ce35eddf29e99210c22a5203541ea2e3313bfed187a500eaf1b7e3560122b546f6b656e6b65675166655a79694e77414a624e62474b50465843577542766639537336323356513544411a0a46616b6520546f6b656e220746616b65546f6b000746efd16f0dbb3b452ba95ff78dd11596fe49d85ab27fbf18e35a61da5f9c76708e8f58302c4e6c27d58c6a9cc440ae2c28b710b1e095d45ac3f490013da8bc04", + "token_accounts_infos": [ + { + "base_address": "BkoECWJYM7w9qNZ6EGCoBtnkphWau6nZMPbPqvm4eYF4", + "token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "token_mint": "GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou", + "token_account": "92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx" + } + ] + } + }, + "result": { + "expected_signature": "f03cf48bd421011c5e04affeb52e8aac726d345871b74494acf59c507bda41ce4a3624fad0650312de32bb1b6afe5d22778bbb9eaabd0a16f24189181394290e" + } + }, { "description": "Create Token Account and Transfer Token - predefined", "parameters": { diff --git a/common/tools/cointool.py b/common/tools/cointool.py index 42500cd7482..6686d2351d0 100755 --- a/common/tools/cointool.py +++ b/common/tools/cointool.py @@ -142,7 +142,6 @@ def render_file( src: Path, dst: Path, coins: CoinsInfo, support_info: SupportInfo, models: list[str] ) -> None: """Renders `src` template into `dst`. - `src` is a filename, `dst` is an open file object. """ template = mako.template.Template(filename=str(src.resolve())) @@ -150,10 +149,11 @@ def render_file( DEFINITIONS_TIMESTAMP_PATH.read_text().strip() ) this_file = Path(src) + # REVIEW/TODO: adapt for solana? result = template.render( support_info=support_info, supported_on=make_support_filter(support_info), - ethereum_defs_timestamp=int(eth_defs_date.timestamp()), + defs_timestamp=int(eth_defs_date.timestamp()), THIS_FILE=this_file, ROOT=ROOT, **coins, diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 820f5e2e232..a7ede859a81 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -1105,6 +1105,7 @@ static void _librust_qstrs(void) { MP_QSTR_ripple__destination_tag_template; MP_QSTR_solana__account_index; MP_QSTR_solana__associated_token_account; + MP_QSTR_solana__base_fee; MP_QSTR_solana__confirm_multisig; MP_QSTR_solana__expected_fee; MP_QSTR_solana__instruction_accounts_template; @@ -1112,10 +1113,15 @@ static void _librust_qstrs(void) { MP_QSTR_solana__instruction_is_multisig; MP_QSTR_solana__is_provided_via_lookup_table_template; MP_QSTR_solana__lookup_table_address; + MP_QSTR_solana__max_fees_rent; + MP_QSTR_solana__max_rent_fee; MP_QSTR_solana__multiple_signers; + MP_QSTR_solana__priority_fee; MP_QSTR_solana__token_address; MP_QSTR_solana__transaction_contains_unknown_instructions; + MP_QSTR_solana__transaction_fee; MP_QSTR_solana__transaction_requires_x_signers_template; + MP_QSTR_solana__unknown; MP_QSTR_stellar__account_merge; MP_QSTR_stellar__account_thresholds; MP_QSTR_stellar__add_signer; diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs index 82a3456bfc5..231e5829557 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs +++ b/core/embed/rust/src/translations/generated/translated_string.rs @@ -1382,6 +1382,18 @@ pub enum TranslatedString { misc__enable_labeling = 973, // "Enable labeling?" #[cfg(feature = "universal_fw")] ethereum__unknown_contract_address_short = 974, // "Unknown contract address." + #[cfg(feature = "universal_fw")] + solana__base_fee = 975, // "Base fee" + #[cfg(feature = "universal_fw")] + solana__max_fees_rent = 976, // "Max fees and rent" + #[cfg(feature = "universal_fw")] + solana__max_rent_fee = 977, // "Max rent fee" + #[cfg(feature = "universal_fw")] + solana__priority_fee = 978, // "Priority fee" + #[cfg(feature = "universal_fw")] + solana__transaction_fee = 979, // "Transaction fee" + #[cfg(feature = "universal_fw")] + solana__unknown = 980, // "\"\"" } impl TranslatedString { @@ -2760,6 +2772,18 @@ impl TranslatedString { Self::misc__enable_labeling => "Enable labeling?", #[cfg(feature = "universal_fw")] Self::ethereum__unknown_contract_address_short => "Unknown contract address.", + #[cfg(feature = "universal_fw")] + Self::solana__base_fee => "Base fee", + #[cfg(feature = "universal_fw")] + Self::solana__max_fees_rent => "Max fees and rent", + #[cfg(feature = "universal_fw")] + Self::solana__max_rent_fee => "Max rent fee", + #[cfg(feature = "universal_fw")] + Self::solana__priority_fee => "Priority fee", + #[cfg(feature = "universal_fw")] + Self::solana__transaction_fee => "Transaction fee", + #[cfg(feature = "universal_fw")] + Self::solana__unknown => "\"\"", } } @@ -4137,6 +4161,18 @@ impl TranslatedString { Qstr::MP_QSTR_misc__enable_labeling => Some(Self::misc__enable_labeling), #[cfg(feature = "universal_fw")] Qstr::MP_QSTR_ethereum__unknown_contract_address_short => Some(Self::ethereum__unknown_contract_address_short), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_solana__base_fee => Some(Self::solana__base_fee), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_solana__max_fees_rent => Some(Self::solana__max_fees_rent), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_solana__max_rent_fee => Some(Self::solana__max_rent_fee), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_solana__priority_fee => Some(Self::solana__priority_fee), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_solana__transaction_fee => Some(Self::solana__transaction_fee), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_solana__unknown => Some(Self::solana__unknown), _ => None, } } diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi index 1641d82b022..c52db2e8c9a 100644 --- a/core/mocks/trezortranslate_keys.pyi +++ b/core/mocks/trezortranslate_keys.pyi @@ -770,6 +770,7 @@ class TR: sign_message__verify_address: str = "Verify address" solana__account_index: str = "Account index" solana__associated_token_account: str = "Associated token account" + solana__base_fee: str = "Base fee" solana__confirm_multisig: str = "Confirm multisig" solana__expected_fee: str = "Expected fee" solana__instruction_accounts_template: str = "Instruction contains {0} accounts and its data is {1} bytes long." @@ -777,9 +778,13 @@ class TR: solana__instruction_is_multisig: str = "The following instruction is a multisig instruction." solana__is_provided_via_lookup_table_template: str = "{0} is provided via a lookup table." solana__lookup_table_address: str = "Lookup table address" + solana__max_fees_rent: str = "Max fees and rent" + solana__max_rent_fee: str = "Max rent fee" solana__multiple_signers: str = "Multiple signers" + solana__priority_fee: str = "Priority fee" solana__token_address: str = "Token address" solana__transaction_contains_unknown_instructions: str = "Transaction contains unknown instructions." + solana__transaction_fee: str = "Transaction fee" solana__transaction_requires_x_signers_template: str = "Transaction requires {0} signers which increases the fee." stellar__account_merge: str = "Account Merge" stellar__account_thresholds: str = "Account Thresholds" diff --git a/core/src/all_modules.py b/core/src/all_modules.py index 70651ea3a89..00bc492f541 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -115,6 +115,8 @@ import trezor.enums.DebugWaitType trezor.enums.DecredStakingSpendType import trezor.enums.DecredStakingSpendType +trezor.enums.DefinitionType +import trezor.enums.DefinitionType trezor.enums.DisplayRotation import trezor.enums.DisplayRotation trezor.enums.FailureType @@ -333,6 +335,10 @@ import apps.common.coininfo apps.common.coins import apps.common.coins +apps.common.definitions +import apps.common.definitions +apps.common.definitions_constants +import apps.common.definitions_constants apps.common.keychain import apps.common.keychain apps.common.passphrase @@ -457,8 +463,6 @@ import trezor.enums.CardanoTxWitnessType trezor.enums.EthereumDataType import trezor.enums.EthereumDataType - trezor.enums.EthereumDefinitionType - import trezor.enums.EthereumDefinitionType trezor.enums.MoneroNetworkType import trezor.enums.MoneroNetworkType trezor.enums.NEMImportanceTransferMode @@ -567,8 +571,6 @@ import apps.ethereum apps.ethereum.definitions import apps.ethereum.definitions - apps.ethereum.definitions_constants - import apps.ethereum.definitions_constants apps.ethereum.get_address import apps.ethereum.get_address apps.ethereum.get_public_key @@ -743,6 +745,8 @@ import apps.solana apps.solana.constants import apps.solana.constants + apps.solana.definitions + import apps.solana.definitions apps.solana.format import apps.solana.format apps.solana.get_address diff --git a/core/src/apps/common/definitions.py b/core/src/apps/common/definitions.py new file mode 100644 index 00000000000..3b510680402 --- /dev/null +++ b/core/src/apps/common/definitions.py @@ -0,0 +1,91 @@ +from typing import TYPE_CHECKING + +from trezor.messages import EthereumNetworkInfo, EthereumTokenInfo, SolanaTokenInfo +from trezor.wire import DataError + +if TYPE_CHECKING: + from typing import TypeVar + + # NOTE: it's important all DefType variants can't be cross-parsed + DefType = TypeVar( + "DefType", EthereumNetworkInfo, EthereumTokenInfo, SolanaTokenInfo + ) + + +def decode_definition(definition: bytes, expected_type: type[DefType]) -> DefType: + from trezor.crypto.cosi import verify as cosi_verify + from trezor.crypto.hashlib import sha256 + from trezor.enums import DefinitionType + from trezor.protobuf import decode as protobuf_decode + from trezor.utils import BufferReader + + from apps.common import readers + + from . import definitions_constants as consts + + r = BufferReader(definition) + + # determine the type number from the expected type + expected_type_number = DefinitionType.ETHEREUM_NETWORK + # TODO: can't check equality of MsgDefObjs now, so we check the name + if expected_type.MESSAGE_NAME == EthereumTokenInfo.MESSAGE_NAME: + expected_type_number = DefinitionType.ETHEREUM_TOKEN + if expected_type.MESSAGE_NAME == SolanaTokenInfo.MESSAGE_NAME: + expected_type_number = DefinitionType.SOLANA_TOKEN + + try: + # first check format version + if r.read_memoryview(len(consts.FORMAT_VERSION)) != consts.FORMAT_VERSION: + raise DataError("Invalid definition") + + # second check the type of the data + if r.get() != expected_type_number: + raise DataError("Definition type mismatch") + + # third check data version + if readers.read_uint32_le(r) < consts.MIN_DATA_VERSION: + raise DataError("Definition is outdated") + + # get payload + payload_length = readers.read_uint16_le(r) + payload = r.read_memoryview(payload_length) + + # at the end compute Merkle tree root hash using + # provided leaf data (payload with prefix) and proof + hasher = sha256(b"\x00") + hasher.update(memoryview(definition)[: r.offset]) + hash = hasher.digest() + proof_length = r.get() + for _ in range(proof_length): + proof_entry = r.read_memoryview(32) + hash_a = min(hash, proof_entry) + hash_b = max(hash, proof_entry) + hasher = sha256(b"\x01") + hasher.update(hash_a) + hasher.update(hash_b) + hash = hasher.digest() + + sigmask = r.get() + signature = r.read_memoryview(64) + + if r.remaining_count(): + raise DataError("Invalid definition") + + except EOFError: + raise DataError("Invalid definition") + + # verify signature + result = cosi_verify(signature, hash, consts.THRESHOLD, consts.PUBLIC_KEYS, sigmask) + if __debug__: + debug_result = cosi_verify( + signature, hash, consts.THRESHOLD, consts.DEV_PUBLIC_KEYS, sigmask + ) + result = result or debug_result + if not result: + raise DataError("Invalid definition signature") + + # decode it if it's OK + try: + return protobuf_decode(payload, expected_type, True) + except (ValueError, EOFError): + raise DataError("Invalid definition") diff --git a/core/src/apps/ethereum/definitions_constants.py b/core/src/apps/common/definitions_constants.py similarity index 97% rename from core/src/apps/ethereum/definitions_constants.py rename to core/src/apps/common/definitions_constants.py index 0153c883b0b..73119024474 100644 --- a/core/src/apps/ethereum/definitions_constants.py +++ b/core/src/apps/common/definitions_constants.py @@ -9,7 +9,7 @@ b"\xb8\xd2\xb2\x1d\xe2\x71\x24\xf0\x51\x1f\x90\x3a\xe7\xe6\x0e\x07\x96\x18\x10\xa0\xb8\xf2\x8e\xa7\x55\xfa\x50\x36\x7a\x8a\x2b\x8b", ) -MIN_DATA_VERSION = 1736423322 +MIN_DATA_VERSION = 1725628031 FORMAT_VERSION = b"trzd1" if __debug__: diff --git a/core/src/apps/ethereum/definitions_constants.py.mako b/core/src/apps/common/definitions_constants.py.mako similarity index 95% rename from core/src/apps/ethereum/definitions_constants.py.mako rename to core/src/apps/common/definitions_constants.py.mako index 9623dee0ceb..beb1d70dc9c 100644 --- a/core/src/apps/ethereum/definitions_constants.py.mako +++ b/core/src/apps/common/definitions_constants.py.mako @@ -9,7 +9,7 @@ PUBLIC_KEYS = ( b"\xb8\xd2\xb2\x1d\xe2\x71\x24\xf0\x51\x1f\x90\x3a\xe7\xe6\x0e\x07\x96\x18\x10\xa0\xb8\xf2\x8e\xa7\x55\xfa\x50\x36\x7a\x8a\x2b\x8b", ) -MIN_DATA_VERSION = ${ethereum_defs_timestamp} +MIN_DATA_VERSION = ${defs_timestamp} FORMAT_VERSION = b"trzd1" if __debug__: diff --git a/core/src/apps/ethereum/definitions.py b/core/src/apps/ethereum/definitions.py index fb23a46b04c..355dc9ff7b3 100644 --- a/core/src/apps/ethereum/definitions.py +++ b/core/src/apps/ethereum/definitions.py @@ -4,88 +4,8 @@ from trezor.wire import DataError if TYPE_CHECKING: - from typing import TypeVar - from typing_extensions import Self - DefType = TypeVar("DefType", EthereumNetworkInfo, EthereumTokenInfo) - - -def decode_definition(definition: bytes, expected_type: type[DefType]) -> DefType: - from trezor.crypto.cosi import verify as cosi_verify - from trezor.crypto.hashlib import sha256 - from trezor.enums import EthereumDefinitionType - from trezor.protobuf import decode as protobuf_decode - from trezor.utils import BufferReader - - from apps.common import readers - - from . import definitions_constants as consts - - # check network definition - r = BufferReader(definition) - expected_type_number = EthereumDefinitionType.NETWORK - # TODO: can't check equality of MsgDefObjs now, so we check the name - if expected_type.MESSAGE_NAME == EthereumTokenInfo.MESSAGE_NAME: - expected_type_number = EthereumDefinitionType.TOKEN - - try: - # first check format version - if r.read_memoryview(len(consts.FORMAT_VERSION)) != consts.FORMAT_VERSION: - raise DataError("Invalid Ethereum definition") - - # second check the type of the data - if r.get() != expected_type_number: - raise DataError("Definition type mismatch") - - # third check data version - if readers.read_uint32_le(r) < consts.MIN_DATA_VERSION: - raise DataError("Definition is outdated") - - # get payload - payload_length = readers.read_uint16_le(r) - payload = r.read_memoryview(payload_length) - - # at the end compute Merkle tree root hash using - # provided leaf data (payload with prefix) and proof - hasher = sha256(b"\x00") - hasher.update(memoryview(definition)[: r.offset]) - hash = hasher.digest() - proof_length = r.get() - for _ in range(proof_length): - proof_entry = r.read_memoryview(32) - hash_a = min(hash, proof_entry) - hash_b = max(hash, proof_entry) - hasher = sha256(b"\x01") - hasher.update(hash_a) - hasher.update(hash_b) - hash = hasher.digest() - - sigmask = r.get() - signature = r.read_memoryview(64) - - if r.remaining_count(): - raise DataError("Invalid Ethereum definition") - - except EOFError: - raise DataError("Invalid Ethereum definition") - - # verify signature - result = cosi_verify(signature, hash, consts.THRESHOLD, consts.PUBLIC_KEYS, sigmask) - if __debug__: - debug_result = cosi_verify( - signature, hash, consts.THRESHOLD, consts.DEV_PUBLIC_KEYS, sigmask - ) - result = result or debug_result - if not result: - raise DataError("Invalid definition signature") - - # decode it if it's OK - try: - return protobuf_decode(payload, expected_type, True) - except (ValueError, EOFError): - raise DataError("Invalid Ethereum definition") - class Definitions: """Class that holds Ethereum definitions - network and tokens. @@ -106,6 +26,8 @@ def from_encoded( chain_id: int | None = None, slip44: int | None = None, ) -> Self: + from apps.common.definitions import decode_definition + from .networks import UNKNOWN_NETWORK, by_chain_id, by_slip44 network = UNKNOWN_NETWORK diff --git a/core/src/apps/solana/constants.py b/core/src/apps/solana/constants.py index 79d6115b5c1..2308ade5ce7 100644 --- a/core/src/apps/solana/constants.py +++ b/core/src/apps/solana/constants.py @@ -4,3 +4,17 @@ SOLANA_BASE_FEE_LAMPORTS = const(5000) SOLANA_COMPUTE_UNIT_LIMIT = const(200000) +# Rent exemption is granted after two years of rent +SOLANA_RENT_EXEMPTION_YEARS = const(2) +# NOTE: this is a hard-coded network parameter, +# it CAN change in the future +SOLANA_RENT_LAMPORTS_PER_BYTE_YEAR = const(3480) + +# Size of a Token account +# https://github.com/solana-program/token/blob/08aa3ccecb30692bca18d6f927804337de82d5ff/program/src/state.rs#L134 +SOLANA_TOKEN_ACCOUNT_SIZE = const(165) +# Max size of a Token22 account +# https://github.com/solana-program/token-2022/blob/d9cfcf32cf5fbb3ee32f9f873d3fe3c94356e981/program/src/extension/mod.rs#L1299 +SOLANA_TOKEN22_MAX_ACCOUNT_SIZE = const(195) +# Each Solana account has a 128 bytes overhead +SOLANA_ACCOUNT_OVERHEAD_SIZE = const(128) diff --git a/core/src/apps/solana/definitions.py b/core/src/apps/solana/definitions.py new file mode 100644 index 00000000000..06e521aca29 --- /dev/null +++ b/core/src/apps/solana/definitions.py @@ -0,0 +1,38 @@ +from typing import TYPE_CHECKING + +from trezor.messages import SolanaTokenInfo + +if TYPE_CHECKING: + from typing_extensions import Self + + +class Definitions: + """Class that holds Solana token definitions.""" + + def __init__(self, tokens: dict[bytes, SolanaTokenInfo]) -> None: + self._tokens = tokens + + @classmethod + def from_encoded( + cls, + encoded_token: bytes | None, + ) -> Self: + from apps.common.definitions import decode_definition + + tokens: dict[bytes, SolanaTokenInfo] = {} + + # get token definition + if encoded_token is not None: + token = decode_definition(encoded_token, SolanaTokenInfo) + tokens[token.mint] = token + + return cls(tokens) + + def get_token(self, mint: bytes) -> SolanaTokenInfo: + UNKNOWN_TOKEN = SolanaTokenInfo( + mint=b"", + program_id="", + name="Unknown token", + ticker="[UNKN]", + ) + return self._tokens.get(mint, UNKNOWN_TOKEN) diff --git a/core/src/apps/solana/layout.py b/core/src/apps/solana/layout.py index c03c0c8c96c..5260c36ca4d 100644 --- a/core/src/apps/solana/layout.py +++ b/core/src/apps/solana/layout.py @@ -7,6 +7,7 @@ from trezor.ui.layouts import ( confirm_metadata, confirm_properties, + confirm_solana_recipient, confirm_solana_tx, confirm_value, ) @@ -18,6 +19,7 @@ if TYPE_CHECKING: from typing import Sequence + from .definitions import Definitions from .transaction.instructions import Instruction, SystemProgramTransferInstruction from .types import AddressReference @@ -51,6 +53,7 @@ async def confirm_instruction( instruction_index: int, signer_path: list[int], signer_public_key: bytes, + definitions: Definitions | None, ) -> None: instruction_title = ( f"{instruction_index}/{instructions_count}: {instruction.ui_name}" @@ -110,17 +113,17 @@ async def confirm_instruction( continue account_data: list[tuple[str, str]] = [] + # account included in the transaction directly if len(account_value) == 2: - signer_suffix = "" - if account_value[0] == signer_public_key: - signer_suffix = f" ({TR.words__signer})" - - account_data.append( - ( - ui_property.display_name, - f"{base58.encode(account_value[0])}{signer_suffix}", - ) - ) + account_description = f"{base58.encode(account_value[0])}" + if account_template.is_token_mint and definitions: + token = definitions.get_token(account_value[0]) + account_description = f"{token.ticker}\n{account_description}" + elif account_value[0] == signer_public_key: + account_description = f"{account_description} ({TR.words__signer})" + + account_data.append((ui_property.display_name, account_description)) + # lookup table address reference elif len(account_value) == 3: account_data += _get_address_reference_props( account_value, ui_property.display_name @@ -264,26 +267,28 @@ async def confirm_unsupported_program_confirm( async def confirm_system_transfer( transfer_instruction: SystemProgramTransferInstruction, - fee: int, + base_fee: int, + priority_fee: int, signer_path: list[int], blockhash: bytes, ) -> None: - await confirm_value( + await confirm_solana_recipient( + recipient=base58.encode(transfer_instruction.recipient_account[0]), title=TR.words__recipient, - value=base58.encode(transfer_instruction.recipient_account[0]), - description="", - br_name="confirm_recipient", - br_code=ButtonRequestType.ConfirmOutput, - verb=TR.buttons__continue, + items=( + (f"{TR.words__account}:", _format_path(signer_path)), + (f"{TR.address_details__derivation_path}:", address_n_to_str(signer_path)), + (f"{TR.words__blockhash}:", base58.encode(blockhash)), + ), ) await confirm_custom_transaction( transfer_instruction.lamports, 9, "SOL", - fee, - signer_path, - blockhash, + base_fee, + priority_fee, + 0, ) @@ -291,23 +296,28 @@ async def confirm_token_transfer( destination_account: bytes, token_account: bytes, token_mint: bytes, + token_ticker: str, amount: int, decimals: int, - fee: int, + base_fee: int, + priority_fee: int, + rent: int, signer_path: list[int], blockhash: bytes, ) -> None: - await confirm_value( + items = [(TR.words__recipient, base58.encode(destination_account))] + if token_account != destination_account: + items.append( + (f"{TR.solana__associated_token_account}:", base58.encode(token_account)) + ) + + await confirm_solana_recipient( + recipient=base58.encode(destination_account), title=TR.words__recipient, - value=base58.encode(destination_account), - description="", - br_name="confirm_recipient", - br_code=ButtonRequestType.ConfirmOutput, - verb=TR.buttons__continue, - info_items=( - ((f"{TR.solana__associated_token_account}:", base58.encode(token_account)),) - if token_account != destination_account - else None + items=( + (f"{TR.words__account}:", _format_path(signer_path)), + (f"{TR.address_details__derivation_path}:", address_n_to_str(signer_path)), + (f"{TR.words__blockhash}:", base58.encode(blockhash)), ), ) @@ -323,42 +333,77 @@ async def confirm_token_transfer( await confirm_custom_transaction( amount, decimals, - "[TOKEN]", - fee, - signer_path, - blockhash, + token_ticker, + base_fee, + priority_fee, + rent, ) +def _fee_ui_info( + has_unsupported_instructions: bool, base_fee: int, priority_fee: int, rent: int +): + fee_items: list[tuple[str, str]] = [] + if has_unsupported_instructions: + fee_title = f"{TR.solana__max_fees_rent}:" + fee_str = TR.words__unknown + else: + fee_str = f"{format_amount(base_fee + priority_fee + rent, 9)} SOL" + base_fee_str = f"{format_amount(base_fee, 9)} SOL" + fee_items.append((TR.solana__base_fee, base_fee_str)) + if priority_fee: + priority_fee_str = f"{format_amount(priority_fee, 9)} SOL" + fee_items.append((TR.solana__priority_fee, priority_fee_str)) + if rent: + fee_title = f"{TR.solana__max_fees_rent}:" + rent_str = f"{format_amount(rent, 9)} SOL" + fee_items.append((TR.solana__max_rent_fee, rent_str)) + else: + fee_title = f"{TR.solana__transaction_fee}:" + return fee_title, fee_str, fee_items + + async def confirm_custom_transaction( amount: int, decimals: int, unit: str, - fee: int, - signer_path: list[int], - blockhash: bytes, + base_fee: int, + priority_fee: int, + rent: int, ) -> None: + fee_title, fee_str, fee_items = _fee_ui_info(False, base_fee, priority_fee, rent) await confirm_solana_tx( amount=f"{format_amount(amount, decimals)} {unit}", - fee=f"{format_amount(fee, 9)} SOL", - fee_title=f"{TR.solana__expected_fee}:", - items=( - (f"{TR.words__account}:", _format_path(signer_path)), - (f"{TR.words__blockhash}:", base58.encode(blockhash)), - ), + fee=fee_str, + fee_title=fee_title, + items=fee_items, ) async def confirm_transaction( - signer_path: list[int], blockhash: bytes, fee: int + signer_path: list[int], + blockhash: bytes, + base_fee: int, + priority_fee: int, + rent: int, + has_unsupported_instructions: bool, ) -> None: - await confirm_solana_tx( - amount="", - amount_title="", - fee=f"{format_amount(fee, 9)} SOL", - fee_title=f"{TR.solana__expected_fee}:", - items=( + fee_title, fee_str, fee_items = _fee_ui_info( + has_unsupported_instructions, base_fee, priority_fee, rent + ) + await confirm_properties( + "confirm_account_blockhash", + TR.words__title_information, + ( (f"{TR.words__account}:", _format_path(signer_path)), + (f"{TR.address_details__derivation_path}:", address_n_to_str(signer_path)), (f"{TR.words__blockhash}:", base58.encode(blockhash)), ), ) + await confirm_solana_tx( + amount="", + amount_title="", + fee=fee_str, + fee_title=fee_title, + items=fee_items, + ) diff --git a/core/src/apps/solana/predefined_transaction.py b/core/src/apps/solana/predefined_transaction.py index e2f6edf5e06..38ede39d10d 100644 --- a/core/src/apps/solana/predefined_transaction.py +++ b/core/src/apps/solana/predefined_transaction.py @@ -11,7 +11,7 @@ ) if TYPE_CHECKING: - from trezor.messages import SolanaTxAdditionalInfo + from .types import AdditionalTxInfo TransferTokenInstruction = ( TokenProgramTransferCheckedInstruction @@ -114,10 +114,12 @@ def is_predefined_token_transfer( async def try_confirm_token_transfer_transaction( transaction: Transaction, - fee: int, + base_fee: int, + priority_fee: int, + rent: int, signer_path: list[int], blockhash: bytes, - additional_info: SolanaTxAdditionalInfo | None = None, + additional_info: AdditionalTxInfo | None = None, ) -> bool: from .layout import confirm_token_transfer from .token_account import try_get_token_account_base_address @@ -154,13 +156,20 @@ async def try_confirm_token_transfer_transaction( ] ) + token_ticker = "[UNKN]" + if additional_info and additional_info.definitions: + token_ticker = additional_info.definitions.get_token(token_mint).ticker + await confirm_token_transfer( token_account if base_address is None else base_address, token_account, token_mint, + token_ticker, total_token_amount, transfer_token_instructions[0].decimals, - fee, + base_fee, + priority_fee, + rent, signer_path, blockhash, ) @@ -169,10 +178,12 @@ async def try_confirm_token_transfer_transaction( async def try_confirm_predefined_transaction( transaction: Transaction, - fee: int, + base_fee: int, + priority_fee: int, + rent: int, signer_path: list[int], blockhash: bytes, - additional_info: SolanaTxAdditionalInfo | None = None, + additional_info: AdditionalTxInfo | None = None, ) -> bool: from .layout import confirm_system_transfer from .transaction.instructions import SystemProgramTransferInstruction @@ -186,9 +197,17 @@ async def try_confirm_predefined_transaction( if instructions_count == 1: if SystemProgramTransferInstruction.is_type_of(instructions[0]): - await confirm_system_transfer(instructions[0], fee, signer_path, blockhash) + await confirm_system_transfer( + instructions[0], base_fee, priority_fee, signer_path, blockhash + ) return True return await try_confirm_token_transfer_transaction( - transaction, fee, signer_path, blockhash, additional_info + transaction, + base_fee, + priority_fee, + rent, + signer_path, + blockhash, + additional_info, ) diff --git a/core/src/apps/solana/sign_tx.py b/core/src/apps/solana/sign_tx.py index 828f4b9561d..1620b952271 100644 --- a/core/src/apps/solana/sign_tx.py +++ b/core/src/apps/solana/sign_tx.py @@ -1,11 +1,14 @@ from typing import TYPE_CHECKING +from trezor.crypto import base58 from trezor.wire import DataError from apps.common.keychain import with_slip44_keychain from . import CURVE, PATTERNS, SLIP44_ID +from .definitions import Definitions from .transaction import Transaction +from .types import AdditionalTxInfo if TYPE_CHECKING: from trezor.messages import SolanaSignTx, SolanaTxSignature @@ -56,16 +59,32 @@ async def sign_tx( br_code=ButtonRequestType.Other, ) - fee = calculate_fee(transaction) + base_fee, priority_fee = calculate_fee(transaction) + rent = calculate_rent(transaction) + + additional_tx_info = AdditionalTxInfo.from_solana_tx_additional_info( + msg.additional_info + ) if not await try_confirm_predefined_transaction( - transaction, fee, address_n, transaction.blockhash, msg.additional_info + transaction, + base_fee, + priority_fee, + rent, + address_n, + transaction.blockhash, + additional_tx_info, ): - await confirm_instructions(address_n, signer_public_key, transaction) + await confirm_instructions( + address_n, signer_public_key, transaction, additional_tx_info + ) await confirm_transaction( address_n, transaction.blockhash, - calculate_fee(transaction), + base_fee, + priority_fee, + rent, + _has_unsupported_instructions(transaction), ) signature = ed25519.sign(node.private_key(), serialized_tx) @@ -73,9 +92,25 @@ async def sign_tx( return SolanaTxSignature(signature=signature) +def _has_unsupported_instructions(transaction: Transaction) -> bool: + visible_instructions = transaction.get_visible_instructions() + for instruction in visible_instructions: + if not ( + instruction.is_program_supported and instruction.is_instruction_supported + ): + return True + return False + + async def confirm_instructions( - signer_path: list[int], signer_public_key: bytes, transaction: Transaction -) -> None: + signer_path: list[int], + signer_public_key: bytes, + transaction: Transaction, + additional_info: AdditionalTxInfo | None, +): + definitions: Definitions | None = ( + additional_info.definitions if additional_info else None + ) visible_instructions = transaction.get_visible_instructions() instructions_count = len(visible_instructions) @@ -109,10 +144,11 @@ async def confirm_instructions( instruction_index, signer_path, signer_public_key, + definitions, ) -def calculate_fee(transaction: Transaction) -> int: +def calculate_fee(transaction: Transaction) -> tuple[int, int]: import math from .constants import SOLANA_BASE_FEE_LAMPORTS, SOLANA_COMPUTE_UNIT_LIMIT @@ -152,4 +188,85 @@ def calculate_fee(transaction: Transaction) -> int: unit_price = instruction.lamports is_unit_price_set = True - return int(base_fee + math.ceil(unit_price * unit_limit / 1000000)) + priority_fee = math.ceil(unit_price * unit_limit / 1000000) + return base_fee, priority_fee + + +def calculate_rent(transaction: Transaction) -> int: + """ + Returns max rent exemption in lamports. + + To estimate rent exemption from a transaction we need to go over the instructions. + When new accounts are created, space must be allocated for them, rent exemption value depends on that space. + + There are a handful of instruction that allocate space: + - System program create account instruction (the space data parameter) + - System program create account with seed instruction (the space data parameter) + - System program allocate instruction (the space data parameter) + - System program allocate with seed instruction (the space data parameter) + - Associated token account program create instruction (165 bytes for Token, 170-195 for Token22) + - Associated token account program create idempotent instruction (165 bytes for Token, 170-195 for Token22, might not allocate) + loo + Associated token account program allocates space based on used extensions which must be enabled in the same transaction. + Currently, the token22 extensions aren't supported, so the max value of 195 bytes is assumed. + The min Token22 account size is the base Token account size plus some overhead. + The max Token22 account size is derived from the program source code: + https://github.com/solana-program/token-2022/blob/d9cfcf32cf5fbb3ee32f9f873d3fe3c94356e981/program/src/extension/mod.rs#L1299 + Note that Token/Token22 programs don't allocate space by themselves, they only use preallocated accounts. + """ + from .constants import ( + SOLANA_ACCOUNT_OVERHEAD_SIZE, + SOLANA_RENT_EXEMPTION_YEARS, + SOLANA_RENT_LAMPORTS_PER_BYTE_YEAR, + SOLANA_TOKEN22_MAX_ACCOUNT_SIZE, + SOLANA_TOKEN_ACCOUNT_SIZE, + ) + from .transaction.instructions import ( + _ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, + _ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE, + _ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE_IDEMPOTENT, + _SYSTEM_PROGRAM_ID, + _SYSTEM_PROGRAM_ID_INS_ALLOCATE, + _SYSTEM_PROGRAM_ID_INS_ALLOCATE_WITH_SEED, + _SYSTEM_PROGRAM_ID_INS_CREATE_ACCOUNT, + _SYSTEM_PROGRAM_ID_INS_CREATE_ACCOUNT_WITH_SEED, + _TOKEN_2022_PROGRAM_ID, + _TOKEN_PROGRAM_ID, + ) + + allocation = 0 + for instruction in transaction.instructions: + if instruction.program_id == _SYSTEM_PROGRAM_ID and ( + instruction.instruction_id + in ( + _SYSTEM_PROGRAM_ID_INS_CREATE_ACCOUNT, + _SYSTEM_PROGRAM_ID_INS_CREATE_ACCOUNT_WITH_SEED, + _SYSTEM_PROGRAM_ID_INS_ALLOCATE, + _SYSTEM_PROGRAM_ID_INS_ALLOCATE_WITH_SEED, + ) + ): + allocation += ( + instruction.parsed_data["space"] + SOLANA_ACCOUNT_OVERHEAD_SIZE + ) + elif instruction.program_id == _ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID and ( + instruction.instruction_id + in ( + _ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE, + _ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE_IDEMPOTENT, + ) + ): + spl_token_account = transaction.get_account_address( + instruction.parsed_accounts["spl_token"] + ) + if spl_token_account == base58.decode(_TOKEN_PROGRAM_ID): + allocation += SOLANA_TOKEN_ACCOUNT_SIZE + SOLANA_ACCOUNT_OVERHEAD_SIZE + elif spl_token_account == base58.decode(_TOKEN_2022_PROGRAM_ID): + allocation += ( + SOLANA_TOKEN22_MAX_ACCOUNT_SIZE + SOLANA_ACCOUNT_OVERHEAD_SIZE + ) + + rent_exemption = ( + allocation * SOLANA_RENT_LAMPORTS_PER_BYTE_YEAR * SOLANA_RENT_EXEMPTION_YEARS + ) + + return rent_exemption diff --git a/core/src/apps/solana/transaction/__init__.py b/core/src/apps/solana/transaction/__init__.py index 21e2a1cde1b..d341ac3cf6c 100644 --- a/core/src/apps/solana/transaction/__init__.py +++ b/core/src/apps/solana/transaction/__init__.py @@ -209,3 +209,10 @@ def get_visible_instructions(self) -> list[Instruction]: for instruction in self.instructions if not instruction.is_ui_hidden ] + + def get_account_address(self, account: Account) -> bytes: + if len(account) == 2: + return account[0] + else: + _, index, _ = account + return self.addresses[index][0] diff --git a/core/src/apps/solana/transaction/instructions.py b/core/src/apps/solana/transaction/instructions.py index 4bcd8887081..55a123e5293 100644 --- a/core/src/apps/solana/transaction/instructions.py +++ b/core/src/apps/solana/transaction/instructions.py @@ -902,11 +902,13 @@ def get_instruction( "funding_account", True, False, + False, ), AccountTemplate( "new_account", False, False, + False, ), ], [ @@ -959,6 +961,7 @@ def get_instruction( "assigned_account", True, False, + False, ), ], [ @@ -1004,11 +1007,13 @@ def get_instruction( "funding_account", True, False, + False, ), AccountTemplate( "recipient_account", False, False, + False, ), ], [ @@ -1089,16 +1094,19 @@ def get_instruction( "funding_account", True, False, + False, ), AccountTemplate( "created_account", False, False, + False, ), AccountTemplate( "base_account", True, True, + False, ), ], [ @@ -1143,16 +1151,19 @@ def get_instruction( "nonce_account", False, False, + False, ), AccountTemplate( "recent_blockhashes_sysvar", False, False, + False, ), AccountTemplate( "nonce_authority", True, False, + False, ), ], [ @@ -1198,26 +1209,31 @@ def get_instruction( "nonce_account", False, False, + False, ), AccountTemplate( "recipient_account", False, False, + False, ), AccountTemplate( "recent_blockhashes_sysvar", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), AccountTemplate( "nonce_authority", True, False, + False, ), ], [ @@ -1277,16 +1293,19 @@ def get_instruction( "nonce_account", False, False, + False, ), AccountTemplate( "recent_blockhashes_sysvar", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), ], [ @@ -1332,11 +1351,13 @@ def get_instruction( "nonce_account", False, False, + False, ), AccountTemplate( "nonce_authority", True, False, + False, ), ], [ @@ -1389,6 +1410,7 @@ def get_instruction( "new_account", True, False, + False, ), ], [ @@ -1455,11 +1477,13 @@ def get_instruction( "allocated_account", False, False, + False, ), AccountTemplate( "base_account", True, False, + False, ), ], [ @@ -1519,11 +1543,13 @@ def get_instruction( "assigned_account", False, False, + False, ), AccountTemplate( "base_account", True, False, + False, ), ], [ @@ -1583,16 +1609,19 @@ def get_instruction( "funding_account", False, False, + False, ), AccountTemplate( "base_account", True, False, + False, ), AccountTemplate( "recipient_account", False, False, + False, ), ], [ @@ -1637,6 +1666,7 @@ def get_instruction( "nonce_account", False, False, + False, ), ], [ @@ -1718,11 +1748,13 @@ def get_instruction( "uninitialized_stake_account", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), ], [ @@ -1803,21 +1835,25 @@ def get_instruction( "stake_account", False, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "stake_or_withdraw_authority", True, False, + False, ), AccountTemplate( "lockup_authority", True, True, + False, ), ], [ @@ -1876,31 +1912,37 @@ def get_instruction( "initialized_stake_account", False, False, + False, ), AccountTemplate( "vote_account", False, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "stake_history_sysvar", False, False, + False, ), AccountTemplate( "config_account", False, False, + False, ), AccountTemplate( "stake_authority", True, False, + False, ), ], [ @@ -1953,16 +1995,19 @@ def get_instruction( "stake_account", False, False, + False, ), AccountTemplate( "uninitialized_stake_account", False, False, + False, ), AccountTemplate( "stake_authority", True, False, + False, ), ], [ @@ -2022,31 +2067,37 @@ def get_instruction( "stake_account", False, False, + False, ), AccountTemplate( "recipient_account", False, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "stake_history_sysvar", False, False, + False, ), AccountTemplate( "withdrawal_authority", True, False, + False, ), AccountTemplate( "lockup_authority", True, True, + False, ), ], [ @@ -2098,16 +2149,19 @@ def get_instruction( "delegated_stake_account", False, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "stake_authority", True, False, + False, ), ], [ @@ -2167,11 +2221,13 @@ def get_instruction( "initialized_stake_account", False, False, + False, ), AccountTemplate( "lockup_or_withdraw_authority", True, False, + False, ), ], [ @@ -2230,26 +2286,31 @@ def get_instruction( "destination_stake_account", False, False, + False, ), AccountTemplate( "source_stake_account", False, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "stake_history_sysvar", False, False, + False, ), AccountTemplate( "stake_authority", True, False, + False, ), ], [ @@ -2323,21 +2384,25 @@ def get_instruction( "stake_account", False, False, + False, ), AccountTemplate( "stake_or_withdraw_authority", True, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "lockup_authority", True, True, + False, ), ], [ @@ -2396,21 +2461,25 @@ def get_instruction( "uninitialized_stake_account", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), AccountTemplate( "stake_authority", False, False, + False, ), AccountTemplate( "withdrawal_authority", True, False, + False, ), ], [ @@ -2463,26 +2532,31 @@ def get_instruction( "stake_account", False, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "stake_or_withdraw_authority", True, False, + False, ), AccountTemplate( "new_stake_or_withdraw_authority", True, False, + False, ), AccountTemplate( "lockup_authority", True, True, + False, ), ], [ @@ -2563,26 +2637,31 @@ def get_instruction( "stake_account", False, False, + False, ), AccountTemplate( "stake_or_withdraw_authority", True, False, + False, ), AccountTemplate( "clock_sysvar", False, False, + False, ), AccountTemplate( "new_stake_or_withdraw_authority", True, False, + False, ), AccountTemplate( "lockup_authority", True, True, + False, ), ], [ @@ -2656,16 +2735,19 @@ def get_instruction( "stake_account", False, False, + False, ), AccountTemplate( "lockup_or_withdraw_authority", True, False, + False, ), AccountTemplate( "new_lockup_authority", True, True, + False, ), ], [ @@ -2850,21 +2932,25 @@ def get_instruction( "account_to_initialize", False, False, + False, ), AccountTemplate( "mint_account", False, False, + False, ), AccountTemplate( "owner", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), ], [ @@ -2917,16 +3003,19 @@ def get_instruction( "multisig_account", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), AccountTemplate( "signer_accounts", False, False, + False, ), ], [ @@ -2972,16 +3061,19 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "destination_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -3041,16 +3133,19 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "delegate_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -3095,11 +3190,13 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -3152,11 +3249,13 @@ def get_instruction( "mint_account", False, False, + False, ), AccountTemplate( "current_authority", True, False, + False, ), ], [ @@ -3216,23 +3315,33 @@ def get_instruction( "mint", False, False, + True, ), AccountTemplate( "account_to_mint", False, False, + False, ), AccountTemplate( "minting_authority", True, False, + False, ), ], [ + UIProperty( + None, + "mint", + "Mint token", + False, + None, + ), UIProperty( "amount", None, - "Mint tokens", + "Mint amount", False, None, ), @@ -3278,23 +3387,33 @@ def get_instruction( "account_to_burn_from", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "owner", True, False, + False, ), ], [ + UIProperty( + None, + "token_mint", + "Burn token", + False, + None, + ), UIProperty( "amount", None, - "Burn tokens", + "Burn amount", False, None, ), @@ -3332,16 +3451,19 @@ def get_instruction( "account_to_close", False, False, + False, ), AccountTemplate( "destination_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -3386,16 +3508,19 @@ def get_instruction( "account_to_freeze", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "freeze_authority", True, False, + False, ), ], [ @@ -3406,6 +3531,13 @@ def get_instruction( False, None, ), + UIProperty( + None, + "token_mint", + "Token", + False, + None, + ), UIProperty( None, "freeze_authority", @@ -3433,16 +3565,19 @@ def get_instruction( "account_to_freeze", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "freeze_authority", True, False, + False, ), ], [ @@ -3453,6 +3588,13 @@ def get_instruction( False, None, ), + UIProperty( + None, + "token_mint", + "Token", + False, + None, + ), UIProperty( None, "freeze_authority", @@ -3495,21 +3637,25 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "destination_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -3583,21 +3729,25 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "delegate", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -3671,16 +3821,19 @@ def get_instruction( "mint", False, False, + True, ), AccountTemplate( "account_to_mint", False, False, + False, ), AccountTemplate( "minting_authority", True, False, + False, ), ], [ @@ -3747,16 +3900,19 @@ def get_instruction( "account_to_burn_from", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -3816,16 +3972,19 @@ def get_instruction( "account_to_initialize", False, False, + False, ), AccountTemplate( "mint_account", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), ], [ @@ -3870,6 +4029,7 @@ def get_instruction( "token_account", False, False, + False, ), ], [ @@ -3908,11 +4068,13 @@ def get_instruction( "account_to_initialize", False, False, + False, ), AccountTemplate( "mint_account", False, False, + False, ), ], [ @@ -3957,6 +4119,7 @@ def get_instruction( "account_to_initialize", False, False, + False, ), ], [ @@ -4002,21 +4165,25 @@ def get_instruction( "account_to_initialize", False, False, + False, ), AccountTemplate( "mint_account", False, False, + False, ), AccountTemplate( "owner", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), ], [ @@ -4069,16 +4236,19 @@ def get_instruction( "multisig_account", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), AccountTemplate( "signer_accounts", False, False, + False, ), ], [ @@ -4124,16 +4294,19 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "destination_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -4193,16 +4366,19 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "delegate_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -4247,11 +4423,13 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -4304,11 +4482,13 @@ def get_instruction( "mint_account", False, False, + False, ), AccountTemplate( "current_authority", True, False, + False, ), ], [ @@ -4368,23 +4548,33 @@ def get_instruction( "mint", False, False, + True, ), AccountTemplate( "account_to_mint", False, False, + False, ), AccountTemplate( "minting_authority", True, False, + False, ), ], [ + UIProperty( + None, + "mint", + "Mint token", + False, + None, + ), UIProperty( "amount", None, - "Mint tokens", + "Mint amount", False, None, ), @@ -4430,23 +4620,33 @@ def get_instruction( "account_to_burn_from", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "owner", True, False, + False, ), ], [ + UIProperty( + None, + "token_mint", + "Burn token", + False, + None, + ), UIProperty( "amount", None, - "Burn tokens", + "Burn amount", False, None, ), @@ -4484,16 +4684,19 @@ def get_instruction( "account_to_close", False, False, + False, ), AccountTemplate( "destination_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -4538,16 +4741,19 @@ def get_instruction( "account_to_freeze", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "freeze_authority", True, False, + False, ), ], [ @@ -4558,6 +4764,13 @@ def get_instruction( False, None, ), + UIProperty( + None, + "token_mint", + "Token", + False, + None, + ), UIProperty( None, "freeze_authority", @@ -4585,16 +4798,19 @@ def get_instruction( "account_to_freeze", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "freeze_authority", True, False, + False, ), ], [ @@ -4605,6 +4821,13 @@ def get_instruction( False, None, ), + UIProperty( + None, + "token_mint", + "Token", + False, + None, + ), UIProperty( None, "freeze_authority", @@ -4647,21 +4870,25 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "destination_account", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -4735,21 +4962,25 @@ def get_instruction( "source_account", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "delegate", False, False, + False, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -4823,16 +5054,19 @@ def get_instruction( "mint", False, False, + True, ), AccountTemplate( "account_to_mint", False, False, + False, ), AccountTemplate( "minting_authority", True, False, + False, ), ], [ @@ -4899,16 +5133,19 @@ def get_instruction( "account_to_burn_from", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "owner", True, False, + False, ), ], [ @@ -4968,16 +5205,19 @@ def get_instruction( "account_to_initialize", False, False, + False, ), AccountTemplate( "mint_account", False, False, + False, ), AccountTemplate( "rent_sysvar", False, False, + False, ), ], [ @@ -5022,6 +5262,7 @@ def get_instruction( "token_account", False, False, + False, ), ], [ @@ -5060,11 +5301,13 @@ def get_instruction( "account_to_initialize", False, False, + False, ), AccountTemplate( "mint_account", False, False, + False, ), ], [ @@ -5109,6 +5352,7 @@ def get_instruction( "account_to_initialize", False, False, + False, ), ], [ @@ -5154,36 +5398,43 @@ def get_instruction( "funding_account", True, False, + False, ), AccountTemplate( "associated_token_account", False, False, + False, ), AccountTemplate( "wallet_address", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "system_program", False, False, + False, ), AccountTemplate( "spl_token", False, False, + False, ), AccountTemplate( "rent_sysvar", False, True, + False, ), ], [ @@ -5235,31 +5486,37 @@ def get_instruction( "funding_account", True, False, + False, ), AccountTemplate( "associated_token_account", False, False, + False, ), AccountTemplate( "wallet_addr", False, False, + False, ), AccountTemplate( "token_mint", False, False, + True, ), AccountTemplate( "system_program", False, False, + False, ), AccountTemplate( "spl_token", False, False, + False, ), ], [ @@ -5311,36 +5568,43 @@ def get_instruction( "nested_account", True, False, + False, ), AccountTemplate( "token_mint_nested", False, False, + False, ), AccountTemplate( "associated_token_account", False, False, + False, ), AccountTemplate( "owner", False, False, + False, ), AccountTemplate( "token_mint_owner", False, False, + False, ), AccountTemplate( "wallet_address", True, False, + False, ), AccountTemplate( "spl_token", False, False, + False, ), ], [ @@ -5408,6 +5672,7 @@ def get_instruction( "signer_accounts", True, True, + False, ), ], [ @@ -5468,6 +5733,7 @@ def get_instruction( "signer_accounts", True, True, + False, ), ], [ diff --git a/core/src/apps/solana/transaction/instructions.py.mako b/core/src/apps/solana/transaction/instructions.py.mako index 0606d74a5b5..03f9c48a9c3 100644 --- a/core/src/apps/solana/transaction/instructions.py.mako +++ b/core/src/apps/solana/transaction/instructions.py.mako @@ -181,6 +181,7 @@ def get_instruction( "${reference["name"]}", ${reference["is_authority"]}, ${reference["optional"]}, + ${reference.get("is_token_mint", False)}, ), % endfor ], diff --git a/core/src/apps/solana/types.py b/core/src/apps/solana/types.py index 46d53afe1e7..48a4c965f93 100644 --- a/core/src/apps/solana/types.py +++ b/core/src/apps/solana/types.py @@ -1,10 +1,14 @@ from typing import TYPE_CHECKING +from .definitions import Definitions + if TYPE_CHECKING: from enum import IntEnum from typing import Any, Callable, Generic, TypeVar + from trezor.messages import SolanaTxAdditionalInfo, SolanaTxTokenAccountInfo from trezor.utils import BufferReader + from typing_extensions import Self from .transaction import Instruction @@ -51,10 +55,13 @@ def __init__( class AccountTemplate: - def __init__(self, name: str, is_authority: bool, optional: bool) -> None: + def __init__( + self, name: str, is_authority: bool, optional: bool, is_token_mint: bool + ) -> None: self.name = name self.is_authority = is_authority self.optional = optional + self.is_token_mint = is_token_mint class UIProperty: @@ -71,3 +78,31 @@ def __init__( self.display_name = display_name self.is_authority = is_authority self.default_value_to_hide = default_value_to_hide + + +class AdditionalTxInfo: + def __init__( + self, + token_accounts_infos: list[SolanaTxTokenAccountInfo], + definitions: Definitions | None, + ) -> None: + self.token_accounts_infos = token_accounts_infos + self.definitions = definitions + + @classmethod + def from_solana_tx_additional_info( + cls, + additional_info: SolanaTxAdditionalInfo | None, + ) -> Self | None: + self: Self | None = None + if additional_info: + self = cls( + token_accounts_infos=additional_info.token_accounts_infos, + definitions=( + Definitions.from_encoded(additional_info.definitions.encoded_token) + if additional_info.definitions + and additional_info.definitions.encoded_token + else None + ), + ) + return self diff --git a/core/src/trezor/enums/EthereumDefinitionType.py b/core/src/trezor/enums/DefinitionType.py similarity index 53% rename from core/src/trezor/enums/EthereumDefinitionType.py rename to core/src/trezor/enums/DefinitionType.py index 3876e087205..4b43d721e1f 100644 --- a/core/src/trezor/enums/EthereumDefinitionType.py +++ b/core/src/trezor/enums/DefinitionType.py @@ -2,5 +2,6 @@ # fmt: off # isort:skip_file -NETWORK = 0 -TOKEN = 1 +ETHEREUM_NETWORK = 0 +ETHEREUM_TOKEN = 1 +SOLANA_TOKEN = 2 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index d16c3c4a660..756c223e060 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -285,9 +285,10 @@ class DebugWaitType(IntEnum): NEXT_LAYOUT = 1 CURRENT_LAYOUT = 2 - class EthereumDefinitionType(IntEnum): - NETWORK = 0 - TOKEN = 1 + class DefinitionType(IntEnum): + ETHEREUM_NETWORK = 0 + ETHEREUM_TOKEN = 1 + SOLANA_TOKEN = 2 class EthereumDataType(IntEnum): UINT = 1 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 1dbfcbc4078..2dbd8b2045e 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -42,9 +42,9 @@ def __getattr__(name: str) -> Any: from trezor.enums import DebugSwipeDirection # noqa: F401 from trezor.enums import DebugWaitType # noqa: F401 from trezor.enums import DecredStakingSpendType # noqa: F401 + from trezor.enums import DefinitionType # noqa: F401 from trezor.enums import DisplayRotation # noqa: F401 from trezor.enums import EthereumDataType # noqa: F401 - from trezor.enums import EthereumDefinitionType # noqa: F401 from trezor.enums import FailureType # noqa: F401 from trezor.enums import HomescreenFormat # noqa: F401 from trezor.enums import InputScriptType # noqa: F401 @@ -3056,6 +3056,68 @@ class DebugLinkOptigaSetSecMax(protobuf.MessageType): def is_type_of(cls, msg: Any) -> TypeGuard["DebugLinkOptigaSetSecMax"]: return isinstance(msg, cls) + class EthereumNetworkInfo(protobuf.MessageType): + chain_id: "int" + symbol: "str" + slip44: "int" + name: "str" + + def __init__( + self, + *, + chain_id: "int", + symbol: "str", + slip44: "int", + name: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumNetworkInfo"]: + return isinstance(msg, cls) + + class EthereumTokenInfo(protobuf.MessageType): + address: "bytes" + chain_id: "int" + symbol: "str" + decimals: "int" + name: "str" + + def __init__( + self, + *, + address: "bytes", + chain_id: "int", + symbol: "str", + decimals: "int", + name: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTokenInfo"]: + return isinstance(msg, cls) + + class SolanaTokenInfo(protobuf.MessageType): + mint: "bytes" + program_id: "str" + name: "str" + ticker: "str" + + def __init__( + self, + *, + mint: "bytes", + program_id: "str", + name: "str", + ticker: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTokenInfo"]: + return isinstance(msg, cls) + class EosGetPublicKey(protobuf.MessageType): address_n: "list[int]" show_display: "bool | None" @@ -3584,176 +3646,6 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["EosActionUnknown"]: return isinstance(msg, cls) - class EthereumNetworkInfo(protobuf.MessageType): - chain_id: "int" - symbol: "str" - slip44: "int" - name: "str" - - def __init__( - self, - *, - chain_id: "int", - symbol: "str", - slip44: "int", - name: "str", - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumNetworkInfo"]: - return isinstance(msg, cls) - - class EthereumTokenInfo(protobuf.MessageType): - address: "bytes" - chain_id: "int" - symbol: "str" - decimals: "int" - name: "str" - - def __init__( - self, - *, - address: "bytes", - chain_id: "int", - symbol: "str", - decimals: "int", - name: "str", - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTokenInfo"]: - return isinstance(msg, cls) - - class EthereumDefinitions(protobuf.MessageType): - encoded_network: "bytes | None" - encoded_token: "bytes | None" - - def __init__( - self, - *, - encoded_network: "bytes | None" = None, - encoded_token: "bytes | None" = None, - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumDefinitions"]: - return isinstance(msg, cls) - - class EthereumSignTypedData(protobuf.MessageType): - address_n: "list[int]" - primary_type: "str" - metamask_v4_compat: "bool" - definitions: "EthereumDefinitions | None" - - def __init__( - self, - *, - primary_type: "str", - address_n: "list[int] | None" = None, - metamask_v4_compat: "bool | None" = None, - definitions: "EthereumDefinitions | None" = None, - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumSignTypedData"]: - return isinstance(msg, cls) - - class EthereumTypedDataStructRequest(protobuf.MessageType): - name: "str" - - def __init__( - self, - *, - name: "str", - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataStructRequest"]: - return isinstance(msg, cls) - - class EthereumTypedDataStructAck(protobuf.MessageType): - members: "list[EthereumStructMember]" - - def __init__( - self, - *, - members: "list[EthereumStructMember] | None" = None, - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataStructAck"]: - return isinstance(msg, cls) - - class EthereumTypedDataValueRequest(protobuf.MessageType): - member_path: "list[int]" - - def __init__( - self, - *, - member_path: "list[int] | None" = None, - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataValueRequest"]: - return isinstance(msg, cls) - - class EthereumTypedDataValueAck(protobuf.MessageType): - value: "bytes" - - def __init__( - self, - *, - value: "bytes", - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataValueAck"]: - return isinstance(msg, cls) - - class EthereumStructMember(protobuf.MessageType): - type: "EthereumFieldType" - name: "str" - - def __init__( - self, - *, - type: "EthereumFieldType", - name: "str", - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumStructMember"]: - return isinstance(msg, cls) - - class EthereumFieldType(protobuf.MessageType): - data_type: "EthereumDataType" - size: "int | None" - entry_type: "EthereumFieldType | None" - struct_name: "str | None" - - def __init__( - self, - *, - data_type: "EthereumDataType", - size: "int | None" = None, - entry_type: "EthereumFieldType | None" = None, - struct_name: "str | None" = None, - ) -> None: - pass - - @classmethod - def is_type_of(cls, msg: Any) -> TypeGuard["EthereumFieldType"]: - return isinstance(msg, cls) - class EthereumGetPublicKey(protobuf.MessageType): address_n: "list[int]" show_display: "bool | None" @@ -4020,6 +3912,22 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataSignature"]: return isinstance(msg, cls) + class EthereumDefinitions(protobuf.MessageType): + encoded_network: "bytes | None" + encoded_token: "bytes | None" + + def __init__( + self, + *, + encoded_network: "bytes | None" = None, + encoded_token: "bytes | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumDefinitions"]: + return isinstance(msg, cls) + class EthereumAccessList(protobuf.MessageType): address: "str" storage_keys: "list[bytes]" @@ -4036,6 +3944,118 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["EthereumAccessList"]: return isinstance(msg, cls) + class EthereumSignTypedData(protobuf.MessageType): + address_n: "list[int]" + primary_type: "str" + metamask_v4_compat: "bool" + definitions: "EthereumDefinitions | None" + + def __init__( + self, + *, + primary_type: "str", + address_n: "list[int] | None" = None, + metamask_v4_compat: "bool | None" = None, + definitions: "EthereumDefinitions | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumSignTypedData"]: + return isinstance(msg, cls) + + class EthereumTypedDataStructRequest(protobuf.MessageType): + name: "str" + + def __init__( + self, + *, + name: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataStructRequest"]: + return isinstance(msg, cls) + + class EthereumTypedDataStructAck(protobuf.MessageType): + members: "list[EthereumStructMember]" + + def __init__( + self, + *, + members: "list[EthereumStructMember] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataStructAck"]: + return isinstance(msg, cls) + + class EthereumTypedDataValueRequest(protobuf.MessageType): + member_path: "list[int]" + + def __init__( + self, + *, + member_path: "list[int] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataValueRequest"]: + return isinstance(msg, cls) + + class EthereumTypedDataValueAck(protobuf.MessageType): + value: "bytes" + + def __init__( + self, + *, + value: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumTypedDataValueAck"]: + return isinstance(msg, cls) + + class EthereumStructMember(protobuf.MessageType): + type: "EthereumFieldType" + name: "str" + + def __init__( + self, + *, + type: "EthereumFieldType", + name: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumStructMember"]: + return isinstance(msg, cls) + + class EthereumFieldType(protobuf.MessageType): + data_type: "EthereumDataType" + size: "int | None" + entry_type: "EthereumFieldType | None" + struct_name: "str | None" + + def __init__( + self, + *, + data_type: "EthereumDataType", + size: "int | None" = None, + entry_type: "EthereumFieldType | None" = None, + struct_name: "str | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EthereumFieldType"]: + return isinstance(msg, cls) + class MoneroTransactionSourceEntry(protobuf.MessageType): outputs: "list[MoneroOutputEntry]" real_output: "int | None" @@ -5392,11 +5412,13 @@ def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxTokenAccountInfo"]: class SolanaTxAdditionalInfo(protobuf.MessageType): token_accounts_infos: "list[SolanaTxTokenAccountInfo]" + definitions: "SolanaDefinitions | None" def __init__( self, *, token_accounts_infos: "list[SolanaTxTokenAccountInfo] | None" = None, + definitions: "SolanaDefinitions | None" = None, ) -> None: pass @@ -5436,6 +5458,20 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxSignature"]: return isinstance(msg, cls) + class SolanaDefinitions(protobuf.MessageType): + encoded_token: "bytes | None" + + def __init__( + self, + *, + encoded_token: "bytes | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["SolanaDefinitions"]: + return isinstance(msg, cls) + class StellarAsset(protobuf.MessageType): type: "StellarAssetType" code: "str | None" diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py index f628ed94ab4..6f7e7bfb634 100644 --- a/core/src/trezor/ui/layouts/bolt/__init__.py +++ b/core/src/trezor/ui/layouts/bolt/__init__.py @@ -927,6 +927,23 @@ async def confirm_ethereum_staking_tx( br_code=br_code, ) + def confirm_solana_recipient( + recipient: str, + title: str, + items: Iterable[tuple[str, str]], + br_name: str = "confirm_solana_recipient", + br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, + ): + return confirm_value( + title=title, + value=recipient, + description="", + br_name=br_name, + br_code=br_code, + verb=TR.buttons__continue, + info_items=items, + ) + def confirm_solana_tx( amount: str, fee: str, @@ -940,12 +957,14 @@ def confirm_solana_tx( amount_title if amount_title is not None else f"{TR.words__amount}:" ) # def_arg fee_title = fee_title or TR.words__fee # def_arg + info_title = TR.confirm_total__title_fee return _confirm_summary( amount, amount_title, fee, fee_title, extra_items=items, + extra_title=info_title, br_name=br_name, br_code=br_code, ) diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py index e64e6ca635c..8c419f957a6 100644 --- a/core/src/trezor/ui/layouts/caesar/__init__.py +++ b/core/src/trezor/ui/layouts/caesar/__init__.py @@ -901,6 +901,23 @@ async def confirm_ethereum_staking_tx( br_code=br_code, ) + def confirm_solana_recipient( + recipient: str, + title: str, + items: Iterable[tuple[str, str]], + br_name: str = "confirm_solana_recipient", + br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, + ): + return confirm_value( + title=title, + value=recipient, + description="", + br_name=br_name, + br_code=br_code, + verb=TR.buttons__continue, + info_items=items, + ) + def confirm_solana_tx( amount: str, fee: str, @@ -914,6 +931,7 @@ def confirm_solana_tx( amount_title if amount_title is not None else f"{TR.words__amount}:" ) # def_arg fee_title = fee_title or TR.words__fee # def_arg + info_title = TR.confirm_total__title_fee return raise_if_not_confirmed( trezorui_api.confirm_summary( amount=amount, @@ -921,6 +939,7 @@ def confirm_solana_tx( fee=fee, fee_label=fee_title, extra_items=items, # TODO: extra_title here? + extra_title=info_title, ), br_name=br_name, br_code=br_code, diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py index 48c200caeb3..8e3d0056475 100644 --- a/core/src/trezor/ui/layouts/delizia/__init__.py +++ b/core/src/trezor/ui/layouts/delizia/__init__.py @@ -839,6 +839,23 @@ async def confirm_ethereum_staking_tx( br_name=None, ) + def confirm_solana_recipient( + recipient: str, + title: str, + items: Iterable[tuple[str, str]], + br_name: str = "confirm_solana_recipient", + br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, + ): + return confirm_value( + title=title, + value=recipient, + description="", + br_name=br_name, + br_code=br_code, + verb=TR.buttons__continue, + info_items=items, + ) + def confirm_solana_tx( amount: str, fee: str, @@ -857,6 +874,7 @@ def confirm_solana_tx( amount_title, fee, fee_title, + extra_title=TR.confirm_total__title_fee, extra_items=items, br_name=br_name, br_code=br_code, diff --git a/core/tests/ethereum_common.py b/core/tests/ethereum_common.py index 2f480401e72..f5053957ef1 100644 --- a/core/tests/ethereum_common.py +++ b/core/tests/ethereum_common.py @@ -4,7 +4,7 @@ from trezor.crypto import cosi from trezor.crypto.curve import ed25519 from trezor.crypto.hashlib import sha256 -from trezor.enums import EthereumDefinitionType +from trezor.enums import DefinitionType PRIVATE_KEYS_DEV = [byte * 32 for byte in (b"\xdd", b"\xde", b"\xdf")] @@ -41,7 +41,7 @@ def make_token( def make_payload( prefix: bytes = b"trzd1", - data_type: EthereumDefinitionType = EthereumDefinitionType.NETWORK, + data_type: DefinitionType = DefinitionType.ETHEREUM_NETWORK, timestamp: int = 0xFFFF_FFFF, message: ( messages.EthereumNetworkInfo | messages.EthereumTokenInfo | bytes @@ -106,7 +106,7 @@ def encode_network( ) -> bytes: if network is None: network = make_network(chain_id, slip44, symbol, name) - payload = make_payload(data_type=EthereumDefinitionType.NETWORK, message=network) + payload = make_payload(data_type=DefinitionType.ETHEREUM_NETWORK, message=network) proof, signature = sign_payload(payload, []) return payload + proof + signature @@ -121,6 +121,6 @@ def encode_token( ) -> bytes: if token is None: token = make_token(symbol, decimals, address, chain_id, name) - payload = make_payload(data_type=EthereumDefinitionType.TOKEN, message=token) + payload = make_payload(data_type=DefinitionType.ETHEREUM_TOKEN, message=token) proof, signature = sign_payload(payload, []) return payload + proof + signature diff --git a/core/tests/test_apps.ethereum.definitions.py b/core/tests/test_apps.ethereum.definitions.py index 894b1a6fb2e..5ec5299af1e 100644 --- a/core/tests/test_apps.ethereum.definitions.py +++ b/core/tests/test_apps.ethereum.definitions.py @@ -9,11 +9,12 @@ if not utils.BITCOIN_ONLY: from ethereum_common import * - from trezor.enums import EthereumDefinitionType + from trezor.enums import DefinitionType from trezor.messages import EthereumNetworkInfo, EthereumTokenInfo + from apps.common.definitions import decode_definition from apps.ethereum import networks, tokens - from apps.ethereum.definitions import Definitions, decode_definition + from apps.ethereum.definitions import Definitions TETHER_ADDRESS = b"\xda\xc1\x7f\x95\x8d\x2e\xe5\x23\xa2\x20\x62\x06\x99\x45\x97\xc1\x3d\x83\x1e\xc7" @@ -92,7 +93,7 @@ def test_bad_prefix(self): def test_bad_type(self): payload = make_payload( - data_type=EthereumDefinitionType.TOKEN, message=make_token() + data_type=DefinitionType.ETHEREUM_TOKEN, message=make_token() ) proof, signature = sign_payload(payload, []) self.assertFailed(payload + proof + signature) @@ -109,13 +110,13 @@ def test_malformed_protobuf(self): def test_protobuf_mismatch(self): payload = make_payload( - data_type=EthereumDefinitionType.NETWORK, message=make_token() + data_type=DefinitionType.ETHEREUM_NETWORK, message=make_token() ) proof, signature = sign_payload(payload, []) self.assertFailed(payload + proof + signature) payload = make_payload( - data_type=EthereumDefinitionType.TOKEN, message=make_network() + data_type=DefinitionType.ETHEREUM_TOKEN, message=make_network() ) proof, signature = sign_payload(payload, []) self.assertFailed(payload + proof + signature) diff --git a/core/translations/en.json b/core/translations/en.json index 809fbde035f..09412c15a70 100644 --- a/core/translations/en.json +++ b/core/translations/en.json @@ -774,6 +774,11 @@ "solana__associated_token_account": "Associated token account", "solana__confirm_multisig": "Confirm multisig", "solana__expected_fee": "Expected fee", + "solana__transaction_fee": "Transaction fee", + "solana__base_fee": "Base fee", + "solana__priority_fee": "Priority fee", + "solana__max_rent_fee": "Max rent fee", + "solana__max_fees_rent": "Max fees and rent", "solana__instruction_accounts_template": "Instruction contains {0} accounts and its data is {1} bytes long.", "solana__instruction_data": "Instruction data", "solana__instruction_is_multisig": "The following instruction is a multisig instruction.", diff --git a/core/translations/order.json b/core/translations/order.json index 6fee8d6de78..cb0c1458225 100644 --- a/core/translations/order.json +++ b/core/translations/order.json @@ -973,5 +973,11 @@ "971": "instructions__view_all_data", "972": "ethereum__interaction_contract", "973": "misc__enable_labeling", - "974": "ethereum__unknown_contract_address_short" + "974": "ethereum__unknown_contract_address_short", + "975": "solana__base_fee", + "976": "solana__max_fees_rent", + "977": "solana__max_rent_fee", + "978": "solana__priority_fee", + "979": "solana__transaction_fee", + "980": "solana__unknown" } diff --git a/core/translations/signatures.json b/core/translations/signatures.json index 2e84a509e69..84c6594a82d 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -1,8 +1,8 @@ { "current": { - "merkle_root": "29521a9c2bc69353a23515778bfffe1392cd6ba7855eadb7d4f1d9c5ea4f5fc0", - "datetime": "2025-02-11T09:34:38.217582", - "commit": "7631d009c673cde745c76e0882b7cc60a90268fd" + "merkle_root": "7001ce599022be48e1a6a98eccd97ee60f1c1c62a9de0170219f398244ed852e", + "datetime": "2025-02-17T13:29:27.637819", + "commit": "0072be2e00262c942ee51be59b0f665a4209d150" }, "history": [ { diff --git a/docs/common/ethereum-definitions.md b/docs/common/ethereum-definitions.md index e1a13baae7d..183de8f2199 100644 --- a/docs/common/ethereum-definitions.md +++ b/docs/common/ethereum-definitions.md @@ -89,7 +89,7 @@ Each definition is encoded as a protobuf message `EthereumNetworkInfo` or All numbers are unsigned little endian. 1. magic string `trzd1` (5 bytes) -2. definition type according to `EthereumDefinitionType` enum (1 byte) +2. definition type according to `DefinitionType` enum (1 byte) 3. data version of the definition (4 bytes) 4. protobuf payload length (2 bytes) 5. protobuf payload (N bytes) diff --git a/docs/common/solana-definitions.md b/docs/common/solana-definitions.md new file mode 100644 index 00000000000..9e02cbad19d --- /dev/null +++ b/docs/common/solana-definitions.md @@ -0,0 +1,22 @@ +# Solana definitions + +To allow nicer token presentation on the device, Trezor allows passing Definitions as additional info. Currently only +one token can be included in the definitions. Solana tokens are uniquely identified by their program id and mint +account, Trezor additionally expects a name and a ticker. + +In the future we might also include definitions for instructions. + +## Retrieving the definitions +A full list of Solana definitions is compiled from multiple sources and is available [in a separate repository](TODO). + +From this list, a collection of binary blobs is generated, signed, and made available online. + +A given Trezor firmware will only accept signed definitions newer than a certain date, typically one month before +firmware release. This means that a client application should either always fetch fresh definitions from the official +URLs, or refresh its local copy frequently. + +The base URL for the definitions is `https://data.trezor.io/firmware/solana-definitions/`. + +## Definition format + +Look at [`ethereum-definitions.md`'s](ethereum_definitions.md) `Definition Format` section. diff --git a/python/src/trezorlib/cli/solana.py b/python/src/trezorlib/cli/solana.py index 590b4f79146..739ff0ac796 100644 --- a/python/src/trezorlib/cli/solana.py +++ b/python/src/trezorlib/cli/solana.py @@ -65,6 +65,12 @@ def sign_tx( additional_information = None if additional_information_file: raw_additional_information = json.load(additional_information_file) + + definitions: Optional[messages.SolanaDefinitions] = None + token = raw_additional_information.get("token", None) + if token: + definitions = messages.SolanaDefinitions(encoded_token=bytes.fromhex(token)) + additional_information = messages.SolanaTxAdditionalInfo( token_accounts_infos=[ messages.SolanaTxTokenAccountInfo( @@ -74,7 +80,8 @@ def sign_tx( token_account=token_account["token_account"], ) for token_account in raw_additional_information["token_accounts_infos"] - ] + ], + definitions=definitions, ) return solana.sign_tx( diff --git a/python/src/trezorlib/definitions.py b/python/src/trezorlib/definitions.py index 94672e38e30..ca7c91b7c90 100644 --- a/python/src/trezorlib/definitions.py +++ b/python/src/trezorlib/definitions.py @@ -8,7 +8,7 @@ from construct_classes import Struct, subcon from . import cosi, merkle_tree -from .messages import EthereumDefinitionType +from .messages import DefinitionType from .tools import EnumAdapter LOG = logging.getLogger(__name__) @@ -38,13 +38,13 @@ class DefinitionPayload(Struct): magic: bytes - data_type: EthereumDefinitionType + data_type: DefinitionType timestamp: int data: bytes SUBCON = c.Struct( "magic" / c.Const(FORMAT_MAGIC), - "data_type" / EnumAdapter(c.Int8ul, EthereumDefinitionType), + "data_type" / EnumAdapter(c.Int8ul, DefinitionType), "timestamp" / c.Int32ul, "data" / c.Prefixed(c.Int16ul, c.GreedyBytes), ) diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 024c3ae6961..b256f41fe28 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -326,9 +326,10 @@ class DebugWaitType(IntEnum): CURRENT_LAYOUT = 2 -class EthereumDefinitionType(IntEnum): - NETWORK = 0 - TOKEN = 1 +class DefinitionType(IntEnum): + ETHEREUM_NETWORK = 0 + ETHEREUM_TOKEN = 1 + SOLANA_TOKEN = 2 class EthereumDataType(IntEnum): @@ -4328,6 +4329,78 @@ class DebugLinkOptigaSetSecMax(protobuf.MessageType): MESSAGE_WIRE_TYPE = 9008 +class EthereumNetworkInfo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("chain_id", "uint64", repeated=False, required=True), + 2: protobuf.Field("symbol", "string", repeated=False, required=True), + 3: protobuf.Field("slip44", "uint32", repeated=False, required=True), + 4: protobuf.Field("name", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + chain_id: "int", + symbol: "str", + slip44: "int", + name: "str", + ) -> None: + self.chain_id = chain_id + self.symbol = symbol + self.slip44 = slip44 + self.name = name + + +class EthereumTokenInfo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("address", "bytes", repeated=False, required=True), + 2: protobuf.Field("chain_id", "uint64", repeated=False, required=True), + 3: protobuf.Field("symbol", "string", repeated=False, required=True), + 4: protobuf.Field("decimals", "uint32", repeated=False, required=True), + 5: protobuf.Field("name", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + address: "bytes", + chain_id: "int", + symbol: "str", + decimals: "int", + name: "str", + ) -> None: + self.address = address + self.chain_id = chain_id + self.symbol = symbol + self.decimals = decimals + self.name = name + + +class SolanaTokenInfo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("mint", "bytes", repeated=False, required=True), + 2: protobuf.Field("program_id", "string", repeated=False, required=True), + 3: protobuf.Field("name", "string", repeated=False, required=True), + 4: protobuf.Field("ticker", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + mint: "bytes", + program_id: "str", + name: "str", + ticker: "str", + ) -> None: + self.mint = mint + self.program_id = program_id + self.name = name + self.ticker = ticker + + class EosGetPublicKey(protobuf.MessageType): MESSAGE_WIRE_TYPE = 600 FIELDS = { @@ -4924,191 +4997,6 @@ def __init__( self.data_chunk = data_chunk -class EthereumNetworkInfo(protobuf.MessageType): - MESSAGE_WIRE_TYPE = None - FIELDS = { - 1: protobuf.Field("chain_id", "uint64", repeated=False, required=True), - 2: protobuf.Field("symbol", "string", repeated=False, required=True), - 3: protobuf.Field("slip44", "uint32", repeated=False, required=True), - 4: protobuf.Field("name", "string", repeated=False, required=True), - } - - def __init__( - self, - *, - chain_id: "int", - symbol: "str", - slip44: "int", - name: "str", - ) -> None: - self.chain_id = chain_id - self.symbol = symbol - self.slip44 = slip44 - self.name = name - - -class EthereumTokenInfo(protobuf.MessageType): - MESSAGE_WIRE_TYPE = None - FIELDS = { - 1: protobuf.Field("address", "bytes", repeated=False, required=True), - 2: protobuf.Field("chain_id", "uint64", repeated=False, required=True), - 3: protobuf.Field("symbol", "string", repeated=False, required=True), - 4: protobuf.Field("decimals", "uint32", repeated=False, required=True), - 5: protobuf.Field("name", "string", repeated=False, required=True), - } - - def __init__( - self, - *, - address: "bytes", - chain_id: "int", - symbol: "str", - decimals: "int", - name: "str", - ) -> None: - self.address = address - self.chain_id = chain_id - self.symbol = symbol - self.decimals = decimals - self.name = name - - -class EthereumDefinitions(protobuf.MessageType): - MESSAGE_WIRE_TYPE = None - FIELDS = { - 1: protobuf.Field("encoded_network", "bytes", repeated=False, required=False, default=None), - 2: protobuf.Field("encoded_token", "bytes", repeated=False, required=False, default=None), - } - - def __init__( - self, - *, - encoded_network: Optional["bytes"] = None, - encoded_token: Optional["bytes"] = None, - ) -> None: - self.encoded_network = encoded_network - self.encoded_token = encoded_token - - -class EthereumSignTypedData(protobuf.MessageType): - MESSAGE_WIRE_TYPE = 464 - FIELDS = { - 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), - 2: protobuf.Field("primary_type", "string", repeated=False, required=True), - 3: protobuf.Field("metamask_v4_compat", "bool", repeated=False, required=False, default=True), - 4: protobuf.Field("definitions", "EthereumDefinitions", repeated=False, required=False, default=None), - } - - def __init__( - self, - *, - primary_type: "str", - address_n: Optional[Sequence["int"]] = None, - metamask_v4_compat: Optional["bool"] = True, - definitions: Optional["EthereumDefinitions"] = None, - ) -> None: - self.address_n: Sequence["int"] = address_n if address_n is not None else [] - self.primary_type = primary_type - self.metamask_v4_compat = metamask_v4_compat - self.definitions = definitions - - -class EthereumTypedDataStructRequest(protobuf.MessageType): - MESSAGE_WIRE_TYPE = 465 - FIELDS = { - 1: protobuf.Field("name", "string", repeated=False, required=True), - } - - def __init__( - self, - *, - name: "str", - ) -> None: - self.name = name - - -class EthereumTypedDataStructAck(protobuf.MessageType): - MESSAGE_WIRE_TYPE = 466 - FIELDS = { - 1: protobuf.Field("members", "EthereumStructMember", repeated=True, required=False, default=None), - } - - def __init__( - self, - *, - members: Optional[Sequence["EthereumStructMember"]] = None, - ) -> None: - self.members: Sequence["EthereumStructMember"] = members if members is not None else [] - - -class EthereumTypedDataValueRequest(protobuf.MessageType): - MESSAGE_WIRE_TYPE = 467 - FIELDS = { - 1: protobuf.Field("member_path", "uint32", repeated=True, required=False, default=None), - } - - def __init__( - self, - *, - member_path: Optional[Sequence["int"]] = None, - ) -> None: - self.member_path: Sequence["int"] = member_path if member_path is not None else [] - - -class EthereumTypedDataValueAck(protobuf.MessageType): - MESSAGE_WIRE_TYPE = 468 - FIELDS = { - 1: protobuf.Field("value", "bytes", repeated=False, required=True), - } - - def __init__( - self, - *, - value: "bytes", - ) -> None: - self.value = value - - -class EthereumStructMember(protobuf.MessageType): - MESSAGE_WIRE_TYPE = None - FIELDS = { - 1: protobuf.Field("type", "EthereumFieldType", repeated=False, required=True), - 2: protobuf.Field("name", "string", repeated=False, required=True), - } - - def __init__( - self, - *, - type: "EthereumFieldType", - name: "str", - ) -> None: - self.type = type - self.name = name - - -class EthereumFieldType(protobuf.MessageType): - MESSAGE_WIRE_TYPE = None - FIELDS = { - 1: protobuf.Field("data_type", "EthereumDataType", repeated=False, required=True), - 2: protobuf.Field("size", "uint32", repeated=False, required=False, default=None), - 3: protobuf.Field("entry_type", "EthereumFieldType", repeated=False, required=False, default=None), - 4: protobuf.Field("struct_name", "string", repeated=False, required=False, default=None), - } - - def __init__( - self, - *, - data_type: "EthereumDataType", - size: Optional["int"] = None, - entry_type: Optional["EthereumFieldType"] = None, - struct_name: Optional["str"] = None, - ) -> None: - self.data_type = data_type - self.size = size - self.entry_type = entry_type - self.struct_name = struct_name - - class EthereumGetPublicKey(protobuf.MessageType): MESSAGE_WIRE_TYPE = 450 FIELDS = { @@ -5420,6 +5308,23 @@ def __init__( self.address = address +class EthereumDefinitions(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("encoded_network", "bytes", repeated=False, required=False, default=None), + 2: protobuf.Field("encoded_token", "bytes", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + encoded_network: Optional["bytes"] = None, + encoded_token: Optional["bytes"] = None, + ) -> None: + self.encoded_network = encoded_network + self.encoded_token = encoded_token + + class EthereumAccessList(protobuf.MessageType): MESSAGE_WIRE_TYPE = None FIELDS = { @@ -5437,6 +5342,125 @@ def __init__( self.address = address +class EthereumSignTypedData(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 464 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None), + 2: protobuf.Field("primary_type", "string", repeated=False, required=True), + 3: protobuf.Field("metamask_v4_compat", "bool", repeated=False, required=False, default=True), + 4: protobuf.Field("definitions", "EthereumDefinitions", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + primary_type: "str", + address_n: Optional[Sequence["int"]] = None, + metamask_v4_compat: Optional["bool"] = True, + definitions: Optional["EthereumDefinitions"] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.primary_type = primary_type + self.metamask_v4_compat = metamask_v4_compat + self.definitions = definitions + + +class EthereumTypedDataStructRequest(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 465 + FIELDS = { + 1: protobuf.Field("name", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + name: "str", + ) -> None: + self.name = name + + +class EthereumTypedDataStructAck(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 466 + FIELDS = { + 1: protobuf.Field("members", "EthereumStructMember", repeated=True, required=False, default=None), + } + + def __init__( + self, + *, + members: Optional[Sequence["EthereumStructMember"]] = None, + ) -> None: + self.members: Sequence["EthereumStructMember"] = members if members is not None else [] + + +class EthereumTypedDataValueRequest(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 467 + FIELDS = { + 1: protobuf.Field("member_path", "uint32", repeated=True, required=False, default=None), + } + + def __init__( + self, + *, + member_path: Optional[Sequence["int"]] = None, + ) -> None: + self.member_path: Sequence["int"] = member_path if member_path is not None else [] + + +class EthereumTypedDataValueAck(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 468 + FIELDS = { + 1: protobuf.Field("value", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + value: "bytes", + ) -> None: + self.value = value + + +class EthereumStructMember(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("type", "EthereumFieldType", repeated=False, required=True), + 2: protobuf.Field("name", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + type: "EthereumFieldType", + name: "str", + ) -> None: + self.type = type + self.name = name + + +class EthereumFieldType(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("data_type", "EthereumDataType", repeated=False, required=True), + 2: protobuf.Field("size", "uint32", repeated=False, required=False, default=None), + 3: protobuf.Field("entry_type", "EthereumFieldType", repeated=False, required=False, default=None), + 4: protobuf.Field("struct_name", "string", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + data_type: "EthereumDataType", + size: Optional["int"] = None, + entry_type: Optional["EthereumFieldType"] = None, + struct_name: Optional["str"] = None, + ) -> None: + self.data_type = data_type + self.size = size + self.entry_type = entry_type + self.struct_name = struct_name + + class MoneroTransactionSourceEntry(protobuf.MessageType): MESSAGE_WIRE_TYPE = None FIELDS = { @@ -6970,14 +6994,17 @@ class SolanaTxAdditionalInfo(protobuf.MessageType): MESSAGE_WIRE_TYPE = None FIELDS = { 1: protobuf.Field("token_accounts_infos", "SolanaTxTokenAccountInfo", repeated=True, required=False, default=None), + 2: protobuf.Field("definitions", "SolanaDefinitions", repeated=False, required=False, default=None), } def __init__( self, *, token_accounts_infos: Optional[Sequence["SolanaTxTokenAccountInfo"]] = None, + definitions: Optional["SolanaDefinitions"] = None, ) -> None: self.token_accounts_infos: Sequence["SolanaTxTokenAccountInfo"] = token_accounts_infos if token_accounts_infos is not None else [] + self.definitions = definitions class SolanaSignTx(protobuf.MessageType): @@ -7014,6 +7041,20 @@ def __init__( self.signature = signature +class SolanaDefinitions(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("encoded_token", "bytes", repeated=False, required=False, default=None), + } + + def __init__( + self, + *, + encoded_token: Optional["bytes"] = None, + ) -> None: + self.encoded_token = encoded_token + + class StellarAsset(protobuf.MessageType): MESSAGE_WIRE_TYPE = None FIELDS = { diff --git a/rust/trezor-client/src/protos/generated/messages_ethereum_definitions.rs b/rust/trezor-client/src/protos/generated/messages_definitions.rs similarity index 69% rename from rust/trezor-client/src/protos/generated/messages_ethereum_definitions.rs rename to rust/trezor-client/src/protos/generated/messages_definitions.rs index f2ee22698ad..3f91e76e067 100644 --- a/rust/trezor-client/src/protos/generated/messages_ethereum_definitions.rs +++ b/rust/trezor-client/src/protos/generated/messages_definitions.rs @@ -19,26 +19,26 @@ #![allow(unused_results)] #![allow(unused_mut)] -//! Generated file from `messages-ethereum-definitions.proto` +//! Generated file from `messages-definitions.proto` /// Generated files are compatible only with the same version /// of protobuf runtime. const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_3_0; -// @@protoc_insertion_point(message:hw.trezor.messages.ethereum_definitions.EthereumNetworkInfo) +// @@protoc_insertion_point(message:hw.trezor.messages.definitions.EthereumNetworkInfo) #[derive(PartialEq,Clone,Default,Debug)] pub struct EthereumNetworkInfo { // message fields - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumNetworkInfo.chain_id) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumNetworkInfo.chain_id) pub chain_id: ::std::option::Option, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumNetworkInfo.symbol) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumNetworkInfo.symbol) pub symbol: ::std::option::Option<::std::string::String>, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumNetworkInfo.slip44) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumNetworkInfo.slip44) pub slip44: ::std::option::Option, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumNetworkInfo.name) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumNetworkInfo.name) pub name: ::std::option::Option<::std::string::String>, // special fields - // @@protoc_insertion_point(special_field:hw.trezor.messages.ethereum_definitions.EthereumNetworkInfo.special_fields) + // @@protoc_insertion_point(special_field:hw.trezor.messages.definitions.EthereumNetworkInfo.special_fields) pub special_fields: ::protobuf::SpecialFields, } @@ -323,22 +323,22 @@ impl ::protobuf::reflect::ProtobufValue for EthereumNetworkInfo { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; } -// @@protoc_insertion_point(message:hw.trezor.messages.ethereum_definitions.EthereumTokenInfo) +// @@protoc_insertion_point(message:hw.trezor.messages.definitions.EthereumTokenInfo) #[derive(PartialEq,Clone,Default,Debug)] pub struct EthereumTokenInfo { // message fields - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumTokenInfo.address) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumTokenInfo.address) pub address: ::std::option::Option<::std::vec::Vec>, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumTokenInfo.chain_id) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumTokenInfo.chain_id) pub chain_id: ::std::option::Option, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumTokenInfo.symbol) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumTokenInfo.symbol) pub symbol: ::std::option::Option<::std::string::String>, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumTokenInfo.decimals) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumTokenInfo.decimals) pub decimals: ::std::option::Option, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumTokenInfo.name) + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.EthereumTokenInfo.name) pub name: ::std::option::Option<::std::string::String>, // special fields - // @@protoc_insertion_point(special_field:hw.trezor.messages.ethereum_definitions.EthereumTokenInfo.special_fields) + // @@protoc_insertion_point(special_field:hw.trezor.messages.definitions.EthereumTokenInfo.special_fields) pub special_fields: ::protobuf::SpecialFields, } @@ -678,127 +678,225 @@ impl ::protobuf::reflect::ProtobufValue for EthereumTokenInfo { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; } -// @@protoc_insertion_point(message:hw.trezor.messages.ethereum_definitions.EthereumDefinitions) +// @@protoc_insertion_point(message:hw.trezor.messages.definitions.SolanaTokenInfo) #[derive(PartialEq,Clone,Default,Debug)] -pub struct EthereumDefinitions { +pub struct SolanaTokenInfo { // message fields - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumDefinitions.encoded_network) - pub encoded_network: ::std::option::Option<::std::vec::Vec>, - // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_definitions.EthereumDefinitions.encoded_token) - pub encoded_token: ::std::option::Option<::std::vec::Vec>, + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.SolanaTokenInfo.mint) + pub mint: ::std::option::Option<::std::vec::Vec>, + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.SolanaTokenInfo.program_id) + pub program_id: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.SolanaTokenInfo.name) + pub name: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:hw.trezor.messages.definitions.SolanaTokenInfo.ticker) + pub ticker: ::std::option::Option<::std::string::String>, // special fields - // @@protoc_insertion_point(special_field:hw.trezor.messages.ethereum_definitions.EthereumDefinitions.special_fields) + // @@protoc_insertion_point(special_field:hw.trezor.messages.definitions.SolanaTokenInfo.special_fields) pub special_fields: ::protobuf::SpecialFields, } -impl<'a> ::std::default::Default for &'a EthereumDefinitions { - fn default() -> &'a EthereumDefinitions { - ::default_instance() +impl<'a> ::std::default::Default for &'a SolanaTokenInfo { + fn default() -> &'a SolanaTokenInfo { + ::default_instance() } } -impl EthereumDefinitions { - pub fn new() -> EthereumDefinitions { +impl SolanaTokenInfo { + pub fn new() -> SolanaTokenInfo { ::std::default::Default::default() } - // optional bytes encoded_network = 1; + // required bytes mint = 1; - pub fn encoded_network(&self) -> &[u8] { - match self.encoded_network.as_ref() { + pub fn mint(&self) -> &[u8] { + match self.mint.as_ref() { Some(v) => v, None => &[], } } - pub fn clear_encoded_network(&mut self) { - self.encoded_network = ::std::option::Option::None; + pub fn clear_mint(&mut self) { + self.mint = ::std::option::Option::None; } - pub fn has_encoded_network(&self) -> bool { - self.encoded_network.is_some() + pub fn has_mint(&self) -> bool { + self.mint.is_some() } // Param is passed by value, moved - pub fn set_encoded_network(&mut self, v: ::std::vec::Vec) { - self.encoded_network = ::std::option::Option::Some(v); + pub fn set_mint(&mut self, v: ::std::vec::Vec) { + self.mint = ::std::option::Option::Some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_encoded_network(&mut self) -> &mut ::std::vec::Vec { - if self.encoded_network.is_none() { - self.encoded_network = ::std::option::Option::Some(::std::vec::Vec::new()); + pub fn mut_mint(&mut self) -> &mut ::std::vec::Vec { + if self.mint.is_none() { + self.mint = ::std::option::Option::Some(::std::vec::Vec::new()); } - self.encoded_network.as_mut().unwrap() + self.mint.as_mut().unwrap() } // Take field - pub fn take_encoded_network(&mut self) -> ::std::vec::Vec { - self.encoded_network.take().unwrap_or_else(|| ::std::vec::Vec::new()) + pub fn take_mint(&mut self) -> ::std::vec::Vec { + self.mint.take().unwrap_or_else(|| ::std::vec::Vec::new()) } - // optional bytes encoded_token = 2; + // required string program_id = 2; - pub fn encoded_token(&self) -> &[u8] { - match self.encoded_token.as_ref() { + pub fn program_id(&self) -> &str { + match self.program_id.as_ref() { Some(v) => v, - None => &[], + None => "", } } - pub fn clear_encoded_token(&mut self) { - self.encoded_token = ::std::option::Option::None; + pub fn clear_program_id(&mut self) { + self.program_id = ::std::option::Option::None; } - pub fn has_encoded_token(&self) -> bool { - self.encoded_token.is_some() + pub fn has_program_id(&self) -> bool { + self.program_id.is_some() } // Param is passed by value, moved - pub fn set_encoded_token(&mut self, v: ::std::vec::Vec) { - self.encoded_token = ::std::option::Option::Some(v); + pub fn set_program_id(&mut self, v: ::std::string::String) { + self.program_id = ::std::option::Option::Some(v); } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_encoded_token(&mut self) -> &mut ::std::vec::Vec { - if self.encoded_token.is_none() { - self.encoded_token = ::std::option::Option::Some(::std::vec::Vec::new()); + pub fn mut_program_id(&mut self) -> &mut ::std::string::String { + if self.program_id.is_none() { + self.program_id = ::std::option::Option::Some(::std::string::String::new()); } - self.encoded_token.as_mut().unwrap() + self.program_id.as_mut().unwrap() } // Take field - pub fn take_encoded_token(&mut self) -> ::std::vec::Vec { - self.encoded_token.take().unwrap_or_else(|| ::std::vec::Vec::new()) + pub fn take_program_id(&mut self) -> ::std::string::String { + self.program_id.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // required string name = 3; + + pub fn name(&self) -> &str { + match self.name.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_name(&mut self) { + self.name = ::std::option::Option::None; + } + + pub fn has_name(&self) -> bool { + self.name.is_some() + } + + // Param is passed by value, moved + pub fn set_name(&mut self, v: ::std::string::String) { + self.name = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_name(&mut self) -> &mut ::std::string::String { + if self.name.is_none() { + self.name = ::std::option::Option::Some(::std::string::String::new()); + } + self.name.as_mut().unwrap() + } + + // Take field + pub fn take_name(&mut self) -> ::std::string::String { + self.name.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // required string ticker = 4; + + pub fn ticker(&self) -> &str { + match self.ticker.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_ticker(&mut self) { + self.ticker = ::std::option::Option::None; + } + + pub fn has_ticker(&self) -> bool { + self.ticker.is_some() + } + + // Param is passed by value, moved + pub fn set_ticker(&mut self, v: ::std::string::String) { + self.ticker = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_ticker(&mut self) -> &mut ::std::string::String { + if self.ticker.is_none() { + self.ticker = ::std::option::Option::Some(::std::string::String::new()); + } + self.ticker.as_mut().unwrap() + } + + // Take field + pub fn take_ticker(&mut self) -> ::std::string::String { + self.ticker.take().unwrap_or_else(|| ::std::string::String::new()) } fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(2); + let mut fields = ::std::vec::Vec::with_capacity(4); let mut oneofs = ::std::vec::Vec::with_capacity(0); fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "encoded_network", - |m: &EthereumDefinitions| { &m.encoded_network }, - |m: &mut EthereumDefinitions| { &mut m.encoded_network }, + "mint", + |m: &SolanaTokenInfo| { &m.mint }, + |m: &mut SolanaTokenInfo| { &mut m.mint }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "program_id", + |m: &SolanaTokenInfo| { &m.program_id }, + |m: &mut SolanaTokenInfo| { &mut m.program_id }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "name", + |m: &SolanaTokenInfo| { &m.name }, + |m: &mut SolanaTokenInfo| { &mut m.name }, )); fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "encoded_token", - |m: &EthereumDefinitions| { &m.encoded_token }, - |m: &mut EthereumDefinitions| { &mut m.encoded_token }, + "ticker", + |m: &SolanaTokenInfo| { &m.ticker }, + |m: &mut SolanaTokenInfo| { &mut m.ticker }, )); - ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( - "EthereumDefinitions", + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "SolanaTokenInfo", fields, oneofs, ) } } -impl ::protobuf::Message for EthereumDefinitions { - const NAME: &'static str = "EthereumDefinitions"; +impl ::protobuf::Message for SolanaTokenInfo { + const NAME: &'static str = "SolanaTokenInfo"; fn is_initialized(&self) -> bool { + if self.mint.is_none() { + return false; + } + if self.program_id.is_none() { + return false; + } + if self.name.is_none() { + return false; + } + if self.ticker.is_none() { + return false; + } true } @@ -806,10 +904,16 @@ impl ::protobuf::Message for EthereumDefinitions { while let Some(tag) = is.read_raw_tag_or_eof()? { match tag { 10 => { - self.encoded_network = ::std::option::Option::Some(is.read_bytes()?); + self.mint = ::std::option::Option::Some(is.read_bytes()?); }, 18 => { - self.encoded_token = ::std::option::Option::Some(is.read_bytes()?); + self.program_id = ::std::option::Option::Some(is.read_string()?); + }, + 26 => { + self.name = ::std::option::Option::Some(is.read_string()?); + }, + 34 => { + self.ticker = ::std::option::Option::Some(is.read_string()?); }, tag => { ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; @@ -823,11 +927,17 @@ impl ::protobuf::Message for EthereumDefinitions { #[allow(unused_variables)] fn compute_size(&self) -> u64 { let mut my_size = 0; - if let Some(v) = self.encoded_network.as_ref() { + if let Some(v) = self.mint.as_ref() { my_size += ::protobuf::rt::bytes_size(1, &v); } - if let Some(v) = self.encoded_token.as_ref() { - my_size += ::protobuf::rt::bytes_size(2, &v); + if let Some(v) = self.program_id.as_ref() { + my_size += ::protobuf::rt::string_size(2, &v); + } + if let Some(v) = self.name.as_ref() { + my_size += ::protobuf::rt::string_size(3, &v); + } + if let Some(v) = self.ticker.as_ref() { + my_size += ::protobuf::rt::string_size(4, &v); } my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); self.special_fields.cached_size().set(my_size as u32); @@ -835,11 +945,17 @@ impl ::protobuf::Message for EthereumDefinitions { } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { - if let Some(v) = self.encoded_network.as_ref() { + if let Some(v) = self.mint.as_ref() { os.write_bytes(1, v)?; } - if let Some(v) = self.encoded_token.as_ref() { - os.write_bytes(2, v)?; + if let Some(v) = self.program_id.as_ref() { + os.write_string(2, v)?; + } + if let Some(v) = self.name.as_ref() { + os.write_string(3, v)?; + } + if let Some(v) = self.ticker.as_ref() { + os.write_string(4, v)?; } os.write_unknown_fields(self.special_fields.unknown_fields())?; ::std::result::Result::Ok(()) @@ -853,85 +969,94 @@ impl ::protobuf::Message for EthereumDefinitions { &mut self.special_fields } - fn new() -> EthereumDefinitions { - EthereumDefinitions::new() + fn new() -> SolanaTokenInfo { + SolanaTokenInfo::new() } fn clear(&mut self) { - self.encoded_network = ::std::option::Option::None; - self.encoded_token = ::std::option::Option::None; + self.mint = ::std::option::Option::None; + self.program_id = ::std::option::Option::None; + self.name = ::std::option::Option::None; + self.ticker = ::std::option::Option::None; self.special_fields.clear(); } - fn default_instance() -> &'static EthereumDefinitions { - static instance: EthereumDefinitions = EthereumDefinitions { - encoded_network: ::std::option::Option::None, - encoded_token: ::std::option::Option::None, + fn default_instance() -> &'static SolanaTokenInfo { + static instance: SolanaTokenInfo = SolanaTokenInfo { + mint: ::std::option::Option::None, + program_id: ::std::option::Option::None, + name: ::std::option::Option::None, + ticker: ::std::option::Option::None, special_fields: ::protobuf::SpecialFields::new(), }; &instance } } -impl ::protobuf::MessageFull for EthereumDefinitions { +impl ::protobuf::MessageFull for SolanaTokenInfo { fn descriptor() -> ::protobuf::reflect::MessageDescriptor { static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().message_by_package_relative_name("EthereumDefinitions").unwrap()).clone() + descriptor.get(|| file_descriptor().message_by_package_relative_name("SolanaTokenInfo").unwrap()).clone() } } -impl ::std::fmt::Display for EthereumDefinitions { +impl ::std::fmt::Display for SolanaTokenInfo { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for EthereumDefinitions { +impl ::protobuf::reflect::ProtobufValue for SolanaTokenInfo { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; } #[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] -// @@protoc_insertion_point(enum:hw.trezor.messages.ethereum_definitions.EthereumDefinitionType) -pub enum EthereumDefinitionType { - // @@protoc_insertion_point(enum_value:hw.trezor.messages.ethereum_definitions.EthereumDefinitionType.NETWORK) - NETWORK = 0, - // @@protoc_insertion_point(enum_value:hw.trezor.messages.ethereum_definitions.EthereumDefinitionType.TOKEN) - TOKEN = 1, +// @@protoc_insertion_point(enum:hw.trezor.messages.definitions.DefinitionType) +pub enum DefinitionType { + // @@protoc_insertion_point(enum_value:hw.trezor.messages.definitions.DefinitionType.ETHEREUM_NETWORK) + ETHEREUM_NETWORK = 0, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.definitions.DefinitionType.ETHEREUM_TOKEN) + ETHEREUM_TOKEN = 1, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.definitions.DefinitionType.SOLANA_TOKEN) + SOLANA_TOKEN = 2, } -impl ::protobuf::Enum for EthereumDefinitionType { - const NAME: &'static str = "EthereumDefinitionType"; +impl ::protobuf::Enum for DefinitionType { + const NAME: &'static str = "DefinitionType"; fn value(&self) -> i32 { *self as i32 } - fn from_i32(value: i32) -> ::std::option::Option { + fn from_i32(value: i32) -> ::std::option::Option { match value { - 0 => ::std::option::Option::Some(EthereumDefinitionType::NETWORK), - 1 => ::std::option::Option::Some(EthereumDefinitionType::TOKEN), + 0 => ::std::option::Option::Some(DefinitionType::ETHEREUM_NETWORK), + 1 => ::std::option::Option::Some(DefinitionType::ETHEREUM_TOKEN), + 2 => ::std::option::Option::Some(DefinitionType::SOLANA_TOKEN), _ => ::std::option::Option::None } } - fn from_str(str: &str) -> ::std::option::Option { + fn from_str(str: &str) -> ::std::option::Option { match str { - "NETWORK" => ::std::option::Option::Some(EthereumDefinitionType::NETWORK), - "TOKEN" => ::std::option::Option::Some(EthereumDefinitionType::TOKEN), + "ETHEREUM_NETWORK" => ::std::option::Option::Some(DefinitionType::ETHEREUM_NETWORK), + "ETHEREUM_TOKEN" => ::std::option::Option::Some(DefinitionType::ETHEREUM_TOKEN), + "SOLANA_TOKEN" => ::std::option::Option::Some(DefinitionType::SOLANA_TOKEN), _ => ::std::option::Option::None } } - const VALUES: &'static [EthereumDefinitionType] = &[ - EthereumDefinitionType::NETWORK, - EthereumDefinitionType::TOKEN, + const VALUES: &'static [DefinitionType] = &[ + DefinitionType::ETHEREUM_NETWORK, + DefinitionType::ETHEREUM_TOKEN, + DefinitionType::SOLANA_TOKEN, ]; } -impl ::protobuf::EnumFull for EthereumDefinitionType { +impl ::protobuf::EnumFull for DefinitionType { fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().enum_by_package_relative_name("EthereumDefinitionType").unwrap()).clone() + descriptor.get(|| file_descriptor().enum_by_package_relative_name("DefinitionType").unwrap()).clone() } fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { @@ -940,32 +1065,34 @@ impl ::protobuf::EnumFull for EthereumDefinitionType { } } -impl ::std::default::Default for EthereumDefinitionType { +impl ::std::default::Default for DefinitionType { fn default() -> Self { - EthereumDefinitionType::NETWORK + DefinitionType::ETHEREUM_NETWORK } } -impl EthereumDefinitionType { +impl DefinitionType { fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { - ::protobuf::reflect::GeneratedEnumDescriptorData::new::("EthereumDefinitionType") + ::protobuf::reflect::GeneratedEnumDescriptorData::new::("DefinitionType") } } static file_descriptor_proto_data: &'static [u8] = b"\ - \n#messages-ethereum-definitions.proto\x12'hw.trezor.messages.ethereum_d\ - efinitions\"t\n\x13EthereumNetworkInfo\x12\x19\n\x08chain_id\x18\x01\x20\ - \x02(\x04R\x07chainId\x12\x16\n\x06symbol\x18\x02\x20\x02(\tR\x06symbol\ - \x12\x16\n\x06slip44\x18\x03\x20\x02(\rR\x06slip44\x12\x12\n\x04name\x18\ - \x04\x20\x02(\tR\x04name\"\x90\x01\n\x11EthereumTokenInfo\x12\x18\n\x07a\ - ddress\x18\x01\x20\x02(\x0cR\x07address\x12\x19\n\x08chain_id\x18\x02\ - \x20\x02(\x04R\x07chainId\x12\x16\n\x06symbol\x18\x03\x20\x02(\tR\x06sym\ - bol\x12\x1a\n\x08decimals\x18\x04\x20\x02(\rR\x08decimals\x12\x12\n\x04n\ - ame\x18\x05\x20\x02(\tR\x04name\"c\n\x13EthereumDefinitions\x12'\n\x0fen\ - coded_network\x18\x01\x20\x01(\x0cR\x0eencodedNetwork\x12#\n\rencoded_to\ - ken\x18\x02\x20\x01(\x0cR\x0cencodedToken*0\n\x16EthereumDefinitionType\ - \x12\x0b\n\x07NETWORK\x10\0\x12\t\n\x05TOKEN\x10\x01BG\n#com.satoshilabs\ - .trezor.lib.protobufB\x20TrezorMessageEthereumDefinitions\ + \n\x1amessages-definitions.proto\x12\x1ehw.trezor.messages.definitions\"\ + t\n\x13EthereumNetworkInfo\x12\x19\n\x08chain_id\x18\x01\x20\x02(\x04R\ + \x07chainId\x12\x16\n\x06symbol\x18\x02\x20\x02(\tR\x06symbol\x12\x16\n\ + \x06slip44\x18\x03\x20\x02(\rR\x06slip44\x12\x12\n\x04name\x18\x04\x20\ + \x02(\tR\x04name\"\x90\x01\n\x11EthereumTokenInfo\x12\x18\n\x07address\ + \x18\x01\x20\x02(\x0cR\x07address\x12\x19\n\x08chain_id\x18\x02\x20\x02(\ + \x04R\x07chainId\x12\x16\n\x06symbol\x18\x03\x20\x02(\tR\x06symbol\x12\ + \x1a\n\x08decimals\x18\x04\x20\x02(\rR\x08decimals\x12\x12\n\x04name\x18\ + \x05\x20\x02(\tR\x04name\"p\n\x0fSolanaTokenInfo\x12\x12\n\x04mint\x18\ + \x01\x20\x02(\x0cR\x04mint\x12\x1d\n\nprogram_id\x18\x02\x20\x02(\tR\tpr\ + ogramId\x12\x12\n\x04name\x18\x03\x20\x02(\tR\x04name\x12\x16\n\x06ticke\ + r\x18\x04\x20\x02(\tR\x06ticker*L\n\x0eDefinitionType\x12\x14\n\x10ETHER\ + EUM_NETWORK\x10\0\x12\x12\n\x0eETHEREUM_TOKEN\x10\x01\x12\x10\n\x0cSOLAN\ + A_TOKEN\x10\x02B?\n#com.satoshilabs.trezor.lib.protobufB\x18TrezorMessag\ + eDefinitions\ "; /// `FileDescriptorProto` object which was a source for this generated file @@ -986,9 +1113,9 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { let mut messages = ::std::vec::Vec::with_capacity(3); messages.push(EthereumNetworkInfo::generated_message_descriptor_data()); messages.push(EthereumTokenInfo::generated_message_descriptor_data()); - messages.push(EthereumDefinitions::generated_message_descriptor_data()); + messages.push(SolanaTokenInfo::generated_message_descriptor_data()); let mut enums = ::std::vec::Vec::with_capacity(1); - enums.push(EthereumDefinitionType::generated_enum_descriptor_data()); + enums.push(DefinitionType::generated_enum_descriptor_data()); ::protobuf::reflect::GeneratedFileDescriptor::new_generated( file_descriptor_proto(), deps, diff --git a/rust/trezor-client/src/protos/generated/messages_ethereum.rs b/rust/trezor-client/src/protos/generated/messages_ethereum.rs index 98650c83194..fcc63cf43a1 100644 --- a/rust/trezor-client/src/protos/generated/messages_ethereum.rs +++ b/rust/trezor-client/src/protos/generated/messages_ethereum.rs @@ -865,7 +865,7 @@ pub struct EthereumSignTx { // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignTx.tx_type) pub tx_type: ::std::option::Option, // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignTx.definitions) - pub definitions: ::protobuf::MessageField, + pub definitions: ::protobuf::MessageField, // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignTx.chunkify) pub chunkify: ::std::option::Option, // special fields @@ -1229,7 +1229,7 @@ impl EthereumSignTx { |m: &EthereumSignTx| { &m.tx_type }, |m: &mut EthereumSignTx| { &mut m.tx_type }, )); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::messages_ethereum_definitions::EthereumDefinitions>( + fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EthereumDefinitions>( "definitions", |m: &EthereumSignTx| { &m.definitions }, |m: &mut EthereumSignTx| { &mut m.definitions }, @@ -1497,7 +1497,7 @@ pub struct EthereumSignTxEIP1559 { // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignTxEIP1559.access_list) pub access_list: ::std::vec::Vec, // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignTxEIP1559.definitions) - pub definitions: ::protobuf::MessageField, + pub definitions: ::protobuf::MessageField, // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumSignTxEIP1559.chunkify) pub chunkify: ::std::option::Option, // special fields @@ -1883,7 +1883,7 @@ impl EthereumSignTxEIP1559 { |m: &EthereumSignTxEIP1559| { &m.access_list }, |m: &mut EthereumSignTxEIP1559| { &mut m.access_list }, )); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::messages_ethereum_definitions::EthereumDefinitions>( + fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EthereumDefinitions>( "definitions", |m: &EthereumSignTxEIP1559| { &m.definitions }, |m: &mut EthereumSignTxEIP1559| { &mut m.definitions }, @@ -4093,65 +4093,279 @@ impl ::protobuf::reflect::ProtobufValue for EthereumTypedDataSignature { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; } +// @@protoc_insertion_point(message:hw.trezor.messages.ethereum.EthereumDefinitions) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct EthereumDefinitions { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumDefinitions.encoded_network) + pub encoded_network: ::std::option::Option<::std::vec::Vec>, + // @@protoc_insertion_point(field:hw.trezor.messages.ethereum.EthereumDefinitions.encoded_token) + pub encoded_token: ::std::option::Option<::std::vec::Vec>, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.ethereum.EthereumDefinitions.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a EthereumDefinitions { + fn default() -> &'a EthereumDefinitions { + ::default_instance() + } +} + +impl EthereumDefinitions { + pub fn new() -> EthereumDefinitions { + ::std::default::Default::default() + } + + // optional bytes encoded_network = 1; + + pub fn encoded_network(&self) -> &[u8] { + match self.encoded_network.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_encoded_network(&mut self) { + self.encoded_network = ::std::option::Option::None; + } + + pub fn has_encoded_network(&self) -> bool { + self.encoded_network.is_some() + } + + // Param is passed by value, moved + pub fn set_encoded_network(&mut self, v: ::std::vec::Vec) { + self.encoded_network = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_encoded_network(&mut self) -> &mut ::std::vec::Vec { + if self.encoded_network.is_none() { + self.encoded_network = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.encoded_network.as_mut().unwrap() + } + + // Take field + pub fn take_encoded_network(&mut self) -> ::std::vec::Vec { + self.encoded_network.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + // optional bytes encoded_token = 2; + + pub fn encoded_token(&self) -> &[u8] { + match self.encoded_token.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_encoded_token(&mut self) { + self.encoded_token = ::std::option::Option::None; + } + + pub fn has_encoded_token(&self) -> bool { + self.encoded_token.is_some() + } + + // Param is passed by value, moved + pub fn set_encoded_token(&mut self, v: ::std::vec::Vec) { + self.encoded_token = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_encoded_token(&mut self) -> &mut ::std::vec::Vec { + if self.encoded_token.is_none() { + self.encoded_token = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.encoded_token.as_mut().unwrap() + } + + // Take field + pub fn take_encoded_token(&mut self) -> ::std::vec::Vec { + self.encoded_token.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(2); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "encoded_network", + |m: &EthereumDefinitions| { &m.encoded_network }, + |m: &mut EthereumDefinitions| { &mut m.encoded_network }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "encoded_token", + |m: &EthereumDefinitions| { &m.encoded_token }, + |m: &mut EthereumDefinitions| { &mut m.encoded_token }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "EthereumDefinitions", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for EthereumDefinitions { + const NAME: &'static str = "EthereumDefinitions"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.encoded_network = ::std::option::Option::Some(is.read_bytes()?); + }, + 18 => { + self.encoded_token = ::std::option::Option::Some(is.read_bytes()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.encoded_network.as_ref() { + my_size += ::protobuf::rt::bytes_size(1, &v); + } + if let Some(v) = self.encoded_token.as_ref() { + my_size += ::protobuf::rt::bytes_size(2, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.encoded_network.as_ref() { + os.write_bytes(1, v)?; + } + if let Some(v) = self.encoded_token.as_ref() { + os.write_bytes(2, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> EthereumDefinitions { + EthereumDefinitions::new() + } + + fn clear(&mut self) { + self.encoded_network = ::std::option::Option::None; + self.encoded_token = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static EthereumDefinitions { + static instance: EthereumDefinitions = EthereumDefinitions { + encoded_network: ::std::option::Option::None, + encoded_token: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for EthereumDefinitions { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("EthereumDefinitions").unwrap()).clone() + } +} + +impl ::std::fmt::Display for EthereumDefinitions { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for EthereumDefinitions { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + static file_descriptor_proto_data: &'static [u8] = b"\ \n\x17messages-ethereum.proto\x12\x1bhw.trezor.messages.ethereum\x1a\x15\ - messages-common.proto\x1a#messages-ethereum-definitions.proto\"V\n\x14Et\ - hereumGetPublicKey\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\ - \x12!\n\x0cshow_display\x18\x02\x20\x01(\x08R\x0bshowDisplay\"b\n\x11Eth\ - ereumPublicKey\x129\n\x04node\x18\x01\x20\x02(\x0b2%.hw.trezor.messages.\ - common.HDNodeTypeR\x04node\x12\x12\n\x04xpub\x18\x02\x20\x02(\tR\x04xpub\ - \"\x99\x01\n\x12EthereumGetAddress\x12\x1b\n\taddress_n\x18\x01\x20\x03(\ - \rR\x08addressN\x12!\n\x0cshow_display\x18\x02\x20\x01(\x08R\x0bshowDisp\ - lay\x12'\n\x0fencoded_network\x18\x03\x20\x01(\x0cR\x0eencodedNetwork\ - \x12\x1a\n\x08chunkify\x18\x04\x20\x01(\x08R\x08chunkify\"Q\n\x0fEthereu\ - mAddress\x12$\n\x0c_old_address\x18\x01\x20\x01(\x0cR\nOldAddressB\x02\ - \x18\x01\x12\x18\n\x07address\x18\x02\x20\x01(\tR\x07address\"\xad\x03\n\ - \x0eEthereumSignTx\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\ - \x12\x16\n\x05nonce\x18\x02\x20\x01(\x0c:\0R\x05nonce\x12\x1b\n\tgas_pri\ - ce\x18\x03\x20\x02(\x0cR\x08gasPrice\x12\x1b\n\tgas_limit\x18\x04\x20\ - \x02(\x0cR\x08gasLimit\x12\x10\n\x02to\x18\x0b\x20\x01(\t:\0R\x02to\x12\ - \x16\n\x05value\x18\x06\x20\x01(\x0c:\0R\x05value\x12.\n\x12data_initial\ - _chunk\x18\x07\x20\x01(\x0c:\0R\x10dataInitialChunk\x12\"\n\x0bdata_leng\ - th\x18\x08\x20\x01(\r:\x010R\ndataLength\x12\x19\n\x08chain_id\x18\t\x20\ - \x02(\x04R\x07chainId\x12\x17\n\x07tx_type\x18\n\x20\x01(\rR\x06txType\ - \x12^\n\x0bdefinitions\x18\x0c\x20\x01(\x0b2<.hw.trezor.messages.ethereu\ - m_definitions.EthereumDefinitionsR\x0bdefinitions\x12\x1a\n\x08chunkify\ - \x18\r\x20\x01(\x08R\x08chunkify\"\xfc\x04\n\x15EthereumSignTxEIP1559\ - \x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12\x14\n\x05nonce\ - \x18\x02\x20\x02(\x0cR\x05nonce\x12\x1e\n\x0bmax_gas_fee\x18\x03\x20\x02\ - (\x0cR\tmaxGasFee\x12(\n\x10max_priority_fee\x18\x04\x20\x02(\x0cR\x0ema\ - xPriorityFee\x12\x1b\n\tgas_limit\x18\x05\x20\x02(\x0cR\x08gasLimit\x12\ - \x10\n\x02to\x18\x06\x20\x01(\t:\0R\x02to\x12\x14\n\x05value\x18\x07\x20\ - \x02(\x0cR\x05value\x12.\n\x12data_initial_chunk\x18\x08\x20\x01(\x0c:\0\ - R\x10dataInitialChunk\x12\x1f\n\x0bdata_length\x18\t\x20\x02(\rR\ndataLe\ - ngth\x12\x19\n\x08chain_id\x18\n\x20\x02(\x04R\x07chainId\x12f\n\x0bacce\ - ss_list\x18\x0b\x20\x03(\x0b2E.hw.trezor.messages.ethereum.EthereumSignT\ - xEIP1559.EthereumAccessListR\naccessList\x12^\n\x0bdefinitions\x18\x0c\ - \x20\x01(\x0b2<.hw.trezor.messages.ethereum_definitions.EthereumDefiniti\ - onsR\x0bdefinitions\x12\x1a\n\x08chunkify\x18\r\x20\x01(\x08R\x08chunkif\ - y\x1aQ\n\x12EthereumAccessList\x12\x18\n\x07address\x18\x01\x20\x02(\tR\ - \x07address\x12!\n\x0cstorage_keys\x18\x02\x20\x03(\x0cR\x0bstorageKeys\ - \"\x97\x01\n\x11EthereumTxRequest\x12\x1f\n\x0bdata_length\x18\x01\x20\ - \x01(\rR\ndataLength\x12\x1f\n\x0bsignature_v\x18\x02\x20\x01(\rR\nsigna\ - tureV\x12\x1f\n\x0bsignature_r\x18\x03\x20\x01(\x0cR\nsignatureR\x12\x1f\ - \n\x0bsignature_s\x18\x04\x20\x01(\x0cR\nsignatureS\".\n\rEthereumTxAck\ - \x12\x1d\n\ndata_chunk\x18\x01\x20\x02(\x0cR\tdataChunk\"\x91\x01\n\x13E\ - thereumSignMessage\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\ - \x12\x18\n\x07message\x18\x02\x20\x02(\x0cR\x07message\x12'\n\x0fencoded\ - _network\x18\x03\x20\x01(\x0cR\x0eencodedNetwork\x12\x1a\n\x08chunkify\ - \x18\x04\x20\x01(\x08R\x08chunkify\"R\n\x18EthereumMessageSignature\x12\ - \x1c\n\tsignature\x18\x02\x20\x02(\x0cR\tsignature\x12\x18\n\x07address\ - \x18\x03\x20\x02(\tR\x07address\"\x85\x01\n\x15EthereumVerifyMessage\x12\ - \x1c\n\tsignature\x18\x02\x20\x02(\x0cR\tsignature\x12\x18\n\x07message\ - \x18\x03\x20\x02(\x0cR\x07message\x12\x18\n\x07address\x18\x04\x20\x02(\ - \tR\x07address\x12\x1a\n\x08chunkify\x18\x05\x20\x01(\x08R\x08chunkify\"\ - \xb4\x01\n\x15EthereumSignTypedHash\x12\x1b\n\taddress_n\x18\x01\x20\x03\ - (\rR\x08addressN\x122\n\x15domain_separator_hash\x18\x02\x20\x02(\x0cR\ - \x13domainSeparatorHash\x12!\n\x0cmessage_hash\x18\x03\x20\x01(\x0cR\x0b\ - messageHash\x12'\n\x0fencoded_network\x18\x04\x20\x01(\x0cR\x0eencodedNe\ - twork\"T\n\x1aEthereumTypedDataSignature\x12\x1c\n\tsignature\x18\x01\ - \x20\x02(\x0cR\tsignature\x12\x18\n\x07address\x18\x02\x20\x02(\tR\x07ad\ - dressB<\n#com.satoshilabs.trezor.lib.protobufB\x15TrezorMessageEthereum\ + messages-common.proto\"V\n\x14EthereumGetPublicKey\x12\x1b\n\taddress_n\ + \x18\x01\x20\x03(\rR\x08addressN\x12!\n\x0cshow_display\x18\x02\x20\x01(\ + \x08R\x0bshowDisplay\"b\n\x11EthereumPublicKey\x129\n\x04node\x18\x01\ + \x20\x02(\x0b2%.hw.trezor.messages.common.HDNodeTypeR\x04node\x12\x12\n\ + \x04xpub\x18\x02\x20\x02(\tR\x04xpub\"\x99\x01\n\x12EthereumGetAddress\ + \x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12!\n\x0cshow_dis\ + play\x18\x02\x20\x01(\x08R\x0bshowDisplay\x12'\n\x0fencoded_network\x18\ + \x03\x20\x01(\x0cR\x0eencodedNetwork\x12\x1a\n\x08chunkify\x18\x04\x20\ + \x01(\x08R\x08chunkify\"Q\n\x0fEthereumAddress\x12$\n\x0c_old_address\ + \x18\x01\x20\x01(\x0cR\nOldAddressB\x02\x18\x01\x12\x18\n\x07address\x18\ + \x02\x20\x01(\tR\x07address\"\xa1\x03\n\x0eEthereumSignTx\x12\x1b\n\tadd\ + ress_n\x18\x01\x20\x03(\rR\x08addressN\x12\x16\n\x05nonce\x18\x02\x20\ + \x01(\x0c:\0R\x05nonce\x12\x1b\n\tgas_price\x18\x03\x20\x02(\x0cR\x08gas\ + Price\x12\x1b\n\tgas_limit\x18\x04\x20\x02(\x0cR\x08gasLimit\x12\x10\n\ + \x02to\x18\x0b\x20\x01(\t:\0R\x02to\x12\x16\n\x05value\x18\x06\x20\x01(\ + \x0c:\0R\x05value\x12.\n\x12data_initial_chunk\x18\x07\x20\x01(\x0c:\0R\ + \x10dataInitialChunk\x12\"\n\x0bdata_length\x18\x08\x20\x01(\r:\x010R\nd\ + ataLength\x12\x19\n\x08chain_id\x18\t\x20\x02(\x04R\x07chainId\x12\x17\n\ + \x07tx_type\x18\n\x20\x01(\rR\x06txType\x12R\n\x0bdefinitions\x18\x0c\ + \x20\x01(\x0b20.hw.trezor.messages.ethereum.EthereumDefinitionsR\x0bdefi\ + nitions\x12\x1a\n\x08chunkify\x18\r\x20\x01(\x08R\x08chunkify\"\xf0\x04\ + \n\x15EthereumSignTxEIP1559\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08\ + addressN\x12\x14\n\x05nonce\x18\x02\x20\x02(\x0cR\x05nonce\x12\x1e\n\x0b\ + max_gas_fee\x18\x03\x20\x02(\x0cR\tmaxGasFee\x12(\n\x10max_priority_fee\ + \x18\x04\x20\x02(\x0cR\x0emaxPriorityFee\x12\x1b\n\tgas_limit\x18\x05\ + \x20\x02(\x0cR\x08gasLimit\x12\x10\n\x02to\x18\x06\x20\x01(\t:\0R\x02to\ + \x12\x14\n\x05value\x18\x07\x20\x02(\x0cR\x05value\x12.\n\x12data_initia\ + l_chunk\x18\x08\x20\x01(\x0c:\0R\x10dataInitialChunk\x12\x1f\n\x0bdata_l\ + ength\x18\t\x20\x02(\rR\ndataLength\x12\x19\n\x08chain_id\x18\n\x20\x02(\ + \x04R\x07chainId\x12f\n\x0baccess_list\x18\x0b\x20\x03(\x0b2E.hw.trezor.\ + messages.ethereum.EthereumSignTxEIP1559.EthereumAccessListR\naccessList\ + \x12R\n\x0bdefinitions\x18\x0c\x20\x01(\x0b20.hw.trezor.messages.ethereu\ + m.EthereumDefinitionsR\x0bdefinitions\x12\x1a\n\x08chunkify\x18\r\x20\ + \x01(\x08R\x08chunkify\x1aQ\n\x12EthereumAccessList\x12\x18\n\x07address\ + \x18\x01\x20\x02(\tR\x07address\x12!\n\x0cstorage_keys\x18\x02\x20\x03(\ + \x0cR\x0bstorageKeys\"\x97\x01\n\x11EthereumTxRequest\x12\x1f\n\x0bdata_\ + length\x18\x01\x20\x01(\rR\ndataLength\x12\x1f\n\x0bsignature_v\x18\x02\ + \x20\x01(\rR\nsignatureV\x12\x1f\n\x0bsignature_r\x18\x03\x20\x01(\x0cR\ + \nsignatureR\x12\x1f\n\x0bsignature_s\x18\x04\x20\x01(\x0cR\nsignatureS\ + \".\n\rEthereumTxAck\x12\x1d\n\ndata_chunk\x18\x01\x20\x02(\x0cR\tdataCh\ + unk\"\x91\x01\n\x13EthereumSignMessage\x12\x1b\n\taddress_n\x18\x01\x20\ + \x03(\rR\x08addressN\x12\x18\n\x07message\x18\x02\x20\x02(\x0cR\x07messa\ + ge\x12'\n\x0fencoded_network\x18\x03\x20\x01(\x0cR\x0eencodedNetwork\x12\ + \x1a\n\x08chunkify\x18\x04\x20\x01(\x08R\x08chunkify\"R\n\x18EthereumMes\ + sageSignature\x12\x1c\n\tsignature\x18\x02\x20\x02(\x0cR\tsignature\x12\ + \x18\n\x07address\x18\x03\x20\x02(\tR\x07address\"\x85\x01\n\x15Ethereum\ + VerifyMessage\x12\x1c\n\tsignature\x18\x02\x20\x02(\x0cR\tsignature\x12\ + \x18\n\x07message\x18\x03\x20\x02(\x0cR\x07message\x12\x18\n\x07address\ + \x18\x04\x20\x02(\tR\x07address\x12\x1a\n\x08chunkify\x18\x05\x20\x01(\ + \x08R\x08chunkify\"\xb4\x01\n\x15EthereumSignTypedHash\x12\x1b\n\taddres\ + s_n\x18\x01\x20\x03(\rR\x08addressN\x122\n\x15domain_separator_hash\x18\ + \x02\x20\x02(\x0cR\x13domainSeparatorHash\x12!\n\x0cmessage_hash\x18\x03\ + \x20\x01(\x0cR\x0bmessageHash\x12'\n\x0fencoded_network\x18\x04\x20\x01(\ + \x0cR\x0eencodedNetwork\"T\n\x1aEthereumTypedDataSignature\x12\x1c\n\tsi\ + gnature\x18\x01\x20\x02(\x0cR\tsignature\x12\x18\n\x07address\x18\x02\ + \x20\x02(\tR\x07address\"c\n\x13EthereumDefinitions\x12'\n\x0fencoded_ne\ + twork\x18\x01\x20\x01(\x0cR\x0eencodedNetwork\x12#\n\rencoded_token\x18\ + \x02\x20\x01(\x0cR\x0cencodedTokenB<\n#com.satoshilabs.trezor.lib.protob\ + ufB\x15TrezorMessageEthereum\ "; /// `FileDescriptorProto` object which was a source for this generated file @@ -4168,10 +4382,9 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); file_descriptor.get(|| { let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { - let mut deps = ::std::vec::Vec::with_capacity(2); + let mut deps = ::std::vec::Vec::with_capacity(1); deps.push(super::messages_common::file_descriptor().clone()); - deps.push(super::messages_ethereum_definitions::file_descriptor().clone()); - let mut messages = ::std::vec::Vec::with_capacity(14); + let mut messages = ::std::vec::Vec::with_capacity(15); messages.push(EthereumGetPublicKey::generated_message_descriptor_data()); messages.push(EthereumPublicKey::generated_message_descriptor_data()); messages.push(EthereumGetAddress::generated_message_descriptor_data()); @@ -4185,6 +4398,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { messages.push(EthereumVerifyMessage::generated_message_descriptor_data()); messages.push(EthereumSignTypedHash::generated_message_descriptor_data()); messages.push(EthereumTypedDataSignature::generated_message_descriptor_data()); + messages.push(EthereumDefinitions::generated_message_descriptor_data()); messages.push(ethereum_sign_tx_eip1559::EthereumAccessList::generated_message_descriptor_data()); let mut enums = ::std::vec::Vec::with_capacity(0); ::protobuf::reflect::GeneratedFileDescriptor::new_generated( diff --git a/rust/trezor-client/src/protos/generated/messages_ethereum_eip712.rs b/rust/trezor-client/src/protos/generated/messages_ethereum_eip712.rs index 84cde85c8a2..221e4ffdff4 100644 --- a/rust/trezor-client/src/protos/generated/messages_ethereum_eip712.rs +++ b/rust/trezor-client/src/protos/generated/messages_ethereum_eip712.rs @@ -36,7 +36,7 @@ pub struct EthereumSignTypedData { // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_eip712.EthereumSignTypedData.metamask_v4_compat) pub metamask_v4_compat: ::std::option::Option, // @@protoc_insertion_point(field:hw.trezor.messages.ethereum_eip712.EthereumSignTypedData.definitions) - pub definitions: ::protobuf::MessageField, + pub definitions: ::protobuf::MessageField, // special fields // @@protoc_insertion_point(special_field:hw.trezor.messages.ethereum_eip712.EthereumSignTypedData.special_fields) pub special_fields: ::protobuf::SpecialFields, @@ -126,7 +126,7 @@ impl EthereumSignTypedData { |m: &EthereumSignTypedData| { &m.metamask_v4_compat }, |m: &mut EthereumSignTypedData| { &mut m.metamask_v4_compat }, )); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::messages_ethereum_definitions::EthereumDefinitions>( + fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::messages_ethereum::EthereumDefinitions>( "definitions", |m: &EthereumSignTypedData| { &m.definitions }, |m: &mut EthereumSignTypedData| { &mut m.definitions }, @@ -1400,31 +1400,31 @@ impl ::protobuf::reflect::ProtobufValue for EthereumTypedDataValueAck { static file_descriptor_proto_data: &'static [u8] = b"\ \n\x1emessages-ethereum-eip712.proto\x12\"hw.trezor.messages.ethereum_ei\ - p712\x1a#messages-ethereum-definitions.proto\"\xeb\x01\n\x15EthereumSign\ - TypedData\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12!\n\ - \x0cprimary_type\x18\x02\x20\x02(\tR\x0bprimaryType\x122\n\x12metamask_v\ - 4_compat\x18\x03\x20\x01(\x08:\x04trueR\x10metamaskV4Compat\x12^\n\x0bde\ - finitions\x18\x04\x20\x01(\x0b2<.hw.trezor.messages.ethereum_definitions\ - .EthereumDefinitionsR\x0bdefinitions\"4\n\x1eEthereumTypedDataStructRequ\ - est\x12\x12\n\x04name\x18\x01\x20\x02(\tR\x04name\"\xb4\x05\n\x1aEthereu\ - mTypedDataStructAck\x12m\n\x07members\x18\x01\x20\x03(\x0b2S.hw.trezor.m\ - essages.ethereum_eip712.EthereumTypedDataStructAck.EthereumStructMemberR\ - \x07members\x1a\x90\x01\n\x14EthereumStructMember\x12d\n\x04type\x18\x01\ - \x20\x02(\x0b2P.hw.trezor.messages.ethereum_eip712.EthereumTypedDataStru\ - ctAck.EthereumFieldTypeR\x04type\x12\x12\n\x04name\x18\x02\x20\x02(\tR\ - \x04name\x1a\xa7\x02\n\x11EthereumFieldType\x12l\n\tdata_type\x18\x01\ - \x20\x02(\x0e2O.hw.trezor.messages.ethereum_eip712.EthereumTypedDataStru\ - ctAck.EthereumDataTypeR\x08dataType\x12\x12\n\x04size\x18\x02\x20\x01(\r\ - R\x04size\x12o\n\nentry_type\x18\x03\x20\x01(\x0b2P.hw.trezor.messages.e\ - thereum_eip712.EthereumTypedDataStructAck.EthereumFieldTypeR\tentryType\ - \x12\x1f\n\x0bstruct_name\x18\x04\x20\x01(\tR\nstructName\"j\n\x10Ethere\ - umDataType\x12\x08\n\x04UINT\x10\x01\x12\x07\n\x03INT\x10\x02\x12\t\n\ - \x05BYTES\x10\x03\x12\n\n\x06STRING\x10\x04\x12\x08\n\x04BOOL\x10\x05\ - \x12\x0b\n\x07ADDRESS\x10\x06\x12\t\n\x05ARRAY\x10\x07\x12\n\n\x06STRUCT\ - \x10\x08\"@\n\x1dEthereumTypedDataValueRequest\x12\x1f\n\x0bmember_path\ - \x18\x01\x20\x03(\rR\nmemberPath\"1\n\x19EthereumTypedDataValueAck\x12\ - \x14\n\x05value\x18\x01\x20\x02(\x0cR\x05valueBB\n#com.satoshilabs.trezo\ - r.lib.protobufB\x1bTrezorMessageEthereumEIP712\ + p712\x1a\x17messages-ethereum.proto\"\xdf\x01\n\x15EthereumSignTypedData\ + \x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12!\n\x0cprimary_\ + type\x18\x02\x20\x02(\tR\x0bprimaryType\x122\n\x12metamask_v4_compat\x18\ + \x03\x20\x01(\x08:\x04trueR\x10metamaskV4Compat\x12R\n\x0bdefinitions\ + \x18\x04\x20\x01(\x0b20.hw.trezor.messages.ethereum.EthereumDefinitionsR\ + \x0bdefinitions\"4\n\x1eEthereumTypedDataStructRequest\x12\x12\n\x04name\ + \x18\x01\x20\x02(\tR\x04name\"\xb4\x05\n\x1aEthereumTypedDataStructAck\ + \x12m\n\x07members\x18\x01\x20\x03(\x0b2S.hw.trezor.messages.ethereum_ei\ + p712.EthereumTypedDataStructAck.EthereumStructMemberR\x07members\x1a\x90\ + \x01\n\x14EthereumStructMember\x12d\n\x04type\x18\x01\x20\x02(\x0b2P.hw.\ + trezor.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumField\ + TypeR\x04type\x12\x12\n\x04name\x18\x02\x20\x02(\tR\x04name\x1a\xa7\x02\ + \n\x11EthereumFieldType\x12l\n\tdata_type\x18\x01\x20\x02(\x0e2O.hw.trez\ + or.messages.ethereum_eip712.EthereumTypedDataStructAck.EthereumDataTypeR\ + \x08dataType\x12\x12\n\x04size\x18\x02\x20\x01(\rR\x04size\x12o\n\nentry\ + _type\x18\x03\x20\x01(\x0b2P.hw.trezor.messages.ethereum_eip712.Ethereum\ + TypedDataStructAck.EthereumFieldTypeR\tentryType\x12\x1f\n\x0bstruct_nam\ + e\x18\x04\x20\x01(\tR\nstructName\"j\n\x10EthereumDataType\x12\x08\n\x04\ + UINT\x10\x01\x12\x07\n\x03INT\x10\x02\x12\t\n\x05BYTES\x10\x03\x12\n\n\ + \x06STRING\x10\x04\x12\x08\n\x04BOOL\x10\x05\x12\x0b\n\x07ADDRESS\x10\ + \x06\x12\t\n\x05ARRAY\x10\x07\x12\n\n\x06STRUCT\x10\x08\"@\n\x1dEthereum\ + TypedDataValueRequest\x12\x1f\n\x0bmember_path\x18\x01\x20\x03(\rR\nmemb\ + erPath\"1\n\x19EthereumTypedDataValueAck\x12\x14\n\x05value\x18\x01\x20\ + \x02(\x0cR\x05valueBB\n#com.satoshilabs.trezor.lib.protobufB\x1bTrezorMe\ + ssageEthereumEIP712\ "; /// `FileDescriptorProto` object which was a source for this generated file @@ -1442,7 +1442,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { file_descriptor.get(|| { let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { let mut deps = ::std::vec::Vec::with_capacity(1); - deps.push(super::messages_ethereum_definitions::file_descriptor().clone()); + deps.push(super::messages_ethereum::file_descriptor().clone()); let mut messages = ::std::vec::Vec::with_capacity(7); messages.push(EthereumSignTypedData::generated_message_descriptor_data()); messages.push(EthereumTypedDataStructRequest::generated_message_descriptor_data()); diff --git a/rust/trezor-client/src/protos/generated/messages_solana.rs b/rust/trezor-client/src/protos/generated/messages_solana.rs index 2531e9c47b7..203a13bf736 100644 --- a/rust/trezor-client/src/protos/generated/messages_solana.rs +++ b/rust/trezor-client/src/protos/generated/messages_solana.rs @@ -1046,6 +1046,8 @@ pub struct SolanaTxAdditionalInfo { // message fields // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaTxAdditionalInfo.token_accounts_infos) pub token_accounts_infos: ::std::vec::Vec, + // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaTxAdditionalInfo.definitions) + pub definitions: ::protobuf::MessageField, // special fields // @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaTxAdditionalInfo.special_fields) pub special_fields: ::protobuf::SpecialFields, @@ -1063,13 +1065,18 @@ impl SolanaTxAdditionalInfo { } fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(1); + let mut fields = ::std::vec::Vec::with_capacity(2); let mut oneofs = ::std::vec::Vec::with_capacity(0); fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( "token_accounts_infos", |m: &SolanaTxAdditionalInfo| { &m.token_accounts_infos }, |m: &mut SolanaTxAdditionalInfo| { &mut m.token_accounts_infos }, )); + fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, SolanaDefinitions>( + "definitions", + |m: &SolanaTxAdditionalInfo| { &m.definitions }, + |m: &mut SolanaTxAdditionalInfo| { &mut m.definitions }, + )); ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( "SolanaTxAdditionalInfo", fields, @@ -1087,6 +1094,11 @@ impl ::protobuf::Message for SolanaTxAdditionalInfo { return false; } }; + for v in &self.definitions { + if !v.is_initialized() { + return false; + } + }; true } @@ -1096,6 +1108,9 @@ impl ::protobuf::Message for SolanaTxAdditionalInfo { 10 => { self.token_accounts_infos.push(is.read_message()?); }, + 18 => { + ::protobuf::rt::read_singular_message_into_field(is, &mut self.definitions)?; + }, tag => { ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; }, @@ -1112,6 +1127,10 @@ impl ::protobuf::Message for SolanaTxAdditionalInfo { let len = value.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; }; + if let Some(v) = self.definitions.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + } my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); self.special_fields.cached_size().set(my_size as u32); my_size @@ -1121,6 +1140,9 @@ impl ::protobuf::Message for SolanaTxAdditionalInfo { for v in &self.token_accounts_infos { ::protobuf::rt::write_message_field_with_cached_size(1, v, os)?; }; + if let Some(v) = self.definitions.as_ref() { + ::protobuf::rt::write_message_field_with_cached_size(2, v, os)?; + } os.write_unknown_fields(self.special_fields.unknown_fields())?; ::std::result::Result::Ok(()) } @@ -1139,12 +1161,14 @@ impl ::protobuf::Message for SolanaTxAdditionalInfo { fn clear(&mut self) { self.token_accounts_infos.clear(); + self.definitions.clear(); self.special_fields.clear(); } fn default_instance() -> &'static SolanaTxAdditionalInfo { static instance: SolanaTxAdditionalInfo = SolanaTxAdditionalInfo { token_accounts_infos: ::std::vec::Vec::new(), + definitions: ::protobuf::MessageField::none(), special_fields: ::protobuf::SpecialFields::new(), }; &instance @@ -1535,6 +1559,164 @@ impl ::protobuf::reflect::ProtobufValue for SolanaTxSignature { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; } +// @@protoc_insertion_point(message:hw.trezor.messages.solana.SolanaDefinitions) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct SolanaDefinitions { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaDefinitions.encoded_token) + pub encoded_token: ::std::option::Option<::std::vec::Vec>, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaDefinitions.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a SolanaDefinitions { + fn default() -> &'a SolanaDefinitions { + ::default_instance() + } +} + +impl SolanaDefinitions { + pub fn new() -> SolanaDefinitions { + ::std::default::Default::default() + } + + // optional bytes encoded_token = 1; + + pub fn encoded_token(&self) -> &[u8] { + match self.encoded_token.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_encoded_token(&mut self) { + self.encoded_token = ::std::option::Option::None; + } + + pub fn has_encoded_token(&self) -> bool { + self.encoded_token.is_some() + } + + // Param is passed by value, moved + pub fn set_encoded_token(&mut self, v: ::std::vec::Vec) { + self.encoded_token = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_encoded_token(&mut self) -> &mut ::std::vec::Vec { + if self.encoded_token.is_none() { + self.encoded_token = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.encoded_token.as_mut().unwrap() + } + + // Take field + pub fn take_encoded_token(&mut self) -> ::std::vec::Vec { + self.encoded_token.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(1); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "encoded_token", + |m: &SolanaDefinitions| { &m.encoded_token }, + |m: &mut SolanaDefinitions| { &mut m.encoded_token }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "SolanaDefinitions", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for SolanaDefinitions { + const NAME: &'static str = "SolanaDefinitions"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.encoded_token = ::std::option::Option::Some(is.read_bytes()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.encoded_token.as_ref() { + my_size += ::protobuf::rt::bytes_size(1, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.encoded_token.as_ref() { + os.write_bytes(1, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> SolanaDefinitions { + SolanaDefinitions::new() + } + + fn clear(&mut self) { + self.encoded_token = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static SolanaDefinitions { + static instance: SolanaDefinitions = SolanaDefinitions { + encoded_token: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for SolanaDefinitions { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("SolanaDefinitions").unwrap()).clone() + } +} + +impl ::std::fmt::Display for SolanaDefinitions { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for SolanaDefinitions { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + static file_descriptor_proto_data: &'static [u8] = b"\ \n\x15messages-solana.proto\x12\x19hw.trezor.messages.solana\"T\n\x12Sol\ anaGetPublicKey\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12\ @@ -1547,14 +1729,17 @@ static file_descriptor_proto_data: &'static [u8] = b"\ TokenAccountInfo\x12!\n\x0cbase_address\x18\x01\x20\x02(\tR\x0bbaseAddre\ ss\x12#\n\rtoken_program\x18\x02\x20\x02(\tR\x0ctokenProgram\x12\x1d\n\n\ token_mint\x18\x03\x20\x02(\tR\ttokenMint\x12#\n\rtoken_account\x18\x04\ - \x20\x02(\tR\x0ctokenAccount\"\x7f\n\x16SolanaTxAdditionalInfo\x12e\n\ - \x14token_accounts_infos\x18\x01\x20\x03(\x0b23.hw.trezor.messages.solan\ - a.SolanaTxTokenAccountInfoR\x12tokenAccountsInfos\"\xac\x01\n\x0cSolanaS\ - ignTx\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\x12#\n\rseria\ - lized_tx\x18\x02\x20\x02(\x0cR\x0cserializedTx\x12Z\n\x0fadditional_info\ - \x18\x03\x20\x01(\x0b21.hw.trezor.messages.solana.SolanaTxAdditionalInfo\ - R\x0eadditionalInfo\"1\n\x11SolanaTxSignature\x12\x1c\n\tsignature\x18\ - \x01\x20\x02(\x0cR\tsignature\ + \x20\x02(\tR\x0ctokenAccount\"\xcf\x01\n\x16SolanaTxAdditionalInfo\x12e\ + \n\x14token_accounts_infos\x18\x01\x20\x03(\x0b23.hw.trezor.messages.sol\ + ana.SolanaTxTokenAccountInfoR\x12tokenAccountsInfos\x12N\n\x0bdefinition\ + s\x18\x02\x20\x01(\x0b2,.hw.trezor.messages.solana.SolanaDefinitionsR\ + \x0bdefinitions\"\xac\x01\n\x0cSolanaSignTx\x12\x1b\n\taddress_n\x18\x01\ + \x20\x03(\rR\x08addressN\x12#\n\rserialized_tx\x18\x02\x20\x02(\x0cR\x0c\ + serializedTx\x12Z\n\x0fadditional_info\x18\x03\x20\x01(\x0b21.hw.trezor.\ + messages.solana.SolanaTxAdditionalInfoR\x0eadditionalInfo\"1\n\x11Solana\ + TxSignature\x12\x1c\n\tsignature\x18\x01\x20\x02(\x0cR\tsignature\"8\n\ + \x11SolanaDefinitions\x12#\n\rencoded_token\x18\x01\x20\x01(\x0cR\x0cenc\ + odedToken\ "; /// `FileDescriptorProto` object which was a source for this generated file @@ -1572,7 +1757,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { file_descriptor.get(|| { let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { let mut deps = ::std::vec::Vec::with_capacity(0); - let mut messages = ::std::vec::Vec::with_capacity(8); + let mut messages = ::std::vec::Vec::with_capacity(9); messages.push(SolanaGetPublicKey::generated_message_descriptor_data()); messages.push(SolanaPublicKey::generated_message_descriptor_data()); messages.push(SolanaGetAddress::generated_message_descriptor_data()); @@ -1581,6 +1766,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { messages.push(SolanaTxAdditionalInfo::generated_message_descriptor_data()); messages.push(SolanaSignTx::generated_message_descriptor_data()); messages.push(SolanaTxSignature::generated_message_descriptor_data()); + messages.push(SolanaDefinitions::generated_message_descriptor_data()); let mut enums = ::std::vec::Vec::with_capacity(0); ::protobuf::reflect::GeneratedFileDescriptor::new_generated( file_descriptor_proto(), diff --git a/tests/device_tests/common/__init__.py b/tests/device_tests/common/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/device_tests/ethereum/common.py b/tests/device_tests/common/common.py similarity index 62% rename from tests/device_tests/ethereum/common.py rename to tests/device_tests/common/common.py index 5fe9e9b8d23..956e9a83182 100644 --- a/tests/device_tests/ethereum/common.py +++ b/tests/device_tests/common/common.py @@ -39,11 +39,22 @@ def make_token( ) +def make_solana_token( + mint: bytes, program_id: str, name: str, ticker: str +) -> messages.SolanaTokenInfo: + return messages.SolanaTokenInfo( + mint=mint, program_id=program_id, name=name, ticker=ticker + ) + + def make_payload( - data_type: messages.EthereumDefinitionType = messages.EthereumDefinitionType.NETWORK, + data_type: messages.DefinitionType = messages.DefinitionType.ETHEREUM_NETWORK, timestamp: int = 0xFFFF_FFFF, message: ( - messages.EthereumNetworkInfo | messages.EthereumTokenInfo | bytes + messages.EthereumNetworkInfo + | messages.EthereumTokenInfo + | messages.SolanaTokenInfo + | bytes ) = make_network(), ) -> bytes: if isinstance(message, bytes): @@ -93,7 +104,7 @@ def encode_network( if network is None: network = make_network(chain_id, slip44, symbol, name) payload = make_payload( - data_type=messages.EthereumDefinitionType.NETWORK, message=network + data_type=messages.DefinitionType.ETHEREUM_NETWORK, message=network ) proof, signature = sign_payload(payload, []) return payload + proof + signature @@ -114,7 +125,7 @@ def encode_token( address = bytes.fromhex(address) # type: ignore (typechecker is lying) token = make_token(symbol, decimals, address, chain_id, name) # type: ignore (typechecker is lying) payload = make_payload( - data_type=messages.EthereumDefinitionType.TOKEN, message=token + data_type=messages.DefinitionType.ETHEREUM_TOKEN, message=token ) proof, signature = sign_payload(payload, []) return payload + proof + signature @@ -127,3 +138,49 @@ def make_defs( encoded_network=network, encoded_token=token, ) + + +def encode_solana_token( + token: messages.SolanaTokenInfo | None = None, + mint: t.AnyStr = b"", + program_id: str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + name: str = "Fake Token", + ticker: str = "FakeTok", +) -> bytes: + if token is None: + if isinstance(mint, str): + if mint.startswith("0x"): + mint = mint[2:] + mint = bytes.fromhex(mint) # type: ignore (typechecker is lying) + token = make_solana_token(mint, program_id, name, ticker) # type: ignore (typechecker is lying) + payload = make_payload( + data_type=messages.DefinitionType.SOLANA_TOKEN, message=token + ) + proof, signature = sign_payload(payload, []) + return payload + proof + signature + + +def make_solana_defs(token: bytes | None) -> messages.SolanaDefinitions: + return messages.SolanaDefinitions(encoded_token=token) + + +def decode_base58(string: str) -> bytes: + """ + Convert base58 encoded string to bytes. + """ + alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + origlen = len(string) + string = string.lstrip(alphabet[0]) + newlen = len(string) + + p, acc = 1, 0 + for c in reversed(string): + acc += p * alphabet.index(c) + p *= 58 + + result = [] + while acc > 0: + acc, mod = divmod(acc, 256) + result.append(mod) + + return bytes((b for b in reversed(result + [0] * (origlen - newlen)))) diff --git a/tests/device_tests/ethereum/test_definitions.py b/tests/device_tests/common/test_definitions.py similarity index 100% rename from tests/device_tests/ethereum/test_definitions.py rename to tests/device_tests/common/test_definitions.py diff --git a/tests/device_tests/ethereum/test_definitions_bad.py b/tests/device_tests/common/test_definitions_bad.py similarity index 80% rename from tests/device_tests/ethereum/test_definitions_bad.py rename to tests/device_tests/common/test_definitions_bad.py index 3f21195643f..1514119d29f 100644 --- a/tests/device_tests/ethereum/test_definitions_bad.py +++ b/tests/device_tests/common/test_definitions_bad.py @@ -7,7 +7,7 @@ from trezorlib import ethereum from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.exceptions import TrezorFailure -from trezorlib.messages import EthereumDefinitionType +from trezorlib.messages import DefinitionType from trezorlib.tools import parse_path from .common import make_defs, make_network, make_payload, make_token, sign_payload @@ -27,7 +27,7 @@ def fails(client: Client, network: bytes, match: str) -> None: def test_short_message(client: Client) -> None: - fails(client, b"\x00", "Invalid Ethereum definition") + fails(client, b"\x00", "Invalid definition") def test_mangled_signature(client: Client) -> None: @@ -46,7 +46,7 @@ def test_not_enough_signatures(client: Client) -> None: def test_missing_signature(client: Client) -> None: payload = make_payload() proof, _ = sign_payload(payload, []) - fails(client, payload + proof, "Invalid Ethereum definition") + fails(client, payload + proof, "Invalid definition") def test_mangled_payload(client: Client) -> None: @@ -60,7 +60,7 @@ def test_proof_length_mismatch(client: Client) -> None: payload = make_payload() _, signature = sign_payload(payload, []) bad_proof = b"\x01" - fails(client, payload + bad_proof + signature, "Invalid Ethereum definition") + fails(client, payload + bad_proof + signature, "Invalid definition") def test_bad_proof(client: Client) -> None: @@ -74,19 +74,21 @@ def test_trimmed_proof(client: Client) -> None: payload = make_payload() proof, signature = sign_payload(payload, []) bad_proof = proof[:-1] - fails(client, payload + bad_proof + signature, "Invalid Ethereum definition") + fails(client, payload + bad_proof + signature, "Invalid definition") def test_bad_prefix(client: Client) -> None: payload = make_payload() payload = b"trzd2" + payload[5:] proof, signature = sign_payload(payload, []) - fails(client, payload + proof + signature, "Invalid Ethereum definition") + fails(client, payload + proof + signature, "Invalid definition") def test_bad_type(client: Client) -> None: # assuming we expect a network definition - payload = make_payload(data_type=EthereumDefinitionType.TOKEN, message=make_token()) + payload = make_payload( + data_type=DefinitionType.ETHEREUM_TOKEN, message=make_token() + ) proof, signature = sign_payload(payload, []) fails(client, payload + proof + signature, "Definition type mismatch") @@ -100,22 +102,22 @@ def test_outdated(client: Client) -> None: def test_malformed_protobuf(client: Client) -> None: payload = make_payload(message=b"\x00") proof, signature = sign_payload(payload, []) - fails(client, payload + proof + signature, "Invalid Ethereum definition") + fails(client, payload + proof + signature, "Invalid definition") def test_protobuf_mismatch(client: Client) -> None: payload = make_payload( - data_type=EthereumDefinitionType.NETWORK, message=make_token() + data_type=DefinitionType.ETHEREUM_NETWORK, message=make_token() ) proof, signature = sign_payload(payload, []) - fails(client, payload + proof + signature, "Invalid Ethereum definition") + fails(client, payload + proof + signature, "Invalid definition") payload = make_payload( - data_type=EthereumDefinitionType.TOKEN, message=make_network() + data_type=DefinitionType.ETHEREUM_TOKEN, message=make_network() ) proof, signature = sign_payload(payload, []) # have to do this manually to invoke a method that eats token definitions - with pytest.raises(TrezorFailure, match="Invalid Ethereum definition"): + with pytest.raises(TrezorFailure, match="Invalid definition"): params = DEFAULT_ERC20_PARAMS.copy() params.update(to=ERC20_FAKE_ADDRESS) ethereum.sign_tx( @@ -128,4 +130,4 @@ def test_protobuf_mismatch(client: Client) -> None: def test_trailing_garbage(client: Client) -> None: payload = make_payload() proof, signature = sign_payload(payload, []) - fails(client, payload + proof + signature + b"\x00", "Invalid Ethereum definition") + fails(client, payload + proof + signature + b"\x00", "Invalid definition") diff --git a/tests/device_tests/ethereum/test_sign_typed_data.py b/tests/device_tests/common/test_sign_typed_data.py similarity index 100% rename from tests/device_tests/ethereum/test_sign_typed_data.py rename to tests/device_tests/common/test_sign_typed_data.py diff --git a/tests/device_tests/ethereum/test_signtx.py b/tests/device_tests/ethereum/test_signtx.py index 17a79bbb54b..5ee0355f9e5 100644 --- a/tests/device_tests/ethereum/test_signtx.py +++ b/tests/device_tests/ethereum/test_signtx.py @@ -32,7 +32,7 @@ InputFlowEthereumSignTxShowFeeInfo, InputFlowEthereumSignTxStaking, ) -from .common import encode_network +from ..common.common import encode_network TO_ADDR = "0x1d1c328764a41bda0492b66baa30c4a339ff85ef" diff --git a/tests/device_tests/solana/test_sign_tx.py b/tests/device_tests/solana/test_sign_tx.py index de7ec344625..b14dc79236c 100644 --- a/tests/device_tests/solana/test_sign_tx.py +++ b/tests/device_tests/solana/test_sign_tx.py @@ -47,27 +47,33 @@ def test_solana_sign_tx(client: Client, parameters, result): serialized_tx = _serialize_tx(parameters["construct"]) + additional_info = None + if "additional_info" in parameters: + definitions = None + token = parameters["additional_info"].get("token", None) + if token: + definitions = messages.SolanaDefinitions(encoded_token=bytes.fromhex(token)) + + additional_info = messages.SolanaTxAdditionalInfo( + token_accounts_infos=[ + messages.SolanaTxTokenAccountInfo( + base_address=token_account["base_address"], + token_program=token_account["token_program"], + token_mint=token_account["token_mint"], + token_account=token_account["token_account"], + ) + for token_account in parameters["additional_info"][ + "token_accounts_infos" + ] + ], + definitions=definitions, + ) + actual_result = sign_tx( client, address_n=parse_path(parameters["address"]), serialized_tx=serialized_tx, - additional_info=( - messages.SolanaTxAdditionalInfo( - token_accounts_infos=[ - messages.SolanaTxTokenAccountInfo( - base_address=token_account["base_address"], - token_program=token_account["token_program"], - token_mint=token_account["token_mint"], - token_account=token_account["token_account"], - ) - for token_account in parameters["additional_info"][ - "token_accounts_infos" - ] - ] - ) - if "additional_info" in parameters - else None - ), + additional_info=additional_info, ) assert actual_result == bytes.fromhex(result["expected_signature"]) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 996903c4e2d..5542ad1557d 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -4789,6 +4789,55 @@ "T2T1_en_cardano-test_sign_tx.py::test_cardano_sign_tx_show_details[plutus_transaction_with_total_co-e846c221": "ae9194503111f31cff06a2ca1d74689dabd913d26424f7afb2b8e36c7a74c421", "T2T1_en_cardano-test_sign_tx.py::test_cardano_sign_tx_show_details[transaction_with_cip36_registrat-b9111c27": "9a4c9da367e6b678d92ae8a34bf6a7cf1fdd2d34e69c1d388179f486ddeed909", "T2T1_en_cardano-test_sign_tx.py::test_cardano_sign_tx_show_details[transaction_with_stake_deregistr-6e84da2f": "b4627a2c012c22b2d1e5c324b60b2037716e341fae997784b470f366f60437c8", +"T2T1_en_common-test_definitions.py::test_builtin": "d2539e7f33c17a40e04eb6d00678727c462fff7438aae3c007e6ca14e4976a04", +"T2T1_en_common-test_definitions.py::test_builtin_token": "a8eb56f2c6f7dd7b253ff6c01268cde9c8256528bde9f8a65451b39b1447fde7", +"T2T1_en_common-test_definitions.py::test_chain_id_allowed": "9583807d78f0293a5607cee8e1654240ce7db565c693fa69847c7e963187c73b", +"T2T1_en_common-test_definitions.py::test_chain_id_mismatch": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_definition_does_not_override_builtin": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_external_chain_token_mismatch": "0d50d90e40dee95579fb842af8aa1988fa589a65e3e92003b9f62dd521960c19", +"T2T1_en_common-test_definitions.py::test_external_chain_token_ok": "595d573292e784cbe5be6779b2099577d27fb6ec8ec1adf37d8a6b3af05741b0", +"T2T1_en_common-test_definitions.py::test_external_chain_without_token": "a6556d149cfced0bc86de30a02b49b7effb1e3892caed9958a2390844be88579", +"T2T1_en_common-test_definitions.py::test_external_token": "2c2ae3a9150b92a4f1d6012830c9274a510d153dd8058e1bdcd11c66c78d835d", +"T2T1_en_common-test_definitions.py::test_method_builtin[_call_getaddress]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_method_builtin[_call_sign_typed_data]": "25c1b6650dd5c2b90d092294102c15c26b41a2cc9787ad9aa8e5a011fdbe017e", +"T2T1_en_common-test_definitions.py::test_method_builtin[_call_signmessage]": "3c50637fab3638151e2d0319eccfec3112a0bb4136c6d1a367fccc5292efb8e1", +"T2T1_en_common-test_definitions.py::test_method_def_missing[_call_getaddress]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_method_def_missing[_call_sign_typed_data]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_method_def_missing[_call_signmessage]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_method_external[_call_getaddress]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_method_external[_call_sign_typed_data]": "2970b57484eef3a5dfb67a62a379a34ee82e1a451ceab0e3ae780a69e46ea7b7", +"T2T1_en_common-test_definitions.py::test_method_external[_call_signmessage]": "86a35613223aab0557125ffb9ee16f0f55e475fdd13c371b1ecd8ebf65568237", +"T2T1_en_common-test_definitions.py::test_method_external_mismatch[_call_getaddress]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_method_external_mismatch[_call_sign_typed_data]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_method_external_mismatch[_call_signmessage]": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_slip44_disallowed": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions.py::test_slip44_external": "766db3ee2a8ea23548171b929b6427b08747c6b62533925eb30bd7cbdb0b54ae", +"T2T1_en_common-test_definitions.py::test_slip44_external_disallowed": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_bad_prefix": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_bad_proof": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_bad_type": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_malformed_protobuf": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_mangled_payload": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_mangled_signature": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_missing_signature": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_not_enough_signatures": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_outdated": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_proof_length_mismatch": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_protobuf_mismatch": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_short_message": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_trailing_garbage": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_definitions_bad.py::test_trimmed_proof": "8b1ccc0dbd6e6e3d02a896650ab90dd332ba4edbbcc4095e0fbb6a96e5256f75", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[array_of_structs]": "f00998e6bf994abfa35c8f062da4ce24184e35c357e5b9fef7d9630b5bdc4ef1", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[bare_minimum]": "ad8379750466ae30930d60233a4243d777170dd5f38da9ba080a07c14e254908", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[basic_data]": "13db6411b89d66363e0067beba5aed1a30917b159fb59c142ffe2e34f58a3cf0", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[complex_data]": "97831154cc3112ec77a897cf71991d01da57201261e592b284dfdd72d6c55350", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[full_domain_empty_message]": "15ef9bfa9b34ebb935c30e6484527b3ba0b19a290061f9c600a3cc59f9e895bb", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[injective_testcase]": "cb4e518d482f1d3b98f926cf9b4b4003589d69d541c11571a0010b076bc04ae0", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[struct_list_non_v4]": "13db6411b89d66363e0067beba5aed1a30917b159fb59c142ffe2e34f58a3cf0", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[struct_list_v4]": "13db6411b89d66363e0067beba5aed1a30917b159fb59c142ffe2e34f58a3cf0", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data[structs_arrays_v4]": "13db6411b89d66363e0067beba5aed1a30917b159fb59c142ffe2e34f58a3cf0", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data_cancel": "f9775b905975fbede768b0313d5bd8ffba55686c25fa146a3e0a4a16ec9f4b75", +"T2T1_en_common-test_sign_typed_data.py::test_ethereum_sign_typed_data_show_more_button": "b6442ba261cc653dcaf9e0ff18e3370c946fcaa393ff59b081d94d9456368cc6", "T2T1_en_eos-test_get_public_key.py::test_eos_get_public_key": "0e6dafb4bffde6bfeaafdae4c78431701a8e687525226e4e1d99fce999a48f32", "T2T1_en_eos-test_signtx.py::test_eos_signtx_buyram": "1be1db79d6e1bd6349abe5ca96bcb2b7e355ec99893b6f2cc162fcb25dbfb6b7", "T2T1_en_eos-test_signtx.py::test_eos_signtx_buyrambytes": "7074a2b5dd76baa6836474a7dc7bbb2d19398a22eac37b73733cde909a5a4531", @@ -5105,76 +5154,77 @@ "T2T1_en_solana-test_public_key.py::test_solana_get_public_key[parameters0-result0]": "413607f6a3f3b314e49268e112529ec0f4fa5a970260a26a62af3460df87e61b", "T2T1_en_solana-test_public_key.py::test_solana_get_public_key[parameters1-result1]": "0f66885cf29e4de25f022b6291f3ea717b3570e88ea74ccdd6a58afb3e65c834", "T2T1_en_solana-test_public_key.py::test_solana_get_public_key[parameters2-result2]": "c7ea089bf0c2f7b1212b6eb1ec713a166e1ce445a0bc43602333e270e515bc21", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[advance_nonce_account]": "248d9c3c5c0febbdd0dac842c0d19243b045372c38d531224a34deeca3c689e3", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[allocate]": "78964cf687ef4670a19799480e7db47c51ac17e52342b59fc0802fe44b41c3b7", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[allocate_with_seed]": "9b5af537f6878abe0ba7dff40ee7fe3a358a769eabfbd85aea775546f968048f", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[approve]": "cd6b98f0382c47eb2744063c10eed4512e4479594ee37be640ac17a7a5d02df9", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[approve_checked]": "c20c8d39451e648ebd06a0995b29a3a57d480b5dd4b6b18492307e6febffd5d9", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[approve_checked_-_multisig]": "66be771d9fd82f742125b73801c3c0e887c872238887908fa9bd546fdc90f583", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[assign]": "050b035145e5ef7f258827f0cf7e1a398b0cfd77ce49da99f7028a8379fa500a", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[assign_with_seed]": "a5b1687b63b2790b9a33cf58779bd0524fcd715fb023dc851e21d7032d1cbefb", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize]": "d36dd209279aab5e2b470c876082b288c176814846b52b1b4ed3713f3ff70dc3", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_checked]": "711502c7396a7388c5e018d5b58444e29c8f590495a826ff91cd38bfb091682c", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_checked_with_seed]": "5266fa3e933e8bf203bfd0f1b9e8dd014d0ab11eb632e12ea12246774603aa20", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_nonce_account]": "368004ce6538e6c66950359d906bb7610fb4f6a848bccbc160a02de12025d634", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_with_seed]": "3a9cb44bb8933a1b35df49fa4b1bf2e287cacfd710dc84788e9ee93204f8fa66", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[burn]": "7ae3abe87dcc439d593fff1b5eb49a20ffbd962ef52acc0d1ea6c467c0b2cba0", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[burn_checked]": "8017c70cb7ca945b985210e05195e0b29689527a0bd233ef65e6972a9a5bac20", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[burn_checked_-_multisig]": "7640124cb84555d1ed5abbfcf4098dfb0dbd4cd5461f9694de6518c14fe668c9", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[close_account]": "c003994e4b5f3d8fb3d3273d2db57239aec8dc1e6231ab3d1a2d46e99ce1a575", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[close_account_-_multisig]": "694eca79abd20df9110c32ecb14adc778ffef4e9d4cc8f8553eaa4643e60220b", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_account]": "48574562182de66e96b7e2e7cf47d97db60b979ce69331176ac42aec5ecdb631", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_account_with_seed]": "086e41f22ab5441d38e61e20a4b8090060dcf01d7ee931479b43ccb2a9c76557", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_associated_token_account]": "b3b601c8cb42a6b4a22337fe9b390893da5e32a82a0201d9a667e3a9f59756ae", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_associated_token_account_idempotent]": "71e2469879c3136758436318e9df98a2cda8b4587e9c367f9af14689bf862da3", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_associated_token_account_with_sysvar_ren-cb526e6e": "b3b601c8cb42a6b4a22337fe9b390893da5e32a82a0201d9a667e3a9f59756ae", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_stake_account]": "484ca9aa68a1025e31a68551fdf346c87a246dc338b203e6811699594d360145", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_stake_account_with_compute_budget]": "466394eef2d1f121a13a337d722ff5defcfbaff93303370c6a31cbe4b124856a", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_token_account_and_transfer_token_-_predefined]": "75a9d45b62c0354cbb912f558fa46c7ca484a3a2c3eb95e772e4a16605f85527", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[deactivate]": "61606d68b2c372f97fa72e649d62d026e43a875c721c403095635c014bee6e58", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[delegate]": "c227efee90e84a86602a854007dd5871d11a58bc7a22dcf5806b1dcf0e97d590", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[freeze_account]": "e3f8c4de71faef72f033e5c76677feff6f8868ed6c0c458927aa57b85be75161", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[freeze_account_-_multisig]": "d6b60f9f65e5b65fe2ce7abbb8509ac9b10d3f84ba76e7486e328f4f3b356e76", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_account]": "ca2bcf86db8a5c004ce2377e7fb9e77b38a5b41ef3bcab1026dd3dc077c4e50b", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_checked]": "11256f6fed17f33d8c1387a596881a5ff275e31ee8bee2abf52168d9c641e80e", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_immutable_owner]": "5017efe44346781be112b5818c09fe4e6217c54d7f0df86602a5eabad41623eb", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_multisig]": "00bffbe9ecb5d29279b3df15f7e8293e02c21424802fd8c76603935004e61db1", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_nonce_account]": "6d76377d2f5fbe8db0f3b5c8b75fae3f53862b16a37c20e0cd16eea7ae734f48", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_stake_account]": "0f9190405943feecc3937f8ec0352187cae6bc1a56506879e6ac4598232479b4", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_token_account_2]": "cb2a890ce7da4f4b386885448235d4a1dc8838eebde5fe53891581849c8dea17", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_token_account_3]": "585e4b98a5f1ccdf880d8633d0dd9fefdb1a9dc4220d1ed190aec10c1b78c0a6", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[lookup_tables]": "546eab08945632111b63eaa5de76b967e6e5e5a84c3ec9a40ee9be0783dc428f", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[memo]": "5565c6ee8e954b8b661885516f0efbfe9f0e49eaaf597400b5275f255afc4d6e", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[memo_legacy]": "8a5d133ce06a247988f6109d488a618e4926793f6ac2abcd621634e186af27fa", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[merge]": "cc804bf0c3615e42c948fb5b499e23a9befaec045d23217fe51d48be67eb5933", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[mint_to]": "3395dd949bed8c6ee1d2ee92734fe5937f84ffd29d6586e1eaf97f79a0c323b0", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[mint_to_checked]": "c16c3812385c2e92107ab17e0699a21257511ba885681138bc27ee7756a4ce29", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[mint_to_checked_-_multisig]": "35d5a5f049541e8a0513523ab2ebae4fb8d3c21fe643cb2b3dfe796c2938d228", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[revoke]": "a0434004b50a2be6a15c9967373e1268b809631deaf50d77033d8d22a936ba2a", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[revoke_-_multisig]": "f397d4e9b001f0672d7bdc8bee7f7a35e03de900d7a8a1fa9498f75371c72c33", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_authority]": "35044d89a716f523b88267ee6c2758456caa6c62573f2a19da525a5c061d0ccb", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_authority_-_multisig]": "7647aab302a1d27a9ce4ffb5c2b66e1e8e023194d865269d3d3026dc540d2943", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_lockup_-_with_all_params_set]": "164874de5d767cb0ad7cdd31de6bc678c9e0b43f71de364c9b7526c4d4d1a381", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_lockup_-_with_epoch_only]": "f1c4985080e51dcd6889fc2b2a5ea2502ba44bb4af9fb47656eb2c301395caf5", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_lockup_checked]": "6eb89719cacab2d73b0b91484f6f065e2a3445a24d58f1874702981e6ba45707", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[split]": "6068df82ab79eff5dfdf5e57dc8156e50031ee469934ee63a8d5a38e45aa63ac", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "5a5cc506048b5025bac6cf82ef63d6d81be36fa01cef471eb69f9cd526787f96", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "0312b269438961cac1d5f8d05414f7a318318c390a235402446788fb59ab26a2", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "4613157fbf5d5b48ee47d638a68d814f88bcfa41e88d09e189017fcdeb9fa5f7", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "1f52e2fcf8fc3560890cff045de1b0163c9358705b5f03e3c013a33cb299ace6", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "7721e725a6bc353224b61a51136cedf0a1df0be3f7db52ae0d84a2d4a89c721d", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "c09aa7d4ecfcd61df013cf7eb35927ace16f7efcb72f837366ed99543d353a3f", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "b031063d595906613dcf228833f7c16318e87616e86224e4c29c0d2cacbfd2c0", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "75a9d45b62c0354cbb912f558fa46c7ca484a3a2c3eb95e772e4a16605f85527", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "7c60bfec6f7a48dae7b5ded10d766bcfd644675d2db69fe4af3f02a3cd0cc53f", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "4800260b6cb40ccab96fa8fd4c66991557572dfdaf32750b1e86c95e664aa29f", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "17188e7fcc7ba68fc44059634be7cd2e4a8e8e9c0da0089d42fb1cb65e153af8", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "8845c8f61d4d2fe2d5d68cf8468ae2effdd2f1c4cadad65df1e676066bdb4395", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "6d3c2e590a0b91a39e294a6c0d13490f4c6c541617f7b98e0ba241e7dfc76693", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "f1e96601a1b72ea0a7b0b02d590ba1cdf7c73a9b4b776569a3e2a14632169dcb", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[upgrade_nonce]": "c5046158a24f6ef8ca43a58d3914a3a3da3fcafd94bdcaaf3cd53c77fa37d0e7", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[withdraw]": "9a1e4083290937d22ed9d2a85b757e495b26a8e9fc37cf501b3450bf5217183b", -"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[withdraw_nonce_account]": "25db9577893e79a0823d88eda0b8bfa70ee0125cd574bab912b116f506dbb9c1", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[advance_nonce_account]": "b2295beca694ae44c38ffaa650cf8d4b0340981d147a77f2d94bebf45104f87b", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[allocate]": "f4194cbe9d8b7cec074ff59c24d0aa7da074ce012eea11fdefa7539aba57c48b", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[allocate_with_seed]": "2a7d9211d8923f5fdbd3a778b59ca513c8c0cb6ddeac31ef867a15a899306841", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[approve]": "07de7559328db714a3ad03ad8bf6046c3ee7eb4e6f33aff39b6644c32e5df202", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[approve_checked]": "3df401e5b07e5d2d479f641357d4d65b622e094dda9398be294f3b20519534f6", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[approve_checked_-_multisig]": "28973c67e468ab40c44d03eaf5033bcaed753b3817fff462c81a48df2585b29a", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[assign]": "34c3255f4f30fae89699a9506b209cdadac8721d7ac717592d31ceb94e8337a1", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[assign_with_seed]": "45aab1f0043120498d252347ae66a859fba4f93e5a8368e15387871398853e8a", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize]": "f4e28243243201fd83f4eece1dee004512ce53e5e9fd742538a18dda23875242", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_checked]": "3e5e74f4124f3fb50c37888215364c35e200e2c6632f18e489ef55af5b5f0c31", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_checked_with_seed]": "30e3f7a8a697e83792044f548c72fe0c310f7f3a7b2b5ecfd1ab08efa2e3af51", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_nonce_account]": "fd3c5cb54a7f522311bd7b797cfba1b0ae557507071df3bc13fd5d19a08d3359", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[authorize_with_seed]": "0b46a1ea0a560145be807651c3292211d4587e9b9f19933caec48603eb0ac3dc", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[burn]": "3a76749885a49b79a7911eb6c351d0774654512e58903d3dd37d534662a4f04c", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[burn_checked]": "135c8bae33fa811abe4ba47d64636473b1b9b9ce8ab0553c9ea6615b02d619e4", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[burn_checked_-_multisig]": "d84054917df907a3b284b39b9aa24e53fb698ffb63976994c2b41db6c66d3fdb", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[close_account]": "73e990ae8ef956b3c7b966f59117da0a677b2556a96d9ca960c691330f54f5aa", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[close_account_-_multisig]": "284f00a1526c41dc04e091d8403bcbc80e0f583dc4bbe59c73f586a8732bc22e", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_account]": "5632013881347ff51ee1e6a7e91a865c37d62f53433914b952535e8dabb0db09", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_account_with_seed]": "d9cce0c8db53c19dbabd6d6903f901a100784c2c3ef47261c1bdbf0cb11db2ff", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_associated_token_account]": "f34edac9ec8a0c67a16d015fff4f4fb36d2211c036e0c86c178a35e7ac6055eb", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_associated_token_account_idempotent]": "e45c2eb15949e84c87fada224e0010ce5f6791a67cd0f7803e1131ae564334d4", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_associated_token_account_with_sysvar_ren-cb526e6e": "f34edac9ec8a0c67a16d015fff4f4fb36d2211c036e0c86c178a35e7ac6055eb", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_stake_account]": "fc2d71b8942cbf84c0450bf0fb80ab4b9236047c33e96389048aefe17b7ca675", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_stake_account_with_compute_budget]": "53951cb0476e7d1267f91776e08dace087deb53cd2aecac91ff25f979dd948a9", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[create_token_account_and_transfer_token_-_predefined]": "c14c4700e8e16a6625f1136c9cdae6b88ae067fbe2db43ed2f9ce6ce1e0fa507", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[deactivate]": "261d99c16847f612ff1938899f442f74aaa7bc36dd821546a70f83c85d02db1c", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[delegate]": "b3ab776f7d8a7642d53b74b0cfa39d6a74d569c77299ed4f944f32d3396ae726", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[freeze_account]": "33d845b5fa4496f6480166c689b3b473b8055eaf0348baf4e2422a53e41ecf18", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[freeze_account_-_multisig]": "58027ae2aa133aaf3a65b2d5b10779a700fe9f2a3b304a204a5c32ba4b35e298", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_account]": "780dbad8bef872dc228b8cdb50241655c847128a2dbf333fcd6afc13a4be7fc0", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_checked]": "007a898243694c2cbc16ce12c877c7974cadbc1aef4dc4b82127acfc17fe2c75", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_immutable_owner]": "f9ed879e2e4e39a003adb0c10367793b1f8a78bd0fdeccee7afa3c76d310b0bb", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_multisig]": "0aac1a8b7c8fb2b8e9eb631c81fefab6eafc3845a300d612cf6c134e60b45859", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_nonce_account]": "b24159c7fa711e275fd7cb83d06518f2818f830d3ed249c6d8f5d6ed91cf9ec0", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_stake_account]": "b38a14a3baf24406bf7d13c019ed36a64b323c67d40426b9bb2bbf7aba287a26", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_token_account_2]": "cd2788f21ab619f3601e3b85bf601f1dc64d8f1cd00ad459c05db06ca05c1afd", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[initialize_token_account_3]": "c15e702b9386a962fd6d4a8403706760b0bc0649bc3280eb50caad2719949c3e", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[lookup_tables]": "46590c9719f50421e89bf3af33a4b03ff767339b15dcac117e99a53de20f2f9f", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[memo]": "aa1395f9ef9d7b4dddcb9e0582ec84003588fa2d77c3a41e48134e32658130cf", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[memo_legacy]": "44b9f45e3362e871940d9feeaf58f382162b212b24d9d02f5f023d551c26639d", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[merge]": "360dc6732f1a083944bfcbc7c6d2dcabd7391bdbf32a99ba300c0db6b53e3a64", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[mint_to]": "aa4d49fed36e901a50958a74f9475810dc8799a393caedb9e6fbc0957386a53a", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[mint_to_checked]": "9066da90b52f6a3860d3a7738add41e58f71635544d6682f2aca16c3259facaf", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[mint_to_checked_-_multisig]": "8c249bcf3c2975e18b18d59ebecba2f20c6930e01e93e73a82bbab60b21a970c", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[revoke]": "0101ed45f4b46dade6139cde7b644eab139a862cd365ca7127e5409fc9c48779", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[revoke_-_multisig]": "2f33ae80e104e2ec8748fb5f054653efc2ee44091a34dcf342b78446d6ee25a8", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_authority]": "3d54ce96b042a5f3d8065a84f9633d31963613269d02c99bab3bfa99f3283a60", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_authority_-_multisig]": "3afd1d164ca4ba5134d54bb95bba19415c2ec078084f74d3012103157cc5937a", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_lockup_-_with_all_params_set]": "6828c2f8ccb735be5dbba8f679cab7b8d6b2496d85d69264abb04f88fb5664e8", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_lockup_-_with_epoch_only]": "66586d8c9488c801f869f9ce172211bcede00ec08ace27c36c02f0ad3be60f88", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[set_lockup_checked]": "85fbeaf6fa4c0d97288a3dfd583d8c0363dbb5964c389898c968e108eedc0128", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[split]": "9afd37f3fb07de403ba7f8455991e2066ce9263fac3845d1dff0b10a2255e202", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "9aaa21c4f81837543cd2eabc0cdbf865743986dec20d6fbce674e171e41a7acc", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "baf87c04c02e788c38de9f91a642391c08df742bd410a0597bae5ccb4587b866", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "43445c484a0b7753e3c64a527e8eae9e0c58a2f0adbf3d96caf0f1ed4fe2a8bd", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "a76a703c4f134a5c2185357871660a87e0d91b896d7c6a54d9b5a07e0702264a", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "619f5b866b57d212904c8f0d0d5e1b9dd641573135484ca79e3a05f9fae1b71d", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "195151d919ea902a50f870e2a22cd83826e7f3c69a60e00c68e652786f8c45b2", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "9f262f39f42ffb9cbc31ed0238da71e0cd95d876eee0bebdf48c5df54c2ddfe9", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "445b0a6c7510fbfd77603dfcd856fe213ffda035e708af37d399af27474b7cb8", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined_-_with_definitions]": "f798a751885bc0f045e00980b8ce6bd3c65bb0d25e8486b5c52091f2ef3e4daf", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "90837fa678ed8c00c7aae1010fed97cb64705338876962959d29acc8c280227b", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "081b43ea8e46e7be2e7d74f404b938ccca6a65bb79acff6ad2e286ddb5bcecec", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "5b6a303c22f86d039c215dc9da3cd3b5f654e80e2e8a4bbde32c82fdee8f2d43", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "b9929e2238d22a278bdf654b5b6ce5e2052bc054a2e7660e8922a859c8c60c3f", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "eaaa1acc4628224060e8048e08a0d611b352f6ed35cefe85a5e97f2351f14b7e", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "0f9c99e73b823a509d3de3506f442a2749f307c4170099aa7093bedf3d875bb2", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[upgrade_nonce]": "13c96fb808a3ca7b382dd49c023877f8d1cb1a96e263972fef0b95f55a127759", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[withdraw]": "b55675dd1e99518569697edb830fc751e07a8096ba0ad1f99988daab318dc9a0", +"T2T1_en_solana-test_sign_tx.py::test_solana_sign_tx[withdraw_nonce_account]": "cf7f277976788372991667dcda367f426a041eeaeef8a097c975f0d66d46675e", "T2T1_en_stellar-test_stellar.py::test_get_address[parameters0-result0]": "b1fa93cbf05638472cfb4c343e069267ffd82029391f807fe371fdb960bdcf2d", "T2T1_en_stellar-test_stellar.py::test_get_address[parameters1-result1]": "bc73800840c47711069a3cfc2ef090170a03ec4c0d32312a25260cce435c75e1", "T2T1_en_stellar-test_stellar.py::test_get_address[parameters2-result2]": "bea863256f648c50a010bede5f500e1a433deb4e2001d60ff48655b02cc7a623",