From a5dde47b90c59fa0a044d69d37b4d1947fe5207d Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 13 Feb 2024 15:29:25 +0100 Subject: [PATCH] Update doc --- doc/eth_contract_support_embedded.adoc | 59 +-- doc/eth_starkware_extensions.adoc | 348 -------------- doc/ethapp_plugins.adoc | 628 ++++++++++++------------- 3 files changed, 318 insertions(+), 717 deletions(-) delete mode 100644 doc/eth_starkware_extensions.adoc diff --git a/doc/eth_contract_support_embedded.adoc b/doc/eth_contract_support_embedded.adoc index 67c27c0..36a582c 100644 --- a/doc/eth_contract_support_embedded.adoc +++ b/doc/eth_contract_support_embedded.adoc @@ -6,24 +6,24 @@ Application version 1.3.0 - 05th of July 2020 ## 1.3.0 - Initial release -## About +## About This document described how a specific device UI for a smart contract can be added in the current version of the Ethereum application, before plugins are added ## Standard support -The applications already includes dedicated UI support for those specific contract calls : +The applications already includes dedicated UI support for those specific contract calls : * ERC 20 approve(address, uint256) - implementation in *src_features/erc20_approval* * ERC 20 transfer(address, uint256) - implementation in *src_features/signTx* ## Requirements -The following data is necessary for a specific contract support +The following data is necessary for a specific contract support * Smart contract ABI (at least for calls that are to be supported by the application) -The following data is optional for a specific contract support +The following data is optional for a specific contract support * Contract address (can be optional if supported by multiple instances) @@ -38,60 +38,11 @@ The first four bytes of the call data for a function call specifies the function The following online tool can be used to compute selectors https://emn178.github.io/online-tools/keccak_256.html -## Limitations - - * The total number of parameters of a contract call is 4 (as defined in *src/shared_context.h* for tokenContext_t.data, each parameter being encoded as an uint256). Additional parameters can be supported if not required to handle the displaying logic (see "Also handle exception that only need to process the beginning of data" in *src_features/signTx/logic_signTx.c* for CONTRACT_STARKWARE_VERIFY_ESCAPE and CONTRACT_STARKWARE_REGISTER) - - * Non fixed size types (dynamic sized byte sequences, dynamic sized strings, variable length arrays) are not directly supported - -## Sample implementation of a function call - -This example describes how to implement a specific UI for a contract call. This exemple is using Starkex token deposit - *deposit(uint256 tokenId, uint256 vaultId, uint256 quantizedAmount)* - - - * Implement the specific UI logic in a new *src_features* subdirectory - note that strings.common.maxFee will be provisioned with the maximum fee to pay for this call - -``` -See src_features/stark_contract_deposit for this example -``` - - * Compute the selector - - -``` -keccak-256("deposit(uint256,uint256,uint256)") = 00aeef8a... -``` - - * Add an entry to the contract_call_t enum of *src/shared_context.h* for this function call - -``` -CONTRACT_STARKWARE_DEPOSIT_TOKEN -``` - - * Check for the selector being called and the arguments size in *src_features/signTx/logic_signTx.c* customProcessor function - additional logic can be applied (quantumSet in this example) - -```C -if ((context->currentFieldLength == STARKWARE_DEPOSIT_TOKEN_DATA_SIZE) && - (memcmp(context->workBuffer, STARKWARE_DEPOSIT_TOKEN_ID, 4) == 0) && - quantumSet) { - contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_TOKEN; -} -``` - - * Call the dedicated UI at the end of the transaction parsing in *src_features/signTx/logic_signTx.c* finalizeParsing function - -```C -if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_TOKEN) { - ux_flow_init(0, ux_approval_starkware_deposit_flow, NULL); - return; -} -``` - ## Using ERC 20 token tickers A UI implementation might want to convert an ERC 20 token contract address to a ticker for easier validation -2 tickers can be temporarily provisioned to the application by using the PROVIDE ERC 20 TOKEN INFORMATION APDU, described in *src_features/provideErc20TokenInformation* - the UI can then iterate on the provisioned tickers to display relevant information to the user +2 tickers can be temporarily provisioned to the application by using the PROVIDE ERC 20 TOKEN INFORMATION APDU, described in *src_features/provideErc20TokenInformation* - the UI can then iterate on the provisioned tickers to display relevant information to the user The same mechanism will be extended to support well known contract addresses in the future diff --git a/doc/eth_starkware_extensions.adoc b/doc/eth_starkware_extensions.adoc deleted file mode 100644 index 48e91fd..0000000 --- a/doc/eth_starkware_extensions.adoc +++ /dev/null @@ -1,348 +0,0 @@ -Ethereum application : Starkware extensions -============================================ -Ledger Firmware Team -Application version 1.5.0 - 4th of October 2020 - -## 1.3.0 - - Initial release - -## 1.5.0 - - Update with Starkex v2 APIs - -## About - -This specification describes the APDU messages interface implementing the Starkware extensions for the Ethereum appilcation - -## Modified general purpose APDUs - -### GET APP CONFIGURATION - -#### Description - -This command returns specific application configuration - -It is modified to notify Stark extensions support on flag 0x04 - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 06 | 00 | 00 | 00 | 04 -|============================================================================================================================== - -'Input data' - -None - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Flags - 0x01 : arbitrary data signature enabled by user - - 0x02 : ERC 20 Token information needs to be provided externally - - 0x04 : Stark extensions are supported - - 0x08 : Stark protocol v2 is supported - | 01 -| Application major version | 01 -| Application minor version | 01 -| Application patch version | 01 -|============================================================================================================================== - - -## Additional APDUs - -Additional APDUs use the APDU CLA F0 - -### GET STARK PUBLIC KEY - -#### Description - -This command returns the public Stark key (X and Y coordinates) for the given BIP 32 path. - -The key can be optionally checked on the device before being returned - in that case, only the X coordinate is displayed, as this is what is used in the contract - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| F0 | 02 | 00 : return address - - 01 : display address and confirm before returning - | 00 | variable | variable -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -|============================================================================================================================== - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Stark key | 65 -|============================================================================================================================== - -### SIGN STARK MESSAGE - -#### Description - -This command signs an order or a transfer on the Starkware curve. - -The contract addressed associated to the token shall have be provisioned previously with the PROVIDE ERC 20 TOKEN INFORMATION command or this command will fail. - -The quantum type for v2 messages is encoded as p1 for the PROVIDE QUANTUM command - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| F0 | 04 | - 01 : sign a Stark Order (protocol v1, handles ETH and regular ERC 20) - - 02 : sign a Stark Transfer (protocol v1, handles ETH and regular ERC 20) - - 03 : sign a Stark Order (since protocol v2) - - 04 : sign a Stark Transfer (since protocol v2) - - 05 : sign a Stark Conditional Transfer (since protocol v2) - - | 00 | variable | variable -|============================================================================================================================== - -'Input data for a Stark Order (v1)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Contract address of the token to be sold (or 00..00 for ETH) | 20 -| Quantization of the token to be sold (big endian) | 32 -| Contract address of the token to be bought (or 00..00 for ETH) | 20 -| Quantization of the token to be bought (big endian) | 32 -| ID of the source vault (big endian encoded) | 4 -| ID of the destination vault (big endian encoded) | 4 -| Amount to be sold (big endian encoded) | 8 -| Amount to buy (big endian encoded) | 8 -| Transaction nonce (big endian encoded) | 4 -| Transaction timestamp (big endian encoded) | 4 -|============================================================================================================================== - -'Input data for a Stark Transfer (v1)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Contract address of the token to be transferred (or 00..00 for ETH) | 20 -| Quantization of the token to be transferred (big endian) | 32 -| Token target public key | 32 -| ID of the source vault (big endian encoded) | 4 -| ID of the destination vault (big endian encoded) | 4 -| Amount to be transferred (big endian encoded) | 8 -| Transaction nonce (big endian encoded) | 4 -| Transaction timestamp (big endian encoded) | 4 -|============================================================================================================================== - -'Input data for a Stark Order (v2)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Quantization type of the token to be sold | 1 -| Contract address of the token to be sold (or 00..00 for ETH) | 20 -| Quantization or Token ID of the token to be sold (big endian) | 32 -| Minting blob of the token to be sold (ignored if non mintable) | 32 -| Quantization type of the token to be bought | 1 -| Contract address of the token to be bought (or 00..00 for ETH) | 20 -| Quantization or Token ID of the token to be bought (big endian) | 32 -| Minting blob of the token to be bought (ignored if non mintable) | 32 -| ID of the source vault (big endian encoded) | 4 -| ID of the destination vault (big endian encoded) | 4 -| Amount to be sold (big endian encoded) | 8 -| Amount to buy (big endian encoded) | 8 -| Transaction nonce (big endian encoded) | 4 -| Transaction timestamp (big endian encoded) | 4 -|============================================================================================================================== - -'Input data for a Stark Transfer (v2)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Quantization type of the token to be transferred | 1 -| Contract address of the token to be transferred (or 00..00 for ETH) | 20 -| Quantization or Token ID of the token to be transferred (big endian) | 32 -| Minting blob of the token to be transferred (ignored if non mintable) | 32 -| Token target public key | 32 -| ID of the source vault (big endian encoded) | 4 -| ID of the destination vault (big endian encoded) | 4 -| Amount to be transferred (big endian encoded) | 8 -| Transaction nonce (big endian encoded) | 4 -| Transaction timestamp (big endian encoded) | 4 -|============================================================================================================================== - -'Input data for a Stark Conditional Transfer' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Quantization type of the token to be transferred | 1 -| Contract address of the token to be transferred (or 00..00 for ETH) | 20 -| Quantization or Token ID of the token to be transferred (big endian) | 32 -| Minting blob of the token to be transferred (ignored if non mintable) | 32 -| Token target public key | 32 -| ID of the source vault (big endian encoded) | 4 -| ID of the destination vault (big endian encoded) | 4 -| Amount to be transferred (big endian encoded) | 8 -| Transaction nonce (big endian encoded) | 4 -| Transaction timestamp (big endian encoded) | 4 -| Conditional transfer fact (big endian) | 32 -| Conditional transfer L1 condition logic address | 20 -|============================================================================================================================== - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| RFU (00) | 1 -| r | 32 -| s | 32 -|============================================================================================================================== - - -### PROVIDES QUANTUM - -#### Description - -This command provides quantization data used to compute a tokenId and provide additional information to the user before signing a transaction performing a deposit or withdrawal call on a Stark powered smart contract. - -It shall be called following a PROVIDE ERC 20 TOKEN INFORMATION command called for the associated contract - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| F0 | 08 | - 00 : legacy (protocol v1, handles ETH and regular ERC 20) - - 01 : quantum encoded for ETH (since protocol v2) - - 02 : quantum encoded for a regular ERC 20 (since protocol v2) - - 03 : quantum encoded for a regular ERC 721 (since protocol v2) - - 04 : quantum encoded for a mintable ERC 20 (since protocol v2) - - 05 : quantum encoded for a mintable ERC 721 (since protocol v2) - - | 00 | variable | variable -|============================================================================================================================== - -'Legacy Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Contract address used in the next transaction | 20 -| Quantization to be used in the next transaction | 32 -|============================================================================================================================== - -'v2 Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Contract address used in the next transaction (ignored for ETH) | 20 -| Quantization to be used in the next transaction (ignored for ERC 721s) | 32 -| Minting blob to be used in the next transaction (ignored for non mintable) | 32 -|============================================================================================================================== - -'Output data' - -None - -### UNSAFE SIGN - -#### Description - -This command signs an arbitrary hash on the Starkware Curve after presenting the hash to the user. It is intended for speed of execution in case an unknown Stark model is pushed and should be avoided as much as possible. - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| F0 | 0A | - 00 - | 00 | variable | variable -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Hash to sign | 32 -|============================================================================================================================== - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| RFU (00) | 1 -| r | 32 -| s | 32 -|============================================================================================================================== - diff --git a/doc/ethapp_plugins.adoc b/doc/ethapp_plugins.adoc index a3bdbbe..886928a 100644 --- a/doc/ethapp_plugins.adoc +++ b/doc/ethapp_plugins.adoc @@ -1,315 +1,313 @@ -Ethereum application Plugins : Technical Specifications -======================================================= -Ledger Firmware Team -Specification version 1.0 - 24th of September 2020 - - -## 1.0 - - Initial release - -## About - -This specification describes the plugin interface used to display a specific UI on device for Ethereum smart contracts. - -Feel free to checkout the ParaSwap plugin to see an actual implementation. Link: https://github.com/LedgerHQ/app-ethereum/blob/named-external-plugins/doc/ethapp_plugins.asc . - -## Flow overview - -When signing an Ethereum transaction containing data, the Ethereum application looks for a plugin using .either a selector list or the contract address. - -If a plugin is found, each network serialized data field (32 bytes) is passed to the plugin along with the field offset. The plugin can decide to stop the signature process if a data field isn't expected - -After all fields have been received, the plugin can report to the Ethereum application whether the full data is accepted, and the user interface model that'll be used to display the data - -### Amount/Address user interface - -In this model, the generic (without data) transaction display is used, with the amount and destination address replaced by data provided by the plugin - -### Generic user interface - -In this model, the plugin first reports a number of screens (2 lines of text, the second line being scrollable) to be displayed - -The Ethereum application will request each screen to be displayed to the plugin and let the user browse through them. - -The first screen being displayed is always a description of the plugin being used (name and version reported by the plugin), and the last screens include the transaction fees in ETH and a confirmation prompt - -### Code flow - -The plugin interfacing logic is described in _src/eth_plugin_interface.h_ - -The plugin common dispatcher is found in _src/eth_plugin_handler.c_ - -The plugin generic UI dispatcher is found in _src/eth_plugin_ui.c_ - -Sample internal plugins are provided in _src_plugins/_ - -## Creating a plugin - -### Creating an internal plugin - -Internal plugins are triggered on specific selectors. You can modify _src/eth_plugin_internal.c_ to add your mapping. - -Other specific mappings can be also added by modifying the common dispatcher - -### Creating an external plugin - -An external plugin is a library application named after the base64 encoding of the 20 bytes smart contract address - -## Detailed flow messages - -### Generic fields - -The following generic fields are present in all messages : - - * pluginSharedRW : scratch objects and utilities available to the plugin (can be read and written) - - * pluginSharedRO : transaction data available to the plugin (can only be read) - - * pluginContext : arbitrary data blob holding the plugin context, to be set and used by the plugin - - * result : return code set by the plugin following the message processing - -### ETH_PLUGIN_INIT_CONTRACT - -[source,C] ----- - -typedef struct ethPluginInitContract_t { - - // in - - ethPluginSharedRW_t *pluginSharedRW; - ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; - uint32_t pluginContextLength; - uint8_t *selector; // 4 bytes selector - uint32_t dataSize; - - char *alias; // 29 bytes alias if ETH_PLUGIN_RESULT_OK_ALIAS set - - uint8_t result; - -} ethPluginInitContract_t; - ----- - -This message is sent when the selector of the data has been parsed. The following specific fields are filled when the plugin is called : - - * pluginContextLength : length of the data field available to store the plugin context - * selector : 4 bytes selector of the data field - * dataSize : size in bytes of the data field - -The following return codes are expected, any other will abort the signing process : - - * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized - * ETH_PLUGIN_RESULT_OK_ALIAS : if a base64 encoded alias of another plugin to call is copied to the _alias_ field. In this case, the dispatcher will follow the alias chain, and the original plugin will only be called to retrieve its name when using a generic user interface - * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one - -### ETH_PLUGIN_PROVIDE_PARAMETER - -[source,C] ----- - -typedef struct ethPluginProvideParameter_t { - - ethPluginSharedRW_t *pluginSharedRW; - ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; - uint8_t *parameter; // 32 bytes parameter - uint32_t parameterOffset; - - uint8_t result; - -} ethPluginProvideParameter_t; - ----- - -This message is sent when a new 32 bytes component of the data field is available. The following specific fields are filled when the plugin is called : - - * parameter : pointer to the 32 bytes parameter being parsed - * parameterOffset : offset to this parameter from the beginning of the data field (starts at 4, following the selector) - -The following return codes are expected, any other will abort the signing process : - - * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized - * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one - -There are already defined functions to extract data from a parameter: -[source,C] ----- -void copy_address(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size); -void copy_parameter(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size); - -// Get the value from the beginning of the parameter (right to left) and check if the rest of it is zero -bool U2BE_from_parameter(const uint8_t* parameter, uint16_t* value); -bool U4BE_from_parameter(const uint8_t* parameter, uint32_t* value); ----- - -### ETH_PLUGIN_FINALIZE - -[source,C] ----- - -typedef struct ethPluginFinalize_t { - - ethPluginSharedRW_t *pluginSharedRW; - ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; - - uint8_t *itemLookup1; // set by the plugin if a token or an nft should be looked up - uint8_t *itemLookup2; - - uint8_t *amount; // set an uint256 pointer if uiType is UI_AMOUNT_ADDRESS - 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; - uint8_t numScreens; // ignored if uiType is UI_AMOUNT_ADDRESS - uint8_t result; - -} ethPluginFinalize_t; - ----- - -This message is sent when the data field has been fully parsed. The following specific fields can be filled by the plugin : - - * itemLookup1 : the pointer shall be set to a 20 bytes address to look up an ERC20 token or NFT if needed by the plugin - * itemLookup2 : the pointer shall be set to a 20 bytes address to look up an ERC20 token or NFT if needed by the plugin - * uiType : set to either ETH_UI_TYPE_AMOUNT_ADDRESS for an amount/address UI or ETH_UI_TYPE_GENERIC for a generic UI - -The following specific fields are filled by the plugin when returning an amount/address UI : - - * amount : set to a pointer to a 256 bits number - * address : set to a pointer to a 20 bytes address - -The following specific fields are filled by the plugin when returning a generic UI : - - * numScreens : number of screens handled by the plugin - -The following return codes are expected, any other will abort the signing process : - - * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized - * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one - -### ETH_PLUGIN_PROVIDE_INFO - -[source,C] ----- - -typedef struct ethPluginProvideInfo_t { - - ethPluginSharedRW_t *pluginSharedRW; - ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; - - union extraInfo *item1; // set by the ETH application, to be saved by the plugin - union extraInfo *item2; - - uint8_t additionalScreens; // Used by the plugin if it needs to display additional screens based on the information received. - - uint8_t result; - -} ethPluginProvideInfo_t; - ----- - -This message is sent if an information lookup was required by the plugin when parsing a finalize message. The following specific fields are filled when the plugin is called : - - * item1 : pointer to an union matching itemLookup1, or NULL if not found - * item2 : pointer to an union matching itemLookup2, or NULL if not found - -The following return codes are expected, any other will abort the signing process : - - * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized - * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one - -### ETH_PLUGIN_QUERY_CONTRACT_ID - -[source,C] ----- - -typedef struct ethQueryContractID_t { - - ethPluginSharedRW_t *pluginSharedRW; - ethPluginSharedRO_t *pluginSharedRO; - uint8_t *pluginContext; - - char *name; - uint32_t nameLength; - char *version; - uint32_t versionLength; - - uint8_t result; - -} ethQueryContractID_t; - ----- - -This message is sent after the parsing finalization and information lookups if requested if a generic UI is used. The following specific fields are provided when the plugin is called : - - * name : pointer to the name of the plugin, to be filled by the plugin - * nameLength : maximum name length - * version : pointer to the version of the plugin, to be filled by the plugin - * versionLength : maximum version length - -The following return codes are expected, any other will abort the signing process : - - * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized - -### ETH_PLUGIN_QUERY_CONTRACT_UI - -[source,C] ----- - -typedef struct ethQueryContractUI_t { - - ethPluginSharedRW_t *pluginSharedRW; - ethPluginSharedRO_t *pluginSharedRO; - union extraInfo_t *item1; - union extraInfo_t *item2; - char network_ticker[MAX_TICKER_LEN]; - uint8_t *pluginContext; - uint8_t screenIndex; - char *title; - uint32_t titleLength; - char *msg; - uint32_t msgLength; - - uint8_t result; - -} ethQueryContractUI_t; - ----- - -This message is sent when a plugin screen shall be displayed if a generic UI is used. The following specific fields are provided when the plugin is called : - - - * item1 : pointer to token / nft information - * item2 : pointer to token / nft information - * network_ticker : string that holds the network ticker - * screenIndex : index of the screen to display, starting from 0 - * title : pointer to the first line of the screen, to be filled by the plugin - * titleLength : maximum title length - * msg : pointer to the second line of the screen, to be filled by the plugin - * msgLength : maximum msg length - -The following return codes are expected, any other will abort the signing process : - - * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized - -## Caveats - -When setting a pointer from the plugin space, make sure to use an address that will be accessible from the Ethereum application (typically in the plugin RAM context, *not* on the plugin stack) - -Do not use data types that need to be aligned (such as uint32_t) in the plugin context. - -## TODOs - -Provide a sample callback mechanism for common plugin actions (amount to string, 256 bits number multiplication ...) to avoid duplicating code in the plugin space - -Provide external plugins samples - -Fully support Starkware as an independant application (APDU logic added) - -Support extra flags for the generic UI (fast confirmation on first screen, ...) - -Support extra plugin provisioning (signed list of associated smart contract addresses, ...) +Ethereum application Plugins : Technical Specifications +======================================================= +Ledger Firmware Team +Specification version 1.0 - 24th of September 2020 + + +## 1.0 + - Initial release + +## About + +This specification describes the plugin interface used to display a specific UI on device for Ethereum smart contracts. + +Feel free to checkout the ParaSwap plugin to see an actual implementation. Link: https://github.com/LedgerHQ/app-ethereum/blob/named-external-plugins/doc/ethapp_plugins.asc . + +## Flow overview + +When signing an Ethereum transaction containing data, the Ethereum application looks for a plugin using .either a selector list or the contract address. + +If a plugin is found, each network serialized data field (32 bytes) is passed to the plugin along with the field offset. The plugin can decide to stop the signature process if a data field isn't expected + +After all fields have been received, the plugin can report to the Ethereum application whether the full data is accepted, and the user interface model that'll be used to display the data + +### Amount/Address user interface + +In this model, the generic (without data) transaction display is used, with the amount and destination address replaced by data provided by the plugin + +### Generic user interface + +In this model, the plugin first reports a number of screens (2 lines of text, the second line being scrollable) to be displayed + +The Ethereum application will request each screen to be displayed to the plugin and let the user browse through them. + +The first screen being displayed is always a description of the plugin being used (name and version reported by the plugin), and the last screens include the transaction fees in ETH and a confirmation prompt + +### Code flow + +The plugin interfacing logic is described in _src/eth_plugin_interface.h_ + +The plugin common dispatcher is found in _src/eth_plugin_handler.c_ + +The plugin generic UI dispatcher is found in _src/eth_plugin_ui.c_ + +Sample internal plugins are provided in _src_plugins/_ + +## Creating a plugin + +### Creating an internal plugin + +Internal plugins are triggered on specific selectors. You can modify _src/eth_plugin_internal.c_ to add your mapping. + +Other specific mappings can be also added by modifying the common dispatcher + +### Creating an external plugin + +An external plugin is a library application named after the base64 encoding of the 20 bytes smart contract address + +## Detailed flow messages + +### Generic fields + +The following generic fields are present in all messages : + + * pluginSharedRW : scratch objects and utilities available to the plugin (can be read and written) + + * pluginSharedRO : transaction data available to the plugin (can only be read) + + * pluginContext : arbitrary data blob holding the plugin context, to be set and used by the plugin + + * result : return code set by the plugin following the message processing + +### ETH_PLUGIN_INIT_CONTRACT + +[source,C] +---- + +typedef struct ethPluginInitContract_t { + + // in + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + uint32_t pluginContextLength; + uint8_t *selector; // 4 bytes selector + uint32_t dataSize; + + char *alias; // 29 bytes alias if ETH_PLUGIN_RESULT_OK_ALIAS set + + uint8_t result; + +} ethPluginInitContract_t; + +---- + +This message is sent when the selector of the data has been parsed. The following specific fields are filled when the plugin is called : + + * pluginContextLength : length of the data field available to store the plugin context + * selector : 4 bytes selector of the data field + * dataSize : size in bytes of the data field + +The following return codes are expected, any other will abort the signing process : + + * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized + * ETH_PLUGIN_RESULT_OK_ALIAS : if a base64 encoded alias of another plugin to call is copied to the _alias_ field. In this case, the dispatcher will follow the alias chain, and the original plugin will only be called to retrieve its name when using a generic user interface + * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one + +### ETH_PLUGIN_PROVIDE_PARAMETER + +[source,C] +---- + +typedef struct ethPluginProvideParameter_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + uint8_t *parameter; // 32 bytes parameter + uint32_t parameterOffset; + + uint8_t result; + +} ethPluginProvideParameter_t; + +---- + +This message is sent when a new 32 bytes component of the data field is available. The following specific fields are filled when the plugin is called : + + * parameter : pointer to the 32 bytes parameter being parsed + * parameterOffset : offset to this parameter from the beginning of the data field (starts at 4, following the selector) + +The following return codes are expected, any other will abort the signing process : + + * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized + * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one + +There are already defined functions to extract data from a parameter: +[source,C] +---- +void copy_address(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size); +void copy_parameter(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size); + +// Get the value from the beginning of the parameter (right to left) and check if the rest of it is zero +bool U2BE_from_parameter(const uint8_t* parameter, uint16_t* value); +bool U4BE_from_parameter(const uint8_t* parameter, uint32_t* value); +---- + +### ETH_PLUGIN_FINALIZE + +[source,C] +---- + +typedef struct ethPluginFinalize_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + + uint8_t *itemLookup1; // set by the plugin if a token or an nft should be looked up + uint8_t *itemLookup2; + + uint8_t *amount; // set an uint256 pointer if uiType is UI_AMOUNT_ADDRESS + 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; + uint8_t numScreens; // ignored if uiType is UI_AMOUNT_ADDRESS + uint8_t result; + +} ethPluginFinalize_t; + +---- + +This message is sent when the data field has been fully parsed. The following specific fields can be filled by the plugin : + + * itemLookup1 : the pointer shall be set to a 20 bytes address to look up an ERC20 token or NFT if needed by the plugin + * itemLookup2 : the pointer shall be set to a 20 bytes address to look up an ERC20 token or NFT if needed by the plugin + * uiType : set to either ETH_UI_TYPE_AMOUNT_ADDRESS for an amount/address UI or ETH_UI_TYPE_GENERIC for a generic UI + +The following specific fields are filled by the plugin when returning an amount/address UI : + + * amount : set to a pointer to a 256 bits number + * address : set to a pointer to a 20 bytes address + +The following specific fields are filled by the plugin when returning a generic UI : + + * numScreens : number of screens handled by the plugin + +The following return codes are expected, any other will abort the signing process : + + * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized + * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one + +### ETH_PLUGIN_PROVIDE_INFO + +[source,C] +---- + +typedef struct ethPluginProvideInfo_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + + union extraInfo *item1; // set by the ETH application, to be saved by the plugin + union extraInfo *item2; + + uint8_t additionalScreens; // Used by the plugin if it needs to display additional screens based on the information received. + + uint8_t result; + +} ethPluginProvideInfo_t; + +---- + +This message is sent if an information lookup was required by the plugin when parsing a finalize message. The following specific fields are filled when the plugin is called : + + * item1 : pointer to an union matching itemLookup1, or NULL if not found + * item2 : pointer to an union matching itemLookup2, or NULL if not found + +The following return codes are expected, any other will abort the signing process : + + * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized + * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one + +### ETH_PLUGIN_QUERY_CONTRACT_ID + +[source,C] +---- + +typedef struct ethQueryContractID_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + + char *name; + uint32_t nameLength; + char *version; + uint32_t versionLength; + + uint8_t result; + +} ethQueryContractID_t; + +---- + +This message is sent after the parsing finalization and information lookups if requested if a generic UI is used. The following specific fields are provided when the plugin is called : + + * name : pointer to the name of the plugin, to be filled by the plugin + * nameLength : maximum name length + * version : pointer to the version of the plugin, to be filled by the plugin + * versionLength : maximum version length + +The following return codes are expected, any other will abort the signing process : + + * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized + +### ETH_PLUGIN_QUERY_CONTRACT_UI + +[source,C] +---- + +typedef struct ethQueryContractUI_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + union extraInfo_t *item1; + union extraInfo_t *item2; + char network_ticker[MAX_TICKER_LEN]; + uint8_t *pluginContext; + uint8_t screenIndex; + char *title; + uint32_t titleLength; + char *msg; + uint32_t msgLength; + + uint8_t result; + +} ethQueryContractUI_t; + +---- + +This message is sent when a plugin screen shall be displayed if a generic UI is used. The following specific fields are provided when the plugin is called : + + + * item1 : pointer to token / nft information + * item2 : pointer to token / nft information + * network_ticker : string that holds the network ticker + * screenIndex : index of the screen to display, starting from 0 + * title : pointer to the first line of the screen, to be filled by the plugin + * titleLength : maximum title length + * msg : pointer to the second line of the screen, to be filled by the plugin + * msgLength : maximum msg length + +The following return codes are expected, any other will abort the signing process : + + * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized + +## Caveats + +When setting a pointer from the plugin space, make sure to use an address that will be accessible from the Ethereum application (typically in the plugin RAM context, *not* on the plugin stack) + +Do not use data types that need to be aligned (such as uint32_t) in the plugin context. + +## TODOs + +Provide a sample callback mechanism for common plugin actions (amount to string, 256 bits number multiplication ...) to avoid duplicating code in the plugin space + +Provide external plugins samples + +Support extra flags for the generic UI (fast confirmation on first screen, ...) + +Support extra plugin provisioning (signed list of associated smart contract addresses, ...)