From 66c5d5407a07082f879ad25841eb195709d25fe2 Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Mon, 6 Nov 2023 18:12:06 +0100 Subject: [PATCH 1/2] QOL changes to eth plugin interface --- src/eth_plugin_interface.h | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/eth_plugin_interface.h b/src/eth_plugin_interface.h index 5bf54992a..9832ee814 100644 --- a/src/eth_plugin_interface.h +++ b/src/eth_plugin_interface.h @@ -8,16 +8,16 @@ #include "shared_context.h" // Interface version. To be updated everytime we introduce breaking changes to the plugin interface. -typedef enum { +typedef enum eth_plugin_interface_version_e { ETH_PLUGIN_INTERFACE_VERSION_1 = 1, ETH_PLUGIN_INTERFACE_VERSION_2 = 2, ETH_PLUGIN_INTERFACE_VERSION_3 = 3, ETH_PLUGIN_INTERFACE_VERSION_4 = 4, ETH_PLUGIN_INTERFACE_VERSION_5 = 5, - ETH_PLUGIN_INTERFACE_VERSION_LATEST = 6 + ETH_PLUGIN_INTERFACE_VERSION_LATEST = 6, } eth_plugin_interface_version_t; -typedef enum { +typedef enum eth_plugin_msg_e { ETH_PLUGIN_INIT_CONTRACT = 0x0101, ETH_PLUGIN_PROVIDE_PARAMETER = 0x0102, @@ -25,11 +25,11 @@ typedef enum { ETH_PLUGIN_PROVIDE_INFO = 0x0104, ETH_PLUGIN_QUERY_CONTRACT_ID = 0x0105, ETH_PLUGIN_QUERY_CONTRACT_UI = 0x0106, - ETH_PLUGIN_CHECK_PRESENCE = 0x01FF + ETH_PLUGIN_CHECK_PRESENCE = 0x01FF, } eth_plugin_msg_t; -typedef enum { +typedef enum eth_plugin_result_e { // Unsuccesful return values ETH_PLUGIN_RESULT_ERROR = 0x00, ETH_PLUGIN_RESULT_UNAVAILABLE = 0x01, @@ -39,14 +39,14 @@ typedef enum { ETH_PLUGIN_RESULT_SUCCESSFUL = 0x03, // Used for comparison ETH_PLUGIN_RESULT_OK = 0x04, ETH_PLUGIN_RESULT_OK_ALIAS = 0x05, - ETH_PLUGIN_RESULT_FALLBACK = 0x06 + ETH_PLUGIN_RESULT_FALLBACK = 0x06, } eth_plugin_result_t; -typedef enum { +typedef enum eth_ui_type_e { ETH_UI_TYPE_AMOUNT_ADDRESS = 0x01, - ETH_UI_TYPE_GENERIC = 0x02 + ETH_UI_TYPE_GENERIC = 0x02, } eth_ui_type_t; @@ -54,21 +54,21 @@ typedef void (*PluginCall)(int, void *); // Shared objects, read-write -typedef struct ethPluginSharedRW_t { +typedef struct ethPluginSharedRW_s { cx_sha3_t *sha3; } ethPluginSharedRW_t; // Shared objects, read-only -typedef struct ethPluginSharedRO_t { +typedef struct ethPluginSharedRO_s { txContent_t *txContent; } ethPluginSharedRO_t; // Init Contract -typedef struct ethPluginInitContract_t { +typedef struct ethPluginInitContract_s { uint8_t interfaceVersion; uint8_t result; @@ -86,7 +86,7 @@ typedef struct ethPluginInitContract_t { // Provide parameter -typedef struct ethPluginProvideParameter_t { +typedef struct ethPluginProvideParameter_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; uint8_t *pluginContext; @@ -99,7 +99,7 @@ typedef struct ethPluginProvideParameter_t { // Finalize -typedef struct ethPluginFinalize_t { +typedef struct ethPluginFinalize_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; uint8_t *pluginContext; @@ -127,7 +127,7 @@ typedef struct ethPluginFinalize_t { // Provide token -typedef struct ethPluginProvideInfo_t { +typedef struct ethPluginProvideInfo_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; uint8_t *pluginContext; @@ -146,7 +146,7 @@ typedef struct ethPluginProvideInfo_t { // This is always called on the non aliased contract -typedef struct ethQueryContractID_t { +typedef struct ethQueryContractID_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; uint8_t *pluginContext; @@ -162,7 +162,7 @@ typedef struct ethQueryContractID_t { // Query Contract UI -typedef struct ethQueryContractUI_t { +typedef struct ethQueryContractUI_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; union extraInfo_t *item1; @@ -180,4 +180,9 @@ typedef struct ethQueryContractUI_t { } ethQueryContractUI_t; +// Helper to check that the plugin context structure is not bigger than 5 * 32 +#define PLUGIN_CONTEXT_SIZE (5 * INT256_LENGTH) +#define ASSERT_SIZEOF_PLUGIN_CONTEXT(s) \ + _Static_assert(sizeof(s) <= PLUGIN_CONTEXT_SIZE, "Plugin context structure is too big.") + #endif // _ETH_PLUGIN_INTERFACE_H_ From 41ef631d63775e05dcbdbea336db98ea11fbe7af Mon Sep 17 00:00:00 2001 From: Francois Beutin Date: Mon, 6 Nov 2023 19:24:25 +0100 Subject: [PATCH 2/2] Improve plugin interface file --- src/eth_plugin_interface.h | 111 ++++++++++++++++++++++++------------- src/eth_plugin_internal.h | 1 + 2 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/eth_plugin_interface.h b/src/eth_plugin_interface.h index 9832ee814..fc8f23978 100644 --- a/src/eth_plugin_interface.h +++ b/src/eth_plugin_interface.h @@ -1,3 +1,5 @@ +// clang-format off + #ifndef _ETH_PLUGIN_INTERFACE_H_ #define _ETH_PLUGIN_INTERFACE_H_ @@ -7,7 +9,14 @@ #include "tokens.h" #include "shared_context.h" -// Interface version. To be updated everytime we introduce breaking changes to the plugin interface. +/************************************************************************************************* + * Comments provided in this file are quick reminders on the usage of the plugin interface * + * Reading the real plugin documentation is GREATLY recommended. * + * You can find the latest version here: * + * https://github.com/LedgerHQ/app-ethereum/blob/develop/doc/ethapp_plugins.adoc * + *************************************************************************************************/ + +// Interface version. Will be updated every time a breaking change in the interface is introduced. typedef enum eth_plugin_interface_version_e { ETH_PLUGIN_INTERFACE_VERSION_1 = 1, ETH_PLUGIN_INTERFACE_VERSION_2 = 2, @@ -17,20 +26,26 @@ typedef enum eth_plugin_interface_version_e { ETH_PLUGIN_INTERFACE_VERSION_LATEST = 6, } eth_plugin_interface_version_t; -typedef enum eth_plugin_msg_e { +// Codes for the different requests Ethereum can send to the plugin +// The dispatch is handled by the SDK itself, the plugin code does not have to handle it +typedef enum eth_plugin_msg_e { + // Codes for actions the Ethereum app can ask the plugin to perform ETH_PLUGIN_INIT_CONTRACT = 0x0101, ETH_PLUGIN_PROVIDE_PARAMETER = 0x0102, ETH_PLUGIN_FINALIZE = 0x0103, ETH_PLUGIN_PROVIDE_INFO = 0x0104, ETH_PLUGIN_QUERY_CONTRACT_ID = 0x0105, ETH_PLUGIN_QUERY_CONTRACT_UI = 0x0106, - ETH_PLUGIN_CHECK_PRESENCE = 0x01FF, + // Special request: the Ethereum app is checking if we are installed on the device + ETH_PLUGIN_CHECK_PRESENCE = 0x01FF, } eth_plugin_msg_t; + +// Reply codes when responding to the Ethereum application typedef enum eth_plugin_result_e { - // Unsuccesful return values + // Unsuccessful return values ETH_PLUGIN_RESULT_ERROR = 0x00, ETH_PLUGIN_RESULT_UNAVAILABLE = 0x01, ETH_PLUGIN_RESULT_UNSUCCESSFUL = 0x02, // Used for comparison @@ -40,37 +55,57 @@ typedef enum eth_plugin_result_e { ETH_PLUGIN_RESULT_OK = 0x04, ETH_PLUGIN_RESULT_OK_ALIAS = 0x05, ETH_PLUGIN_RESULT_FALLBACK = 0x06, - } eth_plugin_result_t; -typedef enum eth_ui_type_e { +// Format of UI the Ethereum application has to use for this plugin +typedef enum eth_ui_type_e { + // If uiType is UI_AMOUNT_ADDRESS, Ethereum will use the amount/address UI + // the amount and address provided by the plugin will be used + // If tokenLookup1 is set, the amount is provided for this token ETH_UI_TYPE_AMOUNT_ADDRESS = 0x01, - ETH_UI_TYPE_GENERIC = 0x02, + // If uiType is UI_TYPE_GENERIC, Ethereum will use the dedicated ETH plugin UI + // the ETH application provides tokens if requested then prompts for each UI field + // The first field is forced by the ETH app to be the name + version of the plugin handling the + // request. The last field is the fee amount + ETH_UI_TYPE_GENERIC = 0x02, } eth_ui_type_t; -typedef void (*PluginCall)(int, void *); - -// Shared objects, read-write +// Scratch objects and utilities available to the plugin READ-WRITE typedef struct ethPluginSharedRW_s { cx_sha3_t *sha3; - } ethPluginSharedRW_t; -// Shared objects, read-only +// Transaction data available to the plugin READ-ONLY typedef struct ethPluginSharedRO_s { txContent_t *txContent; - } ethPluginSharedRO_t; + +// Plugin-only memory allocated by the Ethereum application and used by the plugin. +#define PLUGIN_CONTEXT_SIZE (5 * INT256_LENGTH) +// It is recommended to cast the raw uin8_t array to a structure meaningfull for your plugin +// Helper to check that the actual plugin context structure is not bigger than the allocated memory +#define ASSERT_SIZEOF_PLUGIN_CONTEXT(s) \ + _Static_assert(sizeof(s) <= PLUGIN_CONTEXT_SIZE, "Plugin context structure is too big.") + + +/* + * HANDLERS AND PARAMETERS + * Parameters associated with the requests the Ethereum application can ask the plugin to perform + * The plugin SDK will automatically call the relevant handler for the received code, so the plugin + * has to define each of the handler functions declared below. + */ + + // Init Contract typedef struct ethPluginInitContract_s { - uint8_t interfaceVersion; - uint8_t result; + eth_plugin_interface_version_t interfaceVersion; + eth_plugin_result_t result; // in ethPluginSharedRW_t *pluginSharedRW; @@ -83,26 +118,30 @@ typedef struct ethPluginInitContract_s { char *alias; // 29 bytes alias if ETH_PLUGIN_RESULT_OK_ALIAS set } ethPluginInitContract_t; +// void handle_init_contract(ethPluginInitContract_t *parameters); + // Provide parameter typedef struct ethPluginProvideParameter_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; + uint8_t *pluginContext; // PLUGIN_CONTEXT_SIZE const uint8_t *parameter; // 32 bytes parameter uint32_t parameterOffset; - uint8_t result; + eth_plugin_result_t result; } ethPluginProvideParameter_t; +// void handle_provide_parameter(ethPluginProvideParameter_t *parameters); + // Finalize typedef struct ethPluginFinalize_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; + uint8_t *pluginContext; // PLUGIN_CONTEXT_SIZE uint8_t *tokenLookup1; // set by the plugin if a token should be looked up uint8_t *tokenLookup2; @@ -111,26 +150,20 @@ typedef struct ethPluginFinalize_s { const uint8_t *address; // set to the destination address if uiType is UI_AMOUNT_ADDRESS. Set // to the user's address if uiType is UI_TYPE_GENERIC - uint8_t uiType; + eth_ui_type_t uiType; uint8_t numScreens; // ignored if uiType is UI_AMOUNT_ADDRESS - uint8_t result; + eth_plugin_result_t result; } ethPluginFinalize_t; +// void handle_finalize(ethPluginFinalize_t *parameters); -// If uiType is UI_AMOUNT_ADDRESS, the amount and address provided by the plugin will be used -// If tokenLookup1 is set, the amount is provided for this token - -// if uiType is UI_TYPE_GENERIC, the ETH application provides tokens if requested then prompts -// for each UI field -// The first field is forced by the ETH app to be the name + version of the plugin handling the -// request The last field is the fee amount // Provide token typedef struct ethPluginProvideInfo_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; + uint8_t *pluginContext; // PLUGIN_CONTEXT_SIZE union extraInfo_t *item1; // set by the ETH application, to be saved by the plugin union extraInfo_t *item2; @@ -138,9 +171,11 @@ typedef struct ethPluginProvideInfo_s { uint8_t additionalScreens; // Used by the plugin if it needs to display additional screens // based on the information received from the token definitions. - uint8_t result; + eth_plugin_result_t result; } ethPluginProvideInfo_t; +// void handle_provide_token(ethPluginProvideInfo_t *parameters); + // Query Contract name and version @@ -149,16 +184,18 @@ typedef struct ethPluginProvideInfo_s { typedef struct ethQueryContractID_s { ethPluginSharedRW_t *pluginSharedRW; ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; + uint8_t *pluginContext; // PLUGIN_CONTEXT_SIZE char *name; size_t nameLength; char *version; size_t versionLength; - uint8_t result; + eth_plugin_result_t result; } ethQueryContractID_t; +// void handle_query_contract_id(ethQueryContractID_t *parameters); + // Query Contract UI @@ -168,7 +205,7 @@ typedef struct ethQueryContractUI_s { union extraInfo_t *item1; union extraInfo_t *item2; char network_ticker[MAX_TICKER_LEN]; - uint8_t *pluginContext; + uint8_t *pluginContext; // PLUGIN_CONTEXT_SIZE uint8_t screenIndex; char *title; @@ -176,13 +213,11 @@ typedef struct ethQueryContractUI_s { char *msg; size_t msgLength; - uint8_t result; + eth_plugin_result_t result; } ethQueryContractUI_t; - -// Helper to check that the plugin context structure is not bigger than 5 * 32 -#define PLUGIN_CONTEXT_SIZE (5 * INT256_LENGTH) -#define ASSERT_SIZEOF_PLUGIN_CONTEXT(s) \ - _Static_assert(sizeof(s) <= PLUGIN_CONTEXT_SIZE, "Plugin context structure is too big.") +// void handle_query_contract_ui(ethQueryContractUI_t *parameters); #endif // _ETH_PLUGIN_INTERFACE_H_ + +// clang-format on diff --git a/src/eth_plugin_internal.h b/src/eth_plugin_internal.h index 2d2ca0369..956cec4b8 100644 --- a/src/eth_plugin_internal.h +++ b/src/eth_plugin_internal.h @@ -21,6 +21,7 @@ bool U2BE_from_parameter(const uint8_t* parameter, uint16_t* value); bool U4BE_from_parameter(const uint8_t* parameter, uint32_t* value); typedef bool (*PluginAvailableCheck)(void); +typedef void (*PluginCall)(int, void *); typedef struct internalEthPlugin_t { PluginAvailableCheck availableCheck;