Files
app-ethereum/doc/ethapp_plugins.asc
apaillier-ledger fcc3dd6d31 Add support for ERC-721 and ERC-1155 (v3) (#218)
* First draft for erc721 token allowance

* Split ui and provide parameters into their own files

* Print txtype when not supported

* fix compilation for erc721

* Use pluginType

* Add debug statement in compound plugin

* add debug error msg in plugin error

* Add parameter parsing for all methods

* Remove debug logs

* Add SET_APPROVAL_FOR_ALL; Add correct parsing method on contract init

* Add dst_size parameter to copy functions

* Add query contract id code

* format

* Add UIs

* update ethapp.asc

* Change setExternalPlugin to setPlugin; Add support for ERC721

* clang-format

* Fix typo Unconsistent -> Inconsistent

* Add support for 721; use extraInfo

* Add extraInfo to ethpluginQueryConractUI

* Rename extraInfo to item

* Add txFromEtherscan to tests

* Add nft key and temp padding

* Remove comments around HAVE_BYPASS_SIGNATURES

* Rename TESTING_KEY to NFT_TESTING_KEY

* Add comments regarding value of queryContractUI->item

* Fix comment regarding method selector

* Rename provideToken to provideInfo; Update plugin doc

* fix caps of eth_plugin_prepare_provide_info

* fix caps of handle_provide_info

* Use verificationFn insead of hardcoded cx_ecdsa_verify

* Add comments about nftInfo_t and tokenDefinition_t

* Add erc721 test

* Remove comment from plugin interface version

* Fix network_ticker duplicate

* Add setPlugin and provideNFTInfo to doc.asc

* Add back setExternalPlugin; implement new setPlugin

* Update plugin sdk

* Call setPlugin instead of setExternalPlugin

* setPlugin work without checking sig

* Remove printf of displayed fees

* Add working 721 test

* Finalize ERC721 and add simple test

* Display NFT address on set approval and operator

* Support set approval for all for erc721

* Finish UI for set approval for all erc721

* Move copy_parameter and copy_address to eth_plugin_internal; Add tests for erc721

* update plugin sdk

* Add erc1155 plugin and 1155 tests placeholder

* Add restriction for AWS key and setPlugin

* Add NOT_OLD_INTERNAL variant; Add erc_1155_plugin_call

* Fixed compilation warnings (function pointer casting)

Co-authored-by: pscott <scott.piriou@ledger.fr>
2021-11-22 14:39:36 +01:00

305 lines
11 KiB
Plaintext

Ethereum application Plugins : Technical Specifications
=======================================================
Ledger Firmware Team <hello@ledger.fr>
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
### 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, ...)