Merge pull request #267 from LedgerHQ/develop_1.9.17

Add Non-Fungible Token (ERC 721 & 1155) support
This commit is contained in:
Edouard Merle
2022-02-14 17:28:50 +01:00
committed by GitHub
117 changed files with 2612 additions and 397 deletions

View File

@@ -5,13 +5,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [1.9.17](https://github.com/ledgerhq/app-ethereum/compare/1.9.16...1.9.17) - 2022-01-14
### Added
- Support for Non-Fungible Tokens (ERC-721 & ERC-1155)
## [1.9.16](https://github.com/ledgerhq/app-ethereum/compare/1.9.14...1.9.16) - 2022-01-13
### Added
- Shyft variant
## [1.9.14](https://github.com/ledgerhq/app-ethereum/compare/1.9.13...1.9.14) - 2021-11-30
### Added
- Added Moonriver BIP44 1285
### Fixed
- Fixed stark order signature on LNS
## [1.9.13](https://github.com/ledgerhq/app-ethereum/compare/1.9.12...1.9.13) - 2021-11-17
### Changed
- Small improvement in app size
## [1.9.12](https://github.com/ledgerhq/app-ethereum/compare/1.9.11...1.9.12) - 2021-11-12
### Fixed
- Fixed stark order signature on LNX
## [1.9.11](https://github.com/ledgerhq/app-ethereum/compare/1.9.10...1.9.11) - 2021-10-12
### Added

36
Makefile Executable file → Normal file
View File

@@ -30,7 +30,7 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'"
APPVERSION_M=1
APPVERSION_N=9
APPVERSION_P=16
APPVERSION_P=17
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION)
@@ -248,10 +248,10 @@ APP_LOAD_PARAMS += $(APP_LOAD_FLAGS) --path "44'/1'"
DEFINES += $(DEFINES_LIB)
#prepare hsm generation
ifeq ($(TARGET_NAME), TARGET_NANOX)
ICONNAME=icons/nanox_app_$(CHAIN).gif
else
ifeq ($(TARGET_NAME),TARGET_NANOS)
ICONNAME=icons/nanos_app_$(CHAIN).gif
else
ICONNAME=icons/nanox_app_$(CHAIN).gif
endif
################
@@ -283,19 +283,21 @@ DEFINES += HAVE_UX_FLOW
DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=0 WEBUSB_URL=""
ifeq ($(TARGET_NAME),TARGET_NANOX)
DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300
DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000
DEFINES += HAVE_BLE_APDU # basic ledger apdu transport over BLE
endif
ifeq ($(TARGET_NAME),TARGET_NANOS)
DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=72
DEFINES += HAVE_WALLET_ID_SDK
else
DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300
DEFINES += HAVE_GLO096
DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64
DEFINES += HAVE_BAGL_ELLIPSIS # long label truncation feature
DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX
DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX
DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX
else
DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=72
DEFINES += HAVE_WALLET_ID_SDK
endif
# Enables direct data signing without having to specify it in the settings. Useful when testing with speculos.
@@ -304,21 +306,31 @@ ifneq ($(ALLOW_DATA),0)
DEFINES += HAVE_ALLOW_DATA
endif
# Bypass the signature verification for setExternalPlugin and provideERC20TokenInfo calls
# Bypass the signature verification for setExternalPlugin, setPlugin, provideERC20TokenInfo and provideNFTInfo calls
BYPASS_SIGNATURES:=0
ifneq ($(BYPASS_SIGNATURES),0)
DEFINES += HAVE_BYPASS_SIGNATURES
endif
# NFTs
ifneq ($(TARGET_NAME),TARGET_NANOS)
DEFINES += HAVE_NFT_SUPPORT
# Enable the NFT testing key
NFT_TESTING_KEY:=0
ifneq ($(NFT_TESTING_KEY),0)
DEFINES += HAVE_NFT_TESTING_KEY
endif
endif
# Enabling debug PRINTF
DEBUG:=0
ifneq ($(DEBUG),0)
DEFINES += HAVE_STACK_OVERFLOW_CHECK
ifeq ($(TARGET_NAME),TARGET_NANOX)
DEFINES += HAVE_PRINTF PRINTF=mcu_usb_printf
else
ifeq ($(TARGET_NAME),TARGET_NANOS)
DEFINES += HAVE_PRINTF PRINTF=screen_printf
else
DEFINES += HAVE_PRINTF PRINTF=mcu_usb_printf
endif
else
DEFINES += PRINTF\(...\)=

View File

@@ -4,7 +4,7 @@ Ledger Blue is not maintained anymore, but the app can still be compiled for thi
This app follows the specification available in the `doc/` folder.
To compile it and load it on a device, please check out our [developer portal](https://developers.ledger.com/docs/NA/start_here/).
To compile it and load it on a device, please check out our [developer portal](https://developers.ledger.com/docs/nano-app/introduction/).
# Plugins
@@ -20,7 +20,7 @@ First [install yarn](https://classic.yarnpkg.com/en/docs/install/#debian-stable)
Open `tests/build_local_test_elfs.sh` and add your BOLOS SDKs path to `NANOS_SDK` and `NANOX_SDK`.
This helper script will build the applications required by the test suite and move them at the right place.
```
cd test
cd tests
./build_local_test_elfs.sh
```
Then you can install the project by simply running:

View File

@@ -23,6 +23,9 @@ Application version 1.5.0 - 25th of September 2020
## 1.7.6
- Add SET EXTERNAL PLUGIN
## 1.9.13
- Add SET PLUGIN
## About
This application describes the APDU messages interface to communicate with the Ethereum application.
@@ -240,6 +243,49 @@ signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f
None
### PROVIDE NFT INFORMATION
#### Description
This commands provides a trusted description of an NFT to associate a contract address with a collectionName.
It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper nft information to the user if necessary, as marked in GET APP CONFIGURATION flags.
The signature is computed on
type || version || len(collectionName) || collectionName || address || chainId || keyId || algorithmId || len(signature) || signature
#### Coding
'Command'
[width="80%"]
|==============================================================================================================================
| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le*
| E0 | 14 | 00 | 00 | variable | 00
|==============================================================================================================================
'Input data'
[width="80%"]
|==============================================================================================================================
| *Description* | *Length*
| Type | 1
| Version | 1
| Collection Name Length | 1
| Collection Name | variable
| Address | 20
| Chain ID | 8
| KeyID | 1
| Algorithm ID | 1
| Signature Length | 1
| Signature | variable
|==============================================================================================================================
'Output data'
None
### SET EXTERNAL PLUGIN
@@ -283,6 +329,56 @@ signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f
None
### SET PLUGIN
#### Description
This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command.
It can be used to set both internal and external plugins.
It shall be run immediately before performing a transaction involving a contract supported by this plugin to display the proper information to the user if necessary.
The function returns an error sw (0x6984) if the plugin requested is not installed on the device, 0x9000 otherwise.
The plugin names `ERC20`, `ERC721` and `ERC1155` are reserved. Additional plugin names might be added to this list in the future.
The signature is computed on
type || version || len(pluginName) || pluginName || address || selector || chainId || keyId || algorithmId || len(signature) || signature
#### Coding
'Command'
[width="80%"]
|==============================================================================================================================
| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le*
| E0 | 16 | 00 | 00 | variable | 00
|==============================================================================================================================
'Input data'
[width="80%"]
|==============================================================================================================================
| *Description* | *Length*
| Type | 1
| Version | 1
| Plugin Name Length | 1
| Plugin Name | variable
| Address | 20
| Selector | 4
| Chain ID | 8
| KeyID | 1
| Algorithm ID | 1
| Signature Length | 1
| Signature | variable
|==============================================================================================================================
'Output data'
None
### GET APP CONFIGURATION
#### Description

View File

@@ -145,8 +145,8 @@ typedef struct ethPluginFinalize_t {
ethPluginSharedRO_t *pluginSharedRO;
uint8_t *pluginContext;
uint8_t *tokenLookup1; // set by the plugin if a token should be looked up
uint8_t *tokenLookup2;
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
@@ -161,8 +161,8 @@ typedef struct ethPluginFinalize_t {
This message is sent when the data field has been fully parsed. The following specific fields can be filled by the plugin :
* tokenLookup1 : the pointer shall be set to a 20 bytes address to look up an ERC 20 token descriptor if needed by the plugin
* tokenLookup2 : the pointer shall be set to a 20 bytes address to look up an ERC 20 token descriptor if needed 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 :
@@ -179,32 +179,32 @@ The following return codes are expected, any other will abort the signing proces
* 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_TOKEN
### ETH_PLUGIN_PROVIDE_INFO
[source,C]
----
typedef struct ethPluginProvideToken_t {
typedef struct ethPluginProvideInfo_t {
ethPluginSharedRW_t *pluginSharedRW;
ethPluginSharedRO_t *pluginSharedRO;
uint8_t *pluginContext;
tokenDefinition_t *token1; // set by the ETH application, to be saved by the plugin
tokenDefinition_t *token2;
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 from the token definitions.
uint8_t additionalScreens; // Used by the plugin if it needs to display additional screens based on the information received.
uint8_t result;
} ethPluginProvideToken_t;
} ethPluginProvideInfo_t;
----
This message is sent if a token lookup was required by the plugin when parsing a finalize message. The following specific fields are filled when the plugin is called :
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 :
* token1 : pointer to a token definition matching tokenLookup1, or NULL if not found
* token2 : pointer to a token definition matching tokenLookup2, or NULL if not found or not requested
* 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 :
@@ -233,7 +233,7 @@ typedef struct ethQueryContractID_t {
----
This message is sent after the parsing finalization and token lookups if requested if a generic UI is used. The following specific fields are provided when the plugin is called :
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
@@ -253,6 +253,9 @@ 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;
@@ -268,6 +271,10 @@ typedef struct 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

View File

@@ -15,6 +15,8 @@
#define INS_GET_ETH2_PUBLIC_KEY 0x0E
#define INS_SET_ETH2_WITHDRAWAL_INDEX 0x10
#define INS_SET_EXTERNAL_PLUGIN 0x12
#define INS_PROVIDE_NFT_INFORMATION 0x14
#define INS_SET_PLUGIN 0x16
#define P1_CONFIRM 0x01
#define P1_NON_CONFIRM 0x00
#define P2_NO_CHAINCODE 0x00
@@ -64,6 +66,12 @@ void handleProvideErc20TokenInformation(uint8_t p1,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx);
void handleProvideNFTInformation(uint8_t p1,
uint8_t p2,
uint8_t *dataBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx);
void handleSign(uint8_t p1,
uint8_t p2,
uint8_t *dataBuffer,
@@ -96,6 +104,13 @@ void handleSetExternalPlugin(uint8_t p1,
unsigned int *flags,
unsigned int *tx);
void handleSetPlugin(uint8_t p1,
uint8_t p2,
uint8_t *workBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx);
#ifdef HAVE_ETH2
void handleGetEth2PublicKey(uint8_t p1,

View File

@@ -2,6 +2,7 @@
#include "eth_plugin_handler.h"
#include "eth_plugin_internal.h"
#include "shared_context.h"
#include "network.h"
void eth_plugin_prepare_init(ethPluginInitContract_t *init, uint8_t *selector, uint32_t dataSize) {
memset((uint8_t *) init, 0, sizeof(ethPluginInitContract_t));
@@ -21,8 +22,8 @@ void eth_plugin_prepare_finalize(ethPluginFinalize_t *finalize) {
memset((uint8_t *) finalize, 0, sizeof(ethPluginFinalize_t));
}
void eth_plugin_prepare_provide_token(ethPluginProvideToken_t *provideToken) {
memset((uint8_t *) provideToken, 0, sizeof(ethPluginProvideToken_t));
void eth_plugin_prepare_provide_info(ethPluginProvideInfo_t *provideToken) {
memset((uint8_t *) provideToken, 0, sizeof(ethPluginProvideInfo_t));
}
void eth_plugin_prepare_query_contract_ID(ethQueryContractID_t *queryContractID,
@@ -44,6 +45,23 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI,
char *msg,
uint32_t msgLength) {
memset((uint8_t *) queryContractUI, 0, sizeof(ethQueryContractUI_t));
// If no extra information was found, set the pointer to NULL
if (allzeroes(&tmpCtx.transactionContext.extraInfo[1], sizeof(union extraInfo_t))) {
queryContractUI->item1 = NULL;
} else {
queryContractUI->item1 = &tmpCtx.transactionContext.extraInfo[1];
}
// If no extra information was found, set the pointer to NULL
if (allzeroes(&tmpCtx.transactionContext.extraInfo[0], sizeof(union extraInfo_t))) {
queryContractUI->item2 = NULL;
} else {
queryContractUI->item2 = &tmpCtx.transactionContext.extraInfo[0];
}
strlcpy(queryContractUI->network_ticker, get_network_ticker(), MAX_TICKER_LEN);
queryContractUI->screenIndex = screenIndex;
strlcpy(queryContractUI->network_ticker,
get_network_ticker(),
@@ -54,61 +72,83 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI,
queryContractUI->msgLength = msgLength;
}
eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
static void eth_plugin_perform_init_default(uint8_t *contractAddress,
ethPluginInitContract_t *init) {
uint8_t i;
const uint8_t **selectors;
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE;
// check if the registered external plugin matches the TX contract address / selector
if (memcmp(contractAddress,
dataContext.tokenContext.contractAddress,
sizeof(dataContext.tokenContext.contractAddress)) != 0) {
PRINTF("Got contract: %.*H\n", ADDRESS_LENGTH, contractAddress);
PRINTF("Expected contract: %.*H\n",
ADDRESS_LENGTH,
dataContext.tokenContext.contractAddress);
os_sched_exit(0);
}
if (memcmp(init->selector,
dataContext.tokenContext.methodSelector,
sizeof(dataContext.tokenContext.methodSelector)) != 0) {
PRINTF("Got selector: %.*H\n", SELECTOR_SIZE, init->selector);
PRINTF("Expected selector: %.*H\n", SELECTOR_SIZE, dataContext.tokenContext.methodSelector);
os_sched_exit(0);
}
PRINTF("Plugin will be used\n");
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
}
PRINTF("Selector %.*H\n", 4, init->selector);
if (externalPluginIsSet) {
// check if the registered external plugin matches the TX contract address / method selector
if (memcmp(contractAddress,
dataContext.tokenContext.contract_address,
sizeof(dataContext.tokenContext.contract_address)) != 0) {
PRINTF("Got contract: %.*H\n", ADDRESS_LENGTH, contractAddress);
PRINTF("Expected contract: %.*H\n",
ADDRESS_LENGTH,
dataContext.tokenContext.contract_address);
os_sched_exit(0);
static bool eth_plugin_perform_init_old_internal(uint8_t *contractAddress,
ethPluginInitContract_t *init) {
uint8_t i, j;
const uint8_t **selectors;
// Search internal plugin list
for (i = 0;; i++) {
selectors = (const uint8_t **) PIC(INTERNAL_ETH_PLUGINS[i].selectors);
if (selectors == NULL) {
break;
}
if (memcmp(init->selector,
dataContext.tokenContext.method_selector,
sizeof(dataContext.tokenContext.method_selector)) != 0) {
PRINTF("Got selector: %.*H\n", SELECTOR_SIZE, init->selector);
PRINTF("Expected selector: %.*H\n",
SELECTOR_SIZE,
dataContext.tokenContext.method_selector);
os_sched_exit(0);
}
PRINTF("External plugin will be used\n");
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
contractAddress = NULL;
} else {
// Search internal plugin list
for (i = 0;; i++) {
uint8_t j;
selectors = (const uint8_t **) PIC(INTERNAL_ETH_PLUGINS[i].selectors);
if (selectors == NULL) {
break;
}
for (j = 0; ((j < INTERNAL_ETH_PLUGINS[i].num_selectors) && (contractAddress != NULL));
j++) {
if (memcmp(init->selector, (const void *) PIC(selectors[j]), SELECTOR_SIZE) == 0) {
if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) ||
((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck))()) {
strlcpy(dataContext.tokenContext.pluginName,
INTERNAL_ETH_PLUGINS[i].alias,
PLUGIN_ID_LENGTH);
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
contractAddress = NULL;
break;
}
for (j = 0; ((j < INTERNAL_ETH_PLUGINS[i].num_selectors) && (contractAddress != NULL));
j++) {
if (memcmp(init->selector, (const void *) PIC(selectors[j]), SELECTOR_SIZE) == 0) {
if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) ||
((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck))()) {
strlcpy(dataContext.tokenContext.pluginName,
INTERNAL_ETH_PLUGINS[i].alias,
PLUGIN_ID_LENGTH);
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
return true;
}
}
}
}
return false;
}
eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
ethPluginInitContract_t *init) {
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE;
PRINTF("Selector %.*H\n", 4, init->selector);
switch (pluginType) {
#ifdef HAVE_NFT_SUPPORT
case ERC1155:
case ERC721:
#endif // HAVE_NFT_SUPPORT
case EXTERNAL:
eth_plugin_perform_init_default(contractAddress, init);
contractAddress = NULL;
break;
case OLD_INTERNAL:
if (eth_plugin_perform_init_old_internal(contractAddress, init)) {
contractAddress = NULL;
}
break;
default:
PRINTF("Unsupported pluginType %d\n", pluginType);
os_sched_exit(0);
break;
}
// Do not handle a plugin if running in swap mode
if (called_from_swap && (contractAddress != NULL)) {
PRINTF("eth_plug_init aborted in swap mode\n");
@@ -139,7 +179,6 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
ethPluginSharedRO_t pluginRO;
char *alias;
uint8_t i;
uint8_t internalPlugin = 0;
pluginRW.sha3 = &global_sha3;
pluginRO.txContent = &tmpContent.txContent;
@@ -182,12 +221,12 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
((ethPluginFinalize_t *) parameter)->pluginContext =
(uint8_t *) &dataContext.tokenContext.pluginContext;
break;
case ETH_PLUGIN_PROVIDE_TOKEN:
PRINTF("-- PLUGIN PROVIDE TOKEN --\n");
((ethPluginProvideToken_t *) parameter)->result = ETH_PLUGIN_RESULT_UNAVAILABLE;
((ethPluginProvideToken_t *) parameter)->pluginSharedRW = &pluginRW;
((ethPluginProvideToken_t *) parameter)->pluginSharedRO = &pluginRO;
((ethPluginProvideToken_t *) parameter)->pluginContext =
case ETH_PLUGIN_PROVIDE_INFO:
PRINTF("-- PLUGIN PROVIDE INFO --\n");
((ethPluginProvideInfo_t *) parameter)->result = ETH_PLUGIN_RESULT_UNAVAILABLE;
((ethPluginProvideInfo_t *) parameter)->pluginSharedRW = &pluginRW;
((ethPluginProvideInfo_t *) parameter)->pluginSharedRO = &pluginRO;
((ethPluginProvideInfo_t *) parameter)->pluginContext =
(uint8_t *) &dataContext.tokenContext.pluginContext;
break;
case ETH_PLUGIN_QUERY_CONTRACT_ID:
@@ -210,35 +249,52 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
return ETH_PLUGIN_RESULT_UNAVAILABLE;
}
// Perform the call
for (i = 0;; i++) {
if (INTERNAL_ETH_PLUGINS[i].alias[0] == 0) {
switch (pluginType) {
case EXTERNAL: {
uint32_t params[3];
params[0] = (uint32_t) alias;
params[1] = method;
params[2] = (uint32_t) parameter;
BEGIN_TRY {
TRY {
os_lib_call(params);
}
CATCH_OTHER(e) {
PRINTF("Plugin call exception for %s\n", alias);
}
FINALLY {
}
}
END_TRY;
break;
}
if (strcmp(alias, INTERNAL_ETH_PLUGINS[i].alias) == 0) {
internalPlugin = 1;
((PluginCall) PIC(INTERNAL_ETH_PLUGINS[i].impl))(method, parameter);
#ifdef HAVE_NFT_SUPPORT
case ERC721: {
erc721_plugin_call(method, parameter);
break;
}
}
if (!internalPlugin) {
uint32_t params[3];
params[0] = (uint32_t) alias;
params[1] = method;
params[2] = (uint32_t) parameter;
BEGIN_TRY {
TRY {
os_lib_call(params);
}
CATCH_OTHER(e) {
PRINTF("Plugin call exception for %s\n", alias);
}
FINALLY {
}
case ERC1155: {
erc1155_plugin_call(method, parameter);
break;
}
#endif // HAVE_NFT_SUPPORT
case OLD_INTERNAL: {
// Perform the call
for (i = 0;; i++) {
if (INTERNAL_ETH_PLUGINS[i].alias[0] == 0) {
break;
}
if (strcmp(alias, INTERNAL_ETH_PLUGINS[i].alias) == 0) {
((PluginCall) PIC(INTERNAL_ETH_PLUGINS[i].impl))(method, parameter);
break;
}
}
break;
}
default: {
PRINTF("Error with pluginType: %d\n", pluginType);
return ETH_PLUGIN_RESULT_ERROR;
}
END_TRY;
}
// Check the call result
@@ -276,9 +332,9 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
return ETH_PLUGIN_RESULT_UNAVAILABLE;
}
break;
case ETH_PLUGIN_PROVIDE_TOKEN:
PRINTF("RESULT: %d\n", ((ethPluginProvideToken_t *) parameter)->result);
switch (((ethPluginProvideToken_t *) parameter)->result) {
case ETH_PLUGIN_PROVIDE_INFO:
PRINTF("RESULT: %d\n", ((ethPluginProvideInfo_t *) parameter)->result);
switch (((ethPluginProvideInfo_t *) parameter)->result) {
case ETH_PLUGIN_RESULT_OK:
case ETH_PLUGIN_RESULT_FALLBACK:
break;

View File

@@ -7,7 +7,7 @@ void eth_plugin_prepare_provide_parameter(ethPluginProvideParameter_t *providePa
uint8_t *parameter,
uint32_t parameterOffset);
void eth_plugin_prepare_finalize(ethPluginFinalize_t *finalize);
void eth_plugin_prepare_provide_token(ethPluginProvideToken_t *provideToken);
void eth_plugin_prepare_provide_info(ethPluginProvideInfo_t *provideToken);
void eth_plugin_prepare_query_contract_ID(ethQueryContractID_t *queryContractID,
char *name,
uint32_t nameLength,
@@ -24,7 +24,6 @@ eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
ethPluginInitContract_t *init);
// NULL for cached address, or base contract address
eth_plugin_result_t eth_plugin_call(int method, void *parameter);
int compound_plugin_call(uint8_t *contractAddress, int method, void *parameter);
void plugin_ui_start(void);

View File

@@ -6,14 +6,14 @@
#include "cx.h"
#include "ethUstream.h"
#include "tokens.h"
#define PLUGIN_ID_LENGTH 30
#include "shared_context.h"
// Interface version. To be updated everytime we introduce breaking changes to the plugin interface.
typedef enum {
ETH_PLUGIN_INTERFACE_VERSION_1 = 1, // Version 1
ETH_PLUGIN_INTERFACE_VERSION_1 = 1,
ETH_PLUGIN_INTERFACE_VERSION_2 = 2,
ETH_PLUGIN_INTERFACE_VERSION_LATEST = 3,
ETH_PLUGIN_INTERFACE_VERSION_3 = 3,
ETH_PLUGIN_INTERFACE_VERSION_LATEST = 4,
} eth_plugin_interface_version_t;
typedef enum {
@@ -21,7 +21,7 @@ typedef enum {
ETH_PLUGIN_INIT_CONTRACT = 0x0101,
ETH_PLUGIN_PROVIDE_PARAMETER = 0x0102,
ETH_PLUGIN_FINALIZE = 0x0103,
ETH_PLUGIN_PROVIDE_TOKEN = 0x0104,
ETH_PLUGIN_PROVIDE_INFO = 0x0104,
ETH_PLUGIN_QUERY_CONTRACT_ID = 0x0105,
ETH_PLUGIN_QUERY_CONTRACT_UI = 0x0106,
ETH_PLUGIN_CHECK_PRESENCE = 0x01FF
@@ -126,20 +126,20 @@ typedef struct ethPluginFinalize_t {
// Provide token
typedef struct ethPluginProvideToken_t {
typedef struct ethPluginProvideInfo_t {
ethPluginSharedRW_t *pluginSharedRW;
ethPluginSharedRO_t *pluginSharedRO;
uint8_t *pluginContext;
tokenDefinition_t *token1; // set by the ETH application, to be saved by the plugin
tokenDefinition_t *token2;
union extraInfo_t *item1; // set by the ETH application, to be saved by the plugin
union extraInfo_t *item2;
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;
} ethPluginProvideToken_t;
} ethPluginProvideInfo_t;
// Query Contract name and version
@@ -164,9 +164,11 @@ typedef struct ethQueryContractID_t {
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 network_ticker[MAX_TICKER_LEN];
char *title;
size_t titleLength;

View File

@@ -1,11 +1,20 @@
#include "eth_plugin_internal.h"
bool erc20_plugin_available_check(void);
bool erc721_plugin_available_check(void);
void erc20_plugin_call(int message, void* parameters);
void erc721_plugin_call(int message, void* parameters);
void compound_plugin_call(int message, void* parameters);
void copy_address(uint8_t* dst, uint8_t* parameter, uint8_t dst_size) {
uint8_t copy_size = MIN(dst_size, ADDRESS_LENGTH);
memmove(dst, parameter + PARAMETER_LENGTH - copy_size, copy_size);
}
void copy_parameter(uint8_t* dst, uint8_t* parameter, uint8_t dst_size) {
uint8_t copy_size = MIN(dst_size, PARAMETER_LENGTH);
memmove(dst, parameter, copy_size);
}
#ifdef HAVE_STARKWARE
void starkware_plugin_call(int message, void* parameters);
#endif
@@ -19,10 +28,6 @@ static const uint8_t ERC20_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7,
const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS] = {ERC20_TRANSFER_SELECTOR,
ERC20_APPROVE_SELECTOR};
static const uint8_t ERC721_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7, 0xb3};
const uint8_t* const ERC721_SELECTORS[NUM_ERC721_SELECTORS] = {ERC721_APPROVE_SELECTOR};
static const uint8_t COMPOUND_REDEEM_UNDERLYING_SELECTOR[SELECTOR_SIZE] = {0x85, 0x2a, 0x12, 0xe3};
static const uint8_t COMPOUND_REDEEM_SELECTOR[SELECTOR_SIZE] = {0xdb, 0x00, 0x6a, 0x75};
static const uint8_t COMPOUND_MINT_SELECTOR[SELECTOR_SIZE] = {0xa0, 0x71, 0x2d, 0x68};
@@ -105,12 +110,6 @@ const internalEthPlugin_t INTERNAL_ETH_PLUGINS[] = {
"-erc20",
erc20_plugin_call},
{erc721_plugin_available_check,
(const uint8_t**) ERC721_SELECTORS,
NUM_ERC721_SELECTORS,
"-er721",
erc721_plugin_call},
{NULL,
(const uint8_t**) COMPOUND_SELECTORS,
NUM_COMPOUND_SELECTORS,

View File

@@ -6,6 +6,13 @@
#define PARAMETER_LENGTH 32
#define RUN_APPLICATION 1
void copy_address(uint8_t* dst, uint8_t* parameter, uint8_t dst_size);
void copy_parameter(uint8_t* dst, uint8_t* parameter, uint8_t dst_size);
void erc721_plugin_call(int message, void* parameters);
void erc1155_plugin_call(int message, void* parameters);
typedef bool (*PluginAvailableCheck)(void);
typedef struct internalEthPlugin_t {
@@ -19,9 +26,6 @@ typedef struct internalEthPlugin_t {
#define NUM_ERC20_SELECTORS 2
extern const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS];
#define NUM_ERC721_SELECTORS 1
extern const uint8_t* const ERC721_SELECTORS[NUM_ERC721_SELECTORS];
#define NUM_COMPOUND_SELECTORS 4
extern const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS];

View File

@@ -50,7 +50,7 @@ cx_sha3_t global_sha3;
uint8_t appState;
bool called_from_swap;
bool externalPluginIsSet;
pluginType_t pluginType;
#ifdef HAVE_STARKWARE
bool quantumSet;
#endif
@@ -72,7 +72,7 @@ void reset_app_context() {
// PRINTF("!!RESET_APP_CONTEXT\n");
appState = APP_STATE_IDLE;
called_from_swap = false;
externalPluginIsSet = false;
pluginType = OLD_INTERNAL;
#ifdef HAVE_STARKWARE
quantumSet = false;
#endif
@@ -155,8 +155,8 @@ unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) {
return 0;
}
tokenDefinition_t *getKnownToken(uint8_t *contractAddress) {
tokenDefinition_t *currentToken = NULL;
extraInfo_t *getKnownToken(uint8_t *contractAddress) {
union extraInfo_t *currentItem = NULL;
#ifdef HAVE_TOKENS_LIST
uint32_t numTokens = 0;
uint32_t i;
@@ -386,12 +386,22 @@ tokenDefinition_t *getKnownToken(uint8_t *contractAddress) {
}
}
#endif
for (size_t i = 0; i < MAX_TOKEN; i++) {
currentToken = &tmpCtx.transactionContext.tokens[i];
//
for (uint8_t i = 0; i < MAX_ITEMS; i++) {
currentItem = (union extraInfo_t *) &tmpCtx.transactionContext.extraInfo[i].token;
if (tmpCtx.transactionContext.tokenSet[i] &&
(memcmp(currentToken->address, contractAddress, ADDRESS_LENGTH) == 0)) {
(memcmp(currentItem->token.address, contractAddress, ADDRESS_LENGTH) == 0)) {
PRINTF("Token found at index %d\n", i);
return currentToken;
return currentItem;
}
}
for (uint8_t i = 0; i < MAX_ITEMS; i++) {
currentItem = (union extraInfo_t *) &tmpCtx.transactionContext.extraInfo[i].token;
if (tmpCtx.transactionContext.tokenSet[i] &&
(memcmp(currentItem->nft.contractAddress, contractAddress, ADDRESS_LENGTH) == 0)) {
PRINTF("Token found at index %d\n", i);
return currentItem;
}
}
@@ -417,7 +427,7 @@ void handleGetWalletId(volatile unsigned int *tx) {
// pubkey -> sha512
cx_hash_sha512(pub.W, sizeof(pub.W), t, sizeof(t));
// ! cookie !
os_memmove(G_io_apdu_buffer, t, 64);
memmove(G_io_apdu_buffer, t, 64);
*tx = 64;
THROW(0x9000);
}
@@ -491,7 +501,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
switch (G_io_apdu_buffer[OFFSET_INS]) {
case INS_GET_PUBLIC_KEY:
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
handleGetPublicKey(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
@@ -509,6 +519,17 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
tx);
break;
#ifdef HAVE_NFT_SUPPORT
case INS_PROVIDE_NFT_INFORMATION:
handleProvideNFTInformation(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
G_io_apdu_buffer[OFFSET_LC],
flags,
tx);
break;
#endif // HAVE_NFT_SUPPORT
case INS_SET_EXTERNAL_PLUGIN:
handleSetExternalPlugin(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
@@ -518,6 +539,15 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
tx);
break;
case INS_SET_PLUGIN:
handleSetPlugin(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
G_io_apdu_buffer[OFFSET_LC],
flags,
tx);
break;
case INS_SIGN:
handleSign(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
@@ -537,7 +567,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
break;
case INS_SIGN_PERSONAL_MESSAGE:
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
@@ -547,7 +577,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
break;
case INS_SIGN_EIP_712_MESSAGE:
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
handleSignEIP712Message(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
@@ -559,7 +589,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
#ifdef HAVE_ETH2
case INS_GET_ETH2_PUBLIC_KEY:
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
handleGetEth2PublicKey(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
@@ -768,7 +798,7 @@ void coin_main(chain_config_t *coin_config) {
chainConfig = coin_config;
}
reset_app_context();
tmpCtx.transactionContext.currentTokenIndex = 0;
tmpCtx.transactionContext.currentItemIndex = 0;
for (;;) {
UX_INIT();

14
src/nft.h Normal file
View File

@@ -0,0 +1,14 @@
#include "tokens.h"
// An `nftInfo_t` must be the same size as a `tokenDefinition_t`. This is because both will be held
// in a `extraInfo_t` which is a union of a `nftInfo_t` and a `tokenDefinition_t`. By having both
// struct the same size, we know they will be aligned, which facilitates accessing the items.
// We defined the collection name max length to be the size of a `tokenDefinition_t` and remove the
// `ADDRESS_LENGTH` which corresponds to `sizeof(contractAddress`).
#define COLLECTION_NAME_MAX_LEN sizeof(tokenDefinition_t) - ADDRESS_LENGTH
typedef struct nftInfo_t {
char collectionName[COLLECTION_NAME_MAX_LEN];
char contractAddress[ADDRESS_LENGTH];
} nftInfo_t;

View File

@@ -14,16 +14,16 @@
#include "uint256.h"
#include "tokens.h"
#include "chainConfig.h"
#include "eth_plugin_interface.h"
#include "nft.h"
#define MAX_BIP32_PATH 10
#define MAX_TOKEN 2
#define WEI_TO_ETHER 18
#define SELECTOR_LENGTH 4
#define PLUGIN_ID_LENGTH 30
#define N_storage (*(volatile internalStorage_t *) PIC(&N_storage_real))
typedef struct internalStorage_t {
@@ -62,8 +62,8 @@ typedef struct tokenContext_t {
union {
struct {
uint8_t contract_address[ADDRESS_LENGTH];
uint8_t method_selector[SELECTOR_LENGTH];
uint8_t contractAddress[ADDRESS_LENGTH];
uint8_t methodSelector[SELECTOR_LENGTH];
};
uint8_t pluginContext[5 * INT256_LENGTH];
};
@@ -84,13 +84,18 @@ typedef struct publicKeyContext_t {
bool getChaincode;
} publicKeyContext_t;
typedef union extraInfo_t {
tokenDefinition_t token;
nftInfo_t nft;
} extraInfo_t;
typedef struct transactionContext_t {
uint8_t pathLength;
uint32_t bip32Path[MAX_BIP32_PATH];
uint8_t hash[INT256_LENGTH];
tokenDefinition_t tokens[MAX_TOKEN];
uint8_t tokenSet[MAX_TOKEN];
uint8_t currentTokenIndex;
union extraInfo_t extraInfo[MAX_ITEMS];
uint8_t tokenSet[MAX_ITEMS];
uint8_t currentItemIndex;
} transactionContext_t;
typedef struct messageSigningContext_t {
@@ -137,6 +142,7 @@ typedef struct starkContext_t {
typedef union {
tokenContext_t tokenContext;
#ifdef HAVE_STARKWARE
starkContext_t starkContext;
#endif
@@ -166,7 +172,7 @@ typedef enum {
typedef struct txStringProperties_t {
char fullAddress[43];
char fullAmount[67];
char fullAmount[79]; // 2^256 is 78 digits long
char maxFee[50];
char nonce[8]; // 10M tx per account ought to be enough for everybody
char network_name[NETWORK_STRING_MAX_SIZE];
@@ -196,7 +202,16 @@ extern cx_sha3_t global_sha3;
extern const internalStorage_t N_storage_real;
extern bool called_from_swap;
extern bool externalPluginIsSet;
typedef enum {
EXTERNAL, // External plugin, set by setExternalPlugin.
ERC721, // Specific ERC721 internal plugin, set by setPlugin.
ERC1155, // Specific ERC1155 internal plugin, set by setPlugin
OLD_INTERNAL // Old internal plugin, not set by any command.
} pluginType_t;
extern pluginType_t pluginType;
extern uint8_t appState;
#ifdef HAVE_STARKWARE
extern bool quantumSet;

View File

@@ -68,7 +68,7 @@ void stark_get_amount_string(uint8_t *contractAddress,
decimals = WEI_TO_ETHER;
PRINTF("stark_get_amount_string - ETH\n");
} else {
tokenDefinition_t *token = getKnownToken(contractAddress);
tokenDefinition_t *token = &getKnownToken(contractAddress)->token;
if (token == NULL) { // caught earlier
THROW(0x6A80);
}

View File

@@ -22,6 +22,7 @@
#include "ethUstream.h"
#define MAX_TICKER_LEN 12 // 10 characters + ' ' + '\0'
#define MAX_ITEMS 2
typedef struct tokenDefinition_t {
#ifdef HAVE_CONTRACT_NAME_IN_DESCRIPTOR
@@ -29,6 +30,9 @@ typedef struct tokenDefinition_t {
#endif
uint8_t address[ADDRESS_LENGTH];
char ticker[MAX_TICKER_LEN];
char nft_pad[20]; // Adding some padding because the `nftInfo_t` is based on the size of a
// `tokenDefinition_t`. By adding some padding here we give more space to the
// collection name in the `nftInfo_t`. See `nftInfo_t` for more information.
uint8_t decimals;
} tokenDefinition_t;

View File

@@ -21,4 +21,4 @@ void ui_warning_contract_data(void);
void io_seproxyhal_send_status(uint32_t sw);
void format_signature_out(const uint8_t *signature);
void finalizeParsing(bool direct);
tokenDefinition_t *getKnownToken(uint8_t *contractAddress);
extraInfo_t *getKnownToken(uint8_t *contractAddress);

View File

@@ -171,7 +171,7 @@ UX_STEP_CB(
"Error",
"Blind signing must be enabled in Settings",
});
#elif defined(TARGET_NANOX)
#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2)
UX_STEP_CB(
ux_warning_contract_data_step,
pnn,

View File

@@ -254,6 +254,7 @@ static void processTo(txContext_t *context) {
}
static void processData(txContext_t *context) {
PRINTF("PROCESS DATA\n");
if (context->currentFieldIsList) {
PRINTF("Invalid type for RLP_DATA\n");
THROW(EXCEPTION);
@@ -268,6 +269,7 @@ static void processData(txContext_t *context) {
copyTxData(context, NULL, copySize);
}
if (context->currentFieldPos == context->currentFieldLength) {
PRINTF("incrementing field\n");
context->currentField++;
context->processingField = false;
}
@@ -506,6 +508,7 @@ static parserStatus_e processTxInternal(txContext_t *context) {
customStatus_e customStatus = CUSTOM_NOT_HANDLED;
// EIP 155 style transaction
if (PARSING_IS_DONE(context)) {
PRINTF("parsing is done\n");
return USTREAM_FINISHED;
}
// Old style transaction (pre EIP-155). Transations could just skip `v,r,s` so we needed to
@@ -518,9 +521,11 @@ static parserStatus_e processTxInternal(txContext_t *context) {
if ((context->txType == LEGACY && context->currentField == LEGACY_RLP_V) &&
(context->commandLength == 0)) {
context->content->vLength = 0;
PRINTF("finished\n");
return USTREAM_FINISHED;
}
if (context->commandLength == 0) {
PRINTF("Command length done\n");
return USTREAM_PROCESSING;
}
if (!context->processingField) {
@@ -531,6 +536,7 @@ static parserStatus_e processTxInternal(txContext_t *context) {
}
if (context->customProcessor != NULL) {
customStatus = context->customProcessor(context);
PRINTF("After customprocessor\n");
switch (customStatus) {
case CUSTOM_NOT_HANDLED:
case CUSTOM_HANDLED:
@@ -571,11 +577,12 @@ static parserStatus_e processTxInternal(txContext_t *context) {
break;
}
default:
PRINTF("Transaction type %u is not supported\n", context->txType);
PRINTF("Transaction type %d is not supported\n", context->txType);
return USTREAM_FAULT;
}
}
}
PRINTF("end of here\n");
}
parserStatus_e processTx(txContext_t *context,
@@ -589,6 +596,7 @@ parserStatus_e processTx(txContext_t *context,
context->commandLength = length;
context->processingFlags = processingFlags;
result = processTxInternal(context);
PRINTF("result: %d\n");
}
CATCH_OTHER(e) {
result = USTREAM_FAULT;

View File

@@ -23,10 +23,10 @@ void handleProvideErc20TokenInformation(uint8_t p1,
cx_sha256_init(&sha256);
tmpCtx.transactionContext.currentTokenIndex =
(tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN;
tmpCtx.transactionContext.currentItemIndex =
(tmpCtx.transactionContext.currentItemIndex + 1) % MAX_ITEMS;
tokenDefinition_t *token =
&tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex];
&tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentItemIndex];
if (dataLength < 1) {
THROW(0x6A80);
@@ -93,7 +93,7 @@ void handleProvideErc20TokenInformation(uint8_t p1,
THROW(0x6A80);
#endif
}
tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1;
tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentItemIndex] = 1;
THROW(0x9000);
}
@@ -114,12 +114,12 @@ void handleProvideErc20TokenInformation(uint8_t p1,
uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t tokenKey;
tmpCtx.transactionContext.currentTokenIndex =
(tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN;
tmpCtx.transactionContext.currentItemIndex =
(tmpCtx.transactionContext.currentItemIndex + 1) % MAX_ITEMS;
tokenDefinition_t *token =
&tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex];
&tmpCtx.transactionContext.extraInfo[tmpCtx.transactionContext.currentItemIndex].token;
PRINTF("Provisioning currentTokenIndex %d\n", tmpCtx.transactionContext.currentTokenIndex);
PRINTF("Provisioning currentItemIndex %d\n", tmpCtx.transactionContext.currentItemIndex);
if (dataLength < 1) {
THROW(0x6A80);
@@ -204,7 +204,7 @@ void handleProvideErc20TokenInformation(uint8_t p1,
}
#endif
tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1;
tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentItemIndex] = 1;
THROW(0x9000);
}

View File

@@ -0,0 +1,230 @@
#ifdef HAVE_NFT_SUPPORT
#include "shared_context.h"
#include "apdu_constants.h"
#include "ui_flow.h"
#include "tokens.h"
#include "utils.h"
#define TYPE_SIZE 1
#define VERSION_SIZE 1
#define NAME_LENGTH_SIZE 1
#define HEADER_SIZE TYPE_SIZE + VERSION_SIZE + NAME_LENGTH_SIZE
#define CHAIN_ID_SIZE 8
#define KEY_ID_SIZE 1
#define ALGORITHM_ID_SIZE 1
#define SIGNATURE_LENGTH_SIZE 1
#define MIN_DER_SIG_SIZE 67
#define MAX_DER_SIG_SIZE 72
#define TEST_NFT_METADATA_KEY 0
#define PROD_NFT_METADATA_KEY 1
#define ALGORITHM_ID_1 1
#define TYPE_1 1
#define VERSION_1 1
static const uint8_t LEDGER_NFT_METADATA_PUBLIC_KEY[] = {
#ifdef HAVE_NFT_TESTING_KEY
0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, 0xe3,
0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, 0x30, 0x00,
0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, 0xa5, 0xaa, 0xb0,
0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, 0xdd, 0xa6, 0xec, 0xe9,
0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4
#else
0x04, 0x98, 0x8d, 0xa6, 0xb2, 0x46, 0xf2, 0x8e, 0x77, 0xc1, 0xba, 0xb6, 0x75, 0xcb,
0x2a, 0x27, 0x44, 0xf7, 0xf5, 0xce, 0xc5, 0x6a, 0xe6, 0xe0, 0x32, 0x23, 0x33, 0x7b,
0x57, 0x94, 0xcd, 0x6a, 0xe0, 0x7d, 0x48, 0xb3, 0x0d, 0xb9, 0xcc, 0xb4, 0x0f, 0x5a,
0x02, 0xa1, 0x1a, 0x3a, 0xb9, 0x9d, 0x5f, 0x59, 0x5a, 0x3d, 0x50, 0xa0, 0xe1, 0x30,
0x23, 0xfd, 0x0d, 0x95, 0x87, 0x92, 0xd7, 0x97, 0x01
#endif
};
typedef bool verificationAlgo(const cx_ecfp_public_key_t *,
int,
cx_md_t,
const unsigned char *,
unsigned int,
unsigned char *,
unsigned int);
void handleProvideNFTInformation(uint8_t p1,
uint8_t p2,
uint8_t *workBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx) {
UNUSED(p1);
UNUSED(p2);
UNUSED(tx);
UNUSED(flags);
uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t nftKey;
PRINTF("In handle provide NFTInformation");
tmpCtx.transactionContext.currentItemIndex =
(tmpCtx.transactionContext.currentItemIndex + 1) % MAX_ITEMS;
nftInfo_t *nft =
&tmpCtx.transactionContext.extraInfo[tmpCtx.transactionContext.currentItemIndex].nft;
PRINTF("Provisioning currentItemIndex %d\n", tmpCtx.transactionContext.currentItemIndex);
uint8_t offset = 0;
if (dataLength <= HEADER_SIZE) {
PRINTF("Data too small for headers: expected at least %d, got %d\n",
HEADER_SIZE,
dataLength);
THROW(0x6A80);
}
uint8_t type = workBuffer[offset];
switch (type) {
case TYPE_1:
break;
default:
PRINTF("Unsupported type %d\n", type);
THROW(0x6a80);
break;
}
offset += TYPE_SIZE;
uint8_t version = workBuffer[offset];
switch (version) {
case VERSION_1:
break;
default:
PRINTF("Unsupported version %d\n", version);
THROW(0x6a80);
break;
}
offset += VERSION_SIZE;
uint8_t collectionNameLength = workBuffer[offset];
offset += NAME_LENGTH_SIZE;
// Size of the payload (everything except the signature)
uint8_t payloadSize = HEADER_SIZE + collectionNameLength + ADDRESS_LENGTH + CHAIN_ID_SIZE +
KEY_ID_SIZE + ALGORITHM_ID_SIZE;
if (dataLength < payloadSize) {
PRINTF("Data too small for payload: expected at least %d, got %d\n",
payloadSize,
dataLength);
THROW(0x6A80);
}
if (collectionNameLength + 1 > sizeof(nft->collectionName)) {
PRINTF("CollectionName too big: expected max %d, got %d\n",
sizeof(nft->collectionName),
collectionNameLength + 1);
THROW(0x6A80);
}
// Safe because we've checked the size before.
memcpy(nft->collectionName, workBuffer + offset, collectionNameLength);
nft->collectionName[collectionNameLength] = '\0';
PRINTF("Length: %d\n", collectionNameLength);
PRINTF("CollectionName: %s\n", nft->collectionName);
offset += collectionNameLength;
memcpy(nft->contractAddress, workBuffer + offset, ADDRESS_LENGTH);
PRINTF("Address: %.*H\n", ADDRESS_LENGTH, workBuffer + offset);
offset += ADDRESS_LENGTH;
uint64_t chainId = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE);
// this prints raw data, so to have a more meaningful print, display
// the buffer before the endianness swap
PRINTF("ChainID: %.*H\n", sizeof(chainId), (workBuffer + offset));
if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) {
PRINTF("Chain ID token mismatch\n");
THROW(0x6A80);
}
offset += CHAIN_ID_SIZE;
uint8_t keyId = workBuffer[offset];
uint8_t *rawKey;
uint8_t rawKeyLen;
PRINTF("KeyID: %d\n", keyId);
switch (keyId) {
#ifdef HAVE_NFT_TESTING_KEY
case TEST_NFT_METADATA_KEY:
#endif
case PROD_NFT_METADATA_KEY:
rawKey = (uint8_t *) LEDGER_NFT_METADATA_PUBLIC_KEY;
rawKeyLen = sizeof(LEDGER_NFT_METADATA_PUBLIC_KEY);
break;
default:
PRINTF("KeyID %d not supported\n", keyId);
THROW(0x6A80);
break;
}
PRINTF("RawKey: %.*H\n", rawKeyLen, rawKey);
offset += KEY_ID_SIZE;
uint8_t algorithmId = workBuffer[offset];
PRINTF("Algorithm: %d\n", algorithmId);
cx_curve_t curve;
verificationAlgo *verificationFn;
cx_md_t hashId;
switch (algorithmId) {
case ALGORITHM_ID_1:
curve = CX_CURVE_256K1;
verificationFn = (verificationAlgo *) cx_ecdsa_verify;
hashId = CX_SHA256;
break;
default:
PRINTF("Incorrect algorithmId %d\n", algorithmId);
THROW(0x6a80);
break;
}
offset += ALGORITHM_ID_SIZE;
PRINTF("hashing: %.*H\n", payloadSize, workBuffer);
cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash));
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE) {
PRINTF("Data too short to hold signature length\n");
THROW(0x6a80);
}
uint8_t signatureLen = workBuffer[offset];
PRINTF("Sigature len: %d\n", signatureLen);
if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) {
PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n",
MIN_DER_SIG_SIZE,
MAX_DER_SIG_SIZE,
signatureLen);
THROW(0x6a80);
}
offset += SIGNATURE_LENGTH_SIZE;
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE + signatureLen) {
PRINTF("Signature could not fit in data\n");
THROW(0x6a80);
}
cx_ecfp_init_public_key(curve, rawKey, rawKeyLen, &nftKey);
if (!verificationFn(&nftKey,
CX_LAST,
hashId,
hash,
sizeof(hash),
workBuffer + offset,
signatureLen)) {
#ifndef HAVE_BYPASS_SIGNATURES
PRINTF("Invalid NFT signature\n");
THROW(0x6A80);
#endif
}
tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentItemIndex] = 1;
THROW(0x9000);
}
#endif // HAVE_NFT_SUPPORT

View File

@@ -2,8 +2,8 @@
#include "apdu_constants.h"
#include "ui_flow.h"
#include "tokens.h"
#define SELECTOR_SIZE 4
#include "eth_plugin_interface.h"
#include "eth_plugin_internal.h"
void handleSetExternalPlugin(uint8_t p1,
uint8_t p2,
@@ -14,17 +14,22 @@ void handleSetExternalPlugin(uint8_t p1,
UNUSED(p1);
UNUSED(p2);
UNUSED(flags);
PRINTF("Handling set External Plugin\n");
uint8_t hash[32];
PRINTF("Handling set Plugin\n");
uint8_t hash[INT256_LENGTH];
cx_ecfp_public_key_t tokenKey;
uint8_t pluginNameLength = *workBuffer;
PRINTF("plugin Name Length: %d\n", pluginNameLength);
const size_t payload_size = 1 + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE;
if (dataLength <= payload_size) {
PRINTF("data too small: expected at least %d got %d\n", payload_size, dataLength);
THROW(0x6A80);
}
if (pluginNameLength + 1 > sizeof(dataContext.tokenContext.pluginName)) {
PRINTF("name length too big: expected max %d, got %d\n",
sizeof(dataContext.tokenContext.pluginName),
pluginNameLength + 1);
THROW(0x6A80);
}
@@ -41,8 +46,8 @@ void handleSetExternalPlugin(uint8_t p1,
sizeof(hash),
workBuffer + payload_size,
dataLength - payload_size)) {
PRINTF("Invalid external plugin signature %.*H\n", payload_size, workBuffer);
#ifndef HAVE_BYPASS_SIGNATURES
PRINTF("Invalid plugin signature %.*H\n", payload_size, workBuffer);
THROW(0x6A80);
#endif
}
@@ -77,10 +82,11 @@ void handleSetExternalPlugin(uint8_t p1,
PRINTF("Plugin found\n");
memmove(dataContext.tokenContext.contract_address, workBuffer, ADDRESS_LENGTH);
memmove(dataContext.tokenContext.contractAddress, workBuffer, ADDRESS_LENGTH);
workBuffer += ADDRESS_LENGTH;
memmove(dataContext.tokenContext.method_selector, workBuffer, SELECTOR_SIZE);
externalPluginIsSet = true;
memmove(dataContext.tokenContext.methodSelector, workBuffer, SELECTOR_SIZE);
pluginType = EXTERNAL;
G_io_apdu_buffer[(*tx)++] = 0x90;
G_io_apdu_buffer[(*tx)++] = 0x00;

View File

@@ -0,0 +1,296 @@
#include "shared_context.h"
#include "apdu_constants.h"
#include "ui_flow.h"
#include "tokens.h"
#include "eth_plugin_interface.h"
#include "eth_plugin_internal.h"
#include "utils.h"
// Supported internal plugins
#define ERC721_STR "ERC721"
#define ERC1155_STR "ERC1155"
#define TYPE_SIZE 1
#define VERSION_SIZE 1
#define PLUGIN_NAME_LENGTH_SIZE 1
#define CHAIN_ID_SIZE 8
#define KEY_ID_SIZE 1
#define ALGORITHM_ID_SIZE 1
#define SIGNATURE_LENGTH_SIZE 1
#define HEADER_SIZE TYPE_SIZE + VERSION_SIZE + PLUGIN_NAME_LENGTH_SIZE
#define MIN_DER_SIG_SIZE 67
#define MAX_DER_SIG_SIZE 72
typedef enum Type {
ETH_PLUGIN = 0x01,
} Type;
typedef enum Version {
VERSION_1 = 0x01,
} Version;
typedef enum KeyId {
TEST_PLUGIN_KEY = 0x00,
// Must ONLY be used with ERC721 and ERC1155 plugin
PROD_PLUGIN_KEY = 0x02,
} KeyId;
// Algorithm Id consists of a Key spec and an algorithm spec.
// Format is: KEYSPEC__ALGOSPEC
typedef enum AlgorithmID {
ECC_SECG_P256K1__ECDSA_SHA_256 = 0x01,
} AlgorithmID;
// Only used for signing NFT plugins (ERC721 and ERC1155)
static const uint8_t LEDGER_NFT_SELECTOR_PUBLIC_KEY[] = {
#ifdef HAVE_NFT_TESTING_KEY
0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, 0xe3,
0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, 0x30, 0x00,
0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, 0xa5, 0xaa, 0xb0,
0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, 0xdd, 0xa6, 0xec, 0xe9,
0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4
#else
0x04, 0xd8, 0x62, 0x6e, 0x01, 0x9e, 0x55, 0x3e, 0x19, 0x69, 0x56, 0xf1, 0x17, 0x4d,
0xcd, 0xb8, 0x9a, 0x1c, 0xda, 0xc4, 0x93, 0x90, 0x08, 0xbc, 0x79, 0x77, 0x33, 0x6d,
0x78, 0x24, 0xee, 0xe3, 0xa2, 0x62, 0x24, 0x1a, 0x62, 0x73, 0x52, 0x3b, 0x09, 0xb8,
0xd0, 0xce, 0x0d, 0x39, 0xe8, 0x60, 0xc9, 0x4d, 0x02, 0x53, 0x58, 0xdb, 0xdc, 0x25,
0x92, 0xc7, 0xc6, 0x48, 0x0d, 0x39, 0xce, 0xbb, 0xa3
#endif
};
// Verification function used to verify the signature
typedef bool verificationAlgo(const cx_ecfp_public_key_t *,
int,
cx_md_t,
const unsigned char *,
unsigned int,
unsigned char *,
unsigned int);
// Returns the plugin type of a given plugin name.
// If the plugin name is not a specific known internal plugin, this function default return value is
// `EXERNAL`.
static pluginType_t getPluginType(char *pluginName, uint8_t pluginNameLength) {
if (pluginNameLength == sizeof(ERC721_STR) - 1 &&
strncmp(pluginName, ERC721_STR, pluginNameLength) == 0) {
return ERC721;
} else if (pluginNameLength == sizeof(ERC1155_STR) - 1 &&
strncmp(pluginName, ERC1155_STR, pluginNameLength) == 0) {
return ERC1155;
} else {
return EXTERNAL;
}
}
void handleSetPlugin(uint8_t p1,
uint8_t p2,
uint8_t *workBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx) {
UNUSED(p1);
UNUSED(p2);
UNUSED(flags);
PRINTF("Handling set Plugin\n");
uint8_t hash[INT256_LENGTH] = {0};
cx_ecfp_public_key_t pluginKey = {0};
tokenContext_t *tokenContext = &dataContext.tokenContext;
uint8_t offset = 0;
if (dataLength <= HEADER_SIZE) {
PRINTF("Data too small for headers: expected at least %d, got %d\n",
HEADER_SIZE,
dataLength);
THROW(0x6A80);
}
enum Type type = workBuffer[offset];
PRINTF("Type: %d\n", type);
switch (type) {
case ETH_PLUGIN:
break;
default:
PRINTF("Unsupported type %d\n", type);
THROW(0x6a80);
break;
}
offset += TYPE_SIZE;
uint8_t version = workBuffer[offset];
PRINTF("version: %d\n", version);
switch (version) {
case VERSION_1:
break;
default:
PRINTF("Unsupported version %d\n", version);
THROW(0x6a80);
break;
}
offset += VERSION_SIZE;
uint8_t pluginNameLength = workBuffer[offset];
offset += PLUGIN_NAME_LENGTH_SIZE;
// Size of the payload (everything except the signature)
uint8_t payloadSize = HEADER_SIZE + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE +
CHAIN_ID_SIZE + KEY_ID_SIZE + ALGORITHM_ID_SIZE;
if (dataLength < payloadSize) {
PRINTF("Data too small for payload: expected at least %d, got %d\n",
payloadSize,
dataLength);
THROW(0x6A80);
}
// `+ 1` because we want to add a null terminating character.
if (pluginNameLength + 1 > sizeof(tokenContext->pluginName)) {
PRINTF("plugin name too big: expected max %d, got %d\n",
sizeof(dataContext.tokenContext.pluginName),
pluginNameLength + 1);
THROW(0x6A80);
}
// Safe because we've checked the size before.
memcpy(tokenContext->pluginName, workBuffer + offset, pluginNameLength);
tokenContext->pluginName[pluginNameLength] = '\0';
PRINTF("Length: %d\n", pluginNameLength);
PRINTF("plugin name: %s\n", tokenContext->pluginName);
offset += pluginNameLength;
memcpy(tokenContext->contractAddress, workBuffer + offset, ADDRESS_LENGTH);
PRINTF("Address: %.*H\n", ADDRESS_LENGTH, workBuffer + offset);
offset += ADDRESS_LENGTH;
memcpy(tokenContext->methodSelector, workBuffer + offset, SELECTOR_SIZE);
PRINTF("Selector: %.*H\n", SELECTOR_SIZE, tokenContext->methodSelector);
offset += SELECTOR_SIZE;
uint64_t chainId = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE);
// this prints raw data, so to have a more meaningful print, display
// the buffer before the endianness swap
PRINTF("ChainID: %.*H\n", sizeof(chainId), (workBuffer + offset));
if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) {
PRINTF("Chain ID token mismatch\n");
THROW(0x6A80);
}
offset += CHAIN_ID_SIZE;
enum KeyId keyId = workBuffer[offset];
uint8_t const *rawKey;
uint8_t rawKeyLen;
PRINTF("KeyID: %d\n", keyId);
switch (keyId) {
#ifdef HAVE_NFT_TESTING_KEY
case TEST_PLUGIN_KEY:
#endif
case PROD_PLUGIN_KEY:
rawKey = LEDGER_NFT_SELECTOR_PUBLIC_KEY;
rawKeyLen = sizeof(LEDGER_NFT_SELECTOR_PUBLIC_KEY);
break;
default:
PRINTF("KeyID %d not supported\n", keyId);
THROW(0x6A80);
break;
}
PRINTF("RawKey: %.*H\n", rawKeyLen, rawKey);
offset += KEY_ID_SIZE;
uint8_t algorithmId = workBuffer[offset];
PRINTF("Algorithm: %d\n", algorithmId);
cx_curve_t curve;
verificationAlgo *verificationFn;
cx_md_t hashId;
switch (algorithmId) {
case ECC_SECG_P256K1__ECDSA_SHA_256:
curve = CX_CURVE_256K1;
verificationFn = (verificationAlgo *) cx_ecdsa_verify;
hashId = CX_SHA256;
break;
default:
PRINTF("Incorrect algorithmId %d\n", algorithmId);
THROW(0x6a80);
break;
}
offset += ALGORITHM_ID_SIZE;
PRINTF("hashing: %.*H\n", payloadSize, workBuffer);
cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash));
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE) {
PRINTF("Data too short to hold signature length\n");
THROW(0x6a80);
}
uint8_t signatureLen = workBuffer[offset];
PRINTF("Sigature len: %d\n", signatureLen);
if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) {
PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n",
MIN_DER_SIG_SIZE,
MAX_DER_SIG_SIZE,
signatureLen);
THROW(0x6a80);
}
offset += SIGNATURE_LENGTH_SIZE;
if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE + signatureLen) {
PRINTF("Signature could not fit in data\n");
THROW(0x6a80);
}
cx_ecfp_init_public_key(curve, rawKey, rawKeyLen, &pluginKey);
if (!verificationFn(&pluginKey,
CX_LAST,
hashId,
hash,
sizeof(hash),
workBuffer + offset,
signatureLen)) {
#ifndef HAVE_BYPASS_SIGNATURES
PRINTF("Invalid NFT signature\n");
THROW(0x6A80);
#endif
}
pluginType = getPluginType(tokenContext->pluginName, pluginNameLength);
if (keyId == PROD_PLUGIN_KEY) {
if (pluginType != ERC721 && pluginType != ERC1155) {
PRINTF("AWS key must only be used to set NFT internal plugins\n");
THROW(0x6A80);
}
}
switch (pluginType) {
case EXTERNAL: {
PRINTF("Check external plugin %s\n", tokenContext->pluginName);
// Check if the plugin is present on the device
uint32_t params[2];
params[0] = (uint32_t) tokenContext->pluginName;
params[1] = ETH_PLUGIN_CHECK_PRESENCE;
BEGIN_TRY {
TRY {
os_lib_call(params);
}
CATCH_OTHER(e) {
PRINTF("%s external plugin is not present\n", tokenContext->pluginName);
memset(tokenContext->pluginName, 0, sizeof(tokenContext->pluginName));
THROW(0x6984);
}
FINALLY {
}
}
END_TRY;
break;
}
default:
break;
}
G_io_apdu_buffer[(*tx)++] = 0x90;
G_io_apdu_buffer[(*tx)++] = 0x00;
}

View File

@@ -2,6 +2,7 @@
#include "apdu_constants.h"
#include "ui_flow.h"
#include "feature_signTx.h"
#include "eth_plugin_interface.h"
void handleSign(uint8_t p1,
uint8_t p2,
@@ -12,6 +13,11 @@ void handleSign(uint8_t p1,
UNUSED(tx);
parserStatus_e txResult;
uint32_t i;
if (os_global_pin_is_validated() != BOLOS_UX_OK) {
PRINTF("Device is PIN-locked");
THROW(0x6982);
}
if (p1 == P1_FIRST) {
if (dataLength < 1) {
PRINTF("Invalid data\n");
@@ -53,7 +59,7 @@ void handleSign(uint8_t p1,
workBuffer++;
dataLength--;
} else {
PRINTF("Transaction type not supported\n");
PRINTF("Transaction type %d not supported\n", txType);
THROW(0x6501);
}
} else {

View File

@@ -65,6 +65,7 @@ customStatus_e customProcessor(txContext_t *context) {
PRINTF("pluginstatus %d\n", dataContext.tokenContext.pluginStatus);
eth_plugin_result_t status = dataContext.tokenContext.pluginStatus;
if (status == ETH_PLUGIN_RESULT_ERROR) {
PRINTF("Plugin error\n");
return CUSTOM_FAULT;
} else if (status >= ETH_PLUGIN_RESULT_SUCCESSFUL) {
dataContext.tokenContext.fieldIndex = 0;
@@ -113,6 +114,7 @@ customStatus_e customProcessor(txContext_t *context) {
copySize);
if (context->currentFieldPos == context->currentFieldLength) {
PRINTF("\n\nIncrementing one\n");
context->currentField++;
context->processingField = false;
}
@@ -238,7 +240,6 @@ static void feesToString(uint256_t *rawFee, char *displayBuffer, uint32_t displa
i++;
}
displayBuffer[tickerOffset + i] = '\0';
PRINTF("Displayed fees: %s\n", displayBuffer);
}
// Compute the fees, transform it to a string, prepend a ticker to it and copy everything to
@@ -301,7 +302,6 @@ void finalizeParsing(bool direct) {
// Verify the chain
if (chainConfig->chainId != ETHEREUM_MAINNET_CHAINID) {
// TODO: Could we remove above check?
uint64_t id = get_chain_id();
if (chainConfig->chainId != id) {
@@ -338,24 +338,24 @@ void finalizeParsing(bool direct) {
}
}
// Lookup tokens if requested
ethPluginProvideToken_t pluginProvideToken;
eth_plugin_prepare_provide_token(&pluginProvideToken);
ethPluginProvideInfo_t pluginProvideInfo;
eth_plugin_prepare_provide_info(&pluginProvideInfo);
if ((pluginFinalize.tokenLookup1 != NULL) || (pluginFinalize.tokenLookup2 != NULL)) {
if (pluginFinalize.tokenLookup1 != NULL) {
PRINTF("Lookup1: %.*H\n", ADDRESS_LENGTH, pluginFinalize.tokenLookup1);
pluginProvideToken.token1 = getKnownToken(pluginFinalize.tokenLookup1);
if (pluginProvideToken.token1 != NULL) {
PRINTF("Token1 ticker: %s\n", pluginProvideToken.token1->ticker);
pluginProvideInfo.item1 = getKnownToken(pluginFinalize.tokenLookup1);
if (pluginProvideInfo.item1 != NULL) {
PRINTF("Token1 ticker: %s\n", pluginProvideInfo.item1->token.ticker);
}
}
if (pluginFinalize.tokenLookup2 != NULL) {
PRINTF("Lookup2: %.*H\n", ADDRESS_LENGTH, pluginFinalize.tokenLookup2);
pluginProvideToken.token2 = getKnownToken(pluginFinalize.tokenLookup2);
if (pluginProvideToken.token2 != NULL) {
PRINTF("Token2 ticker: %s\n", pluginProvideToken.token2->ticker);
pluginProvideInfo.item2 = getKnownToken(pluginFinalize.tokenLookup2);
if (pluginProvideInfo.item2 != NULL) {
PRINTF("Token2 ticker: %s\n", pluginProvideInfo.item2->token.ticker);
}
}
if (eth_plugin_call(ETH_PLUGIN_PROVIDE_TOKEN, (void *) &pluginProvideToken) <=
if (eth_plugin_call(ETH_PLUGIN_PROVIDE_INFO, (void *) &pluginProvideInfo) <=
ETH_PLUGIN_RESULT_UNSUCCESSFUL) {
PRINTF("Plugin provide token call failed\n");
reportFinalizeError(direct);
@@ -363,7 +363,7 @@ void finalizeParsing(bool direct) {
return;
}
}
pluginFinalize.result = pluginProvideToken.result;
pluginFinalize.result = pluginProvideInfo.result;
}
if (pluginFinalize.result != ETH_PLUGIN_RESULT_FALLBACK) {
// Handle the right interface
@@ -373,7 +373,7 @@ void finalizeParsing(bool direct) {
// Add the number of screens + the number of additional screens to get the total
// number of screens needed.
dataContext.tokenContext.pluginUiMaxItems =
pluginFinalize.numScreens + pluginProvideToken.additionalScreens;
pluginFinalize.numScreens + pluginProvideInfo.additionalScreens;
break;
case ETH_UI_TYPE_AMOUNT_ADDRESS:
genericUI = true;
@@ -389,9 +389,9 @@ void finalizeParsing(bool direct) {
tmpContent.txContent.value.length = 32;
memmove(tmpContent.txContent.destination, pluginFinalize.address, 20);
tmpContent.txContent.destinationLength = 20;
if (pluginProvideToken.token1 != NULL) {
decimals = pluginProvideToken.token1->decimals;
ticker = pluginProvideToken.token1->ticker;
if (pluginProvideInfo.item1 != NULL) {
decimals = pluginProvideInfo.item1->token.decimals;
ticker = pluginProvideInfo.item1->token.ticker;
}
break;
default:

View File

@@ -37,19 +37,19 @@ void handleStarkwareProvideQuantum(uint8_t p1,
addressZero = allzeroes(dataBuffer, 20);
}
if ((p1 != STARK_QUANTUM_ETH) && !addressZero) {
for (i = 0; i < MAX_TOKEN; i++) {
currentToken = &tmpCtx.transactionContext.tokens[i];
for (i = 0; i < MAX_ITEMS; i++) {
currentToken = &tmpCtx.transactionContext.extraInfo[i].token;
if (tmpCtx.transactionContext.tokenSet[i] &&
(memcmp(currentToken->address, dataBuffer, 20) == 0)) {
break;
}
}
if (i == MAX_TOKEN) {
if (i == MAX_ITEMS) {
PRINTF("Associated token not found\n");
THROW(0x6A80);
}
} else {
i = MAX_TOKEN;
i = MAX_ITEMS;
}
memmove(dataContext.tokenContext.quantum, dataBuffer + 20, 32);
if (p1 != STARK_QUANTUM_LEGACY) {

View File

@@ -85,6 +85,7 @@ void compound_plugin_call(int message, void *parameters) {
// enforce that ETH amount should be 0, except in ceth.mint case
if (!allzeroes(msg->pluginSharedRO->txContent->value.value, 32)) {
if (context->selectorIndex != CETH_MINT) {
PRINTF("Eth amount is not zero and token minted is not CETH!\n");
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
@@ -148,12 +149,12 @@ void compound_plugin_call(int message, void *parameters) {
msg->result = ETH_PLUGIN_RESULT_OK;
} break;
case ETH_PLUGIN_PROVIDE_TOKEN: {
ethPluginProvideToken_t *msg = (ethPluginProvideToken_t *) parameters;
case ETH_PLUGIN_PROVIDE_INFO: {
ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters;
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
PRINTF("compound plugin provide token: %d\n", (msg->token1 != NULL));
if (msg->token1 != NULL) {
strlcpy(context->ticker_1, msg->token1->ticker, MAX_TICKER_LEN);
PRINTF("compound plugin provide token: %d\n", (msg->item1 != NULL));
if (msg->item1 != NULL) {
strlcpy(context->ticker_1, msg->item1->token.ticker, MAX_TICKER_LEN);
switch (context->selectorIndex) {
case COMPOUND_REDEEM_UNDERLYING:
case COMPOUND_MINT:
@@ -166,7 +167,7 @@ void compound_plugin_call(int message, void *parameters) {
// Only case where we use the compound contract decimals
case COMPOUND_REDEEM:
context->decimals = msg->token1->decimals;
context->decimals = msg->item1->token.decimals;
msg->result = ETH_PLUGIN_RESULT_OK;
break;

View File

@@ -0,0 +1,142 @@
#ifdef HAVE_NFT_SUPPORT
#include "erc1155_plugin.h"
#include "eth_plugin_internal.h"
static const uint8_t ERC1155_APPROVE_FOR_ALL_SELECTOR[SELECTOR_SIZE] = {0xa2, 0x2c, 0xb4, 0x65};
static const uint8_t ERC1155_SAFE_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0xf2, 0x42, 0x43, 0x2a};
static const uint8_t ERC1155_SAFE_BATCH_TRANSFER[SELECTOR_SIZE] = {0x2e, 0xb2, 0xc2, 0xd6};
const uint8_t *const ERC1155_SELECTORS[NUM_ERC1155_SELECTORS] = {
ERC1155_APPROVE_FOR_ALL_SELECTOR,
ERC1155_SAFE_TRANSFER_SELECTOR,
ERC1155_SAFE_BATCH_TRANSFER,
};
static void handle_init_contract(void *parameters) {
ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters;
erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext;
uint8_t i;
for (i = 0; i < NUM_ERC1155_SELECTORS; i++) {
if (memcmp((uint8_t *) PIC(ERC1155_SELECTORS[i]), msg->selector, SELECTOR_SIZE) == 0) {
context->selectorIndex = i;
break;
}
}
// No selector found.
if (i == NUM_ERC1155_SELECTORS) {
PRINTF("Unknown erc1155 selector %.*H\n", SELECTOR_SIZE, msg->selector);
msg->result = ETH_PLUGIN_RESULT_FALLBACK;
return;
}
msg->result = ETH_PLUGIN_RESULT_OK;
switch (context->selectorIndex) {
case SAFE_TRANSFER:
case SAFE_BATCH_TRANSFER:
context->next_param = FROM;
break;
case SET_APPROVAL_FOR_ALL:
context->next_param = OPERATOR;
break;
default:
PRINTF("Unsupported selector index: %d\n", context->selectorIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
static void handle_finalize(void *parameters) {
ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters;
erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext;
if (context->selectorIndex != SAFE_BATCH_TRANSFER) {
msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination;
} else {
msg->tokenLookup1 = NULL;
}
msg->tokenLookup2 = NULL;
switch (context->selectorIndex) {
case SAFE_TRANSFER:
msg->numScreens = 5;
break;
case SET_APPROVAL_FOR_ALL:
case SAFE_BATCH_TRANSFER:
msg->numScreens = 4;
break;
default:
msg->result = ETH_PLUGIN_RESULT_ERROR;
return;
}
// Check if some ETH is attached to this tx
if (!allzeroes((void *) &msg->pluginSharedRO->txContent->value,
sizeof(msg->pluginSharedRO->txContent->value))) {
// Those functions are not payable so return an error.
msg->result = ETH_PLUGIN_RESULT_ERROR;
return;
}
msg->uiType = ETH_UI_TYPE_GENERIC;
msg->result = ETH_PLUGIN_RESULT_OK;
}
static void handle_provide_info(void *parameters) {
ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters;
msg->result = ETH_PLUGIN_RESULT_OK;
}
static void handle_query_contract_id(void *parameters) {
ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters;
erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext;
msg->result = ETH_PLUGIN_RESULT_OK;
strlcpy(msg->name, "NFT", msg->nameLength);
switch (context->selectorIndex) {
case SET_APPROVAL_FOR_ALL:
strlcpy(msg->version, "Allowance", msg->versionLength);
break;
case SAFE_TRANSFER:
strlcpy(msg->version, "Transfer", msg->versionLength);
break;
case SAFE_BATCH_TRANSFER:
strlcpy(msg->version, "Batch Transfer", msg->versionLength);
break;
default:
PRINTF("Unsupported selector %d\n", context->selectorIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
void erc1155_plugin_call(int message, void *parameters) {
switch (message) {
case ETH_PLUGIN_INIT_CONTRACT: {
handle_init_contract(parameters);
} break;
case ETH_PLUGIN_PROVIDE_PARAMETER: {
handle_provide_parameter_1155(parameters);
} break;
case ETH_PLUGIN_FINALIZE: {
handle_finalize(parameters);
} break;
case ETH_PLUGIN_PROVIDE_INFO: {
handle_provide_info(parameters);
} break;
case ETH_PLUGIN_QUERY_CONTRACT_ID: {
handle_query_contract_id(parameters);
} break;
case ETH_PLUGIN_QUERY_CONTRACT_UI: {
handle_query_contract_ui_1155(parameters);
} break;
default:
PRINTF("Unhandled message %d\n", message);
break;
}
}
#endif // HAVE_NFT_SUPPORT

View File

@@ -0,0 +1,55 @@
#ifdef HAVE_NFT_SUPPORT
#pragma once
#include <string.h>
#include "eth_plugin_handler.h"
#include "shared_context.h"
#include "ethUtils.h"
#include "utils.h"
#include "uint256.h"
// Internal plugin for EIP 1155: https://eips.ethereum.org/EIPS/eip-1155
#define NUM_ERC1155_SELECTORS 3
typedef enum {
SET_APPROVAL_FOR_ALL,
SAFE_TRANSFER,
SAFE_BATCH_TRANSFER,
} erc1155_selector_t;
typedef enum {
FROM,
TO,
TOKEN_IDS_OFFSET,
TOKEN_IDS_LENGTH,
TOKEN_ID,
VALUE_OFFSET,
VALUE_LENGTH,
VALUE,
OPERATOR,
APPROVED,
NONE,
} erc1155_selector_field;
typedef struct erc1155_context_t {
uint8_t address[ADDRESS_LENGTH];
uint8_t tokenId[INT256_LENGTH];
uint256_t value;
uint16_t ids_array_len;
uint32_t ids_offset;
uint16_t values_array_len;
uint32_t values_offset;
uint16_t array_index;
bool approved;
erc1155_selector_field next_param;
uint8_t selectorIndex;
} erc1155_context_t;
void handle_provide_parameter_1155(void *parameters);
void handle_query_contract_ui_1155(void *parameters);
#endif // HAVE_NFT_SUPPORT

View File

@@ -0,0 +1,147 @@
#ifdef HAVE_NFT_SUPPORT
#include "erc1155_plugin.h"
#include "eth_plugin_internal.h"
static void handle_safe_transfer(ethPluginProvideParameter_t *msg, erc1155_context_t *context) {
uint8_t new_value[INT256_LENGTH];
switch (context->next_param) {
case FROM:
context->next_param = TO;
break;
case TO:
copy_address(context->address, msg->parameter, sizeof(context->address));
context->next_param = TOKEN_ID;
break;
case TOKEN_ID:
copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId));
context->next_param = VALUE;
break;
case VALUE:
copy_parameter(new_value, msg->parameter, sizeof(new_value));
convertUint256BE(new_value, INT256_LENGTH, &context->value);
context->next_param = NONE;
break;
default:
// Some extra data might be present so don't error.
break;
}
}
static void handle_batch_transfer(ethPluginProvideParameter_t *msg, erc1155_context_t *context) {
uint256_t new_value;
switch (context->next_param) {
case FROM:
context->next_param = TO;
break;
case TO:
copy_address(context->address, msg->parameter, sizeof(context->address));
context->next_param = TOKEN_IDS_OFFSET;
break;
case TOKEN_IDS_OFFSET:
context->ids_offset =
U4BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->ids_offset)) + 4;
context->next_param = VALUE_OFFSET;
break;
case VALUE_OFFSET:
context->values_offset =
U4BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->values_offset)) + 4;
context->next_param = TOKEN_IDS_LENGTH;
break;
case TOKEN_IDS_LENGTH:
if ((msg->parameterOffset + PARAMETER_LENGTH) > context->ids_offset) {
context->ids_array_len =
U2BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->ids_array_len));
context->next_param = TOKEN_ID;
// set to zero for next step
context->array_index = 0;
}
break;
case TOKEN_ID:
// don't copy anything since we won't display it
if (--context->ids_array_len == 0) {
context->next_param = VALUE_LENGTH;
}
context->array_index++;
break;
case VALUE_LENGTH:
if ((msg->parameterOffset + PARAMETER_LENGTH) > context->values_offset) {
context->values_array_len =
U2BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->values_array_len));
if (context->values_array_len != context->array_index) {
PRINTF("Token ids and values array sizes mismatch!");
}
context->next_param = VALUE;
// set to zero for next step
context->array_index = 0;
explicit_bzero(&context->value, sizeof(context->value));
}
break;
case VALUE:
// put it temporarily in token id since we don't use it in batch transfer
copy_parameter(context->tokenId, msg->parameter, sizeof(context->value));
convertUint256BE(context->tokenId, sizeof(context->tokenId), &new_value);
add256(&context->value, &new_value, &context->value);
if (--context->values_array_len == 0) {
context->next_param = NONE;
}
context->array_index++;
break;
default:
// Some extra data might be present so don't error.
break;
}
}
static void handle_approval_for_all(ethPluginProvideParameter_t *msg, erc1155_context_t *context) {
switch (context->next_param) {
case OPERATOR:
context->next_param = APPROVED;
copy_address(context->address, msg->parameter, sizeof(context->address));
break;
case APPROVED:
context->approved = msg->parameter[PARAMETER_LENGTH - 1];
context->next_param = NONE;
break;
default:
PRINTF("Param %d not supported\n", context->next_param);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
void handle_provide_parameter_1155(void *parameters) {
ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters;
erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext;
PRINTF("erc1155 plugin provide parameter %d %.*H\n",
msg->parameterOffset,
PARAMETER_LENGTH,
msg->parameter);
msg->result = ETH_PLUGIN_RESULT_SUCCESSFUL;
// if (context->targetOffset > SELECTOR_SIZE &&
// context->targetOffset != msg->parameterOffset - SELECTOR_SIZE) {
// return;
// }
switch (context->selectorIndex) {
case SAFE_TRANSFER:
handle_safe_transfer(msg, context);
break;
case SAFE_BATCH_TRANSFER:
handle_batch_transfer(msg, context);
break;
case SET_APPROVAL_FOR_ALL:
handle_approval_for_all(msg, context);
break;
default:
PRINTF("Selector index %d not supported\n", context->selectorIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
#endif // HAVE_NFT_SUPPORT

View File

@@ -0,0 +1,152 @@
#ifdef HAVE_NFT_SUPPORT
#include "erc1155_plugin.h"
static void set_approval_for_all_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) {
switch (msg->screenIndex) {
case 0:
if (context->approved) {
strlcpy(msg->title, "Allow", msg->titleLength);
} else {
strlcpy(msg->title, "Revoke", msg->titleLength);
}
getEthDisplayableAddress(context->address,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 1:
strlcpy(msg->title, "To Manage ALL", msg->titleLength);
if (msg->item1) {
strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength);
} else {
strlcpy(msg->msg, "Not found", msg->msgLength);
}
break;
case 2:
strlcpy(msg->title, "NFT Address", msg->titleLength);
getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
default:
PRINTF("Unsupported screen index %d\n", msg->screenIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) {
switch (msg->screenIndex) {
case 0:
strlcpy(msg->title, "To", msg->titleLength);
getEthDisplayableAddress(context->address,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 1:
strlcpy(msg->title, "Collection Name", msg->titleLength);
if (msg->item1) {
strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength);
} else {
strlcpy(msg->msg, "Not Found", msg->msgLength);
}
break;
case 2:
strlcpy(msg->title, "NFT Address", msg->titleLength);
getEthDisplayableAddress((uint8_t *) msg->item1->nft.contractAddress,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 3:
strlcpy(msg->title, "NFT ID", msg->titleLength);
uint256_to_decimal(context->tokenId,
sizeof(context->tokenId),
msg->msg,
msg->msgLength);
break;
case 4:
strlcpy(msg->title, "Quantity", msg->titleLength);
tostring256(&context->value, 10, msg->msg, msg->msgLength);
break;
default:
PRINTF("Unsupported screen index %d\n", msg->screenIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
static void set_batch_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) {
char quantity_str[48];
switch (msg->screenIndex) {
case 0:
strlcpy(msg->title, "To", msg->titleLength);
getEthDisplayableAddress(context->address,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 1:
strlcpy(msg->title, "Collection Name", msg->titleLength);
if (msg->item1) {
strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength);
} else {
strlcpy(msg->msg, "Not Found", msg->msgLength);
}
break;
case 2:
strlcpy(msg->title, "NFT Address", msg->titleLength);
getEthDisplayableAddress((uint8_t *) msg->item1->nft.contractAddress,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 3:
strlcpy(msg->title, "Total Quantity", msg->titleLength);
tostring256(&context->value, 10, &quantity_str[0], sizeof(quantity_str));
snprintf(msg->msg,
msg->msgLength,
"%s from %d NFT IDs",
quantity_str,
context->array_index);
break;
default:
PRINTF("Unsupported screen index %d\n", msg->screenIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
void handle_query_contract_ui_1155(void *parameters) {
ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters;
erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext;
msg->result = ETH_PLUGIN_RESULT_OK;
switch (context->selectorIndex) {
case SET_APPROVAL_FOR_ALL:
set_approval_for_all_ui(msg, context);
break;
case SAFE_TRANSFER:
set_transfer_ui(msg, context);
break;
case SAFE_BATCH_TRANSFER:
set_batch_transfer_ui(msg, context);
break;
default:
msg->result = ETH_PLUGIN_RESULT_ERROR;
PRINTF("Unsupported selector index %d\n", context->selectorIndex);
break;
}
}
#endif // HAVE_NFT_SUPPORT

View File

@@ -159,16 +159,16 @@ void erc20_plugin_call(int message, void *parameters) {
}
} break;
case ETH_PLUGIN_PROVIDE_TOKEN: {
ethPluginProvideToken_t *msg = (ethPluginProvideToken_t *) parameters;
case ETH_PLUGIN_PROVIDE_INFO: {
ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters;
erc20_parameters_t *context = (erc20_parameters_t *) msg->pluginContext;
PRINTF("erc20 plugin provide token 1: %d - 2: %d\n",
(msg->token1 != NULL),
(msg->token2 != NULL));
if (msg->token1 != NULL) {
(msg->item1 != NULL),
(msg->item2 != NULL));
if (msg->item1 != NULL) {
context->target = TARGET_ADDRESS;
strlcpy(context->ticker, msg->token1->ticker, MAX_TICKER_LEN);
context->decimals = msg->token1->decimals;
strlcpy(context->ticker, msg->item1->token.ticker, MAX_TICKER_LEN);
context->decimals = msg->item1->token.decimals;
if (context->selectorIndex == ERC20_APPROVE) {
if (check_contract(context)) {
context->target = TARGET_CONTRACT;

View File

@@ -1,151 +1,150 @@
#include <string.h>
#ifdef HAVE_NFT_SUPPORT
#include "erc721_plugin.h"
#include "eth_plugin_internal.h"
#include "eth_plugin_handler.h"
#include "shared_context.h"
#include "ethUtils.h"
#include "utils.h"
typedef struct erc721_parameters_t {
uint8_t selectorIndex;
uint8_t address[ADDRESS_LENGTH];
uint8_t tokenId[INT256_LENGTH];
// tokenDefinition_t *tokenSelf;
// tokenDefinition_t *tokenAddress;
} erc721_parameters_t;
static const uint8_t ERC721_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7, 0xb3};
static const uint8_t ERC721_APPROVE_FOR_ALL_SELECTOR[SELECTOR_SIZE] = {0xa2, 0x2c, 0xb4, 0x65};
static const uint8_t ERC721_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0x23, 0xb8, 0x72, 0xdd};
static const uint8_t ERC721_SAFE_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0x42, 0x84, 0x2e, 0x0e};
static const uint8_t ERC721_SAFE_TRANSFER_DATA_SELECTOR[SELECTOR_SIZE] = {0xb8, 0x8d, 0x4f, 0xde};
bool erc721_plugin_available_check() {
#ifdef HAVE_STARKWARE
if (quantumSet) {
switch (dataContext.tokenContext.quantumType) {
case STARK_QUANTUM_ERC721:
case STARK_QUANTUM_MINTABLE_ERC721:
return true;
default:
return false;
const uint8_t *const ERC721_SELECTORS[NUM_ERC721_SELECTORS] = {
ERC721_APPROVE_SELECTOR,
ERC721_APPROVE_FOR_ALL_SELECTOR,
ERC721_TRANSFER_SELECTOR,
ERC721_SAFE_TRANSFER_SELECTOR,
ERC721_SAFE_TRANSFER_DATA_SELECTOR,
};
static void handle_init_contract(void *parameters) {
ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters;
erc721_context_t *context = (erc721_context_t *) msg->pluginContext;
uint8_t i;
for (i = 0; i < NUM_ERC721_SELECTORS; i++) {
if (memcmp((uint8_t *) PIC(ERC721_SELECTORS[i]), msg->selector, SELECTOR_SIZE) == 0) {
context->selectorIndex = i;
break;
}
}
return false;
#endif
// No selector found.
if (i == NUM_ERC721_SELECTORS) {
PRINTF("Unknown erc721 selector %.*H\n", SELECTOR_SIZE, msg->selector);
msg->result = ETH_PLUGIN_RESULT_FALLBACK;
return;
}
msg->result = ETH_PLUGIN_RESULT_OK;
switch (context->selectorIndex) {
case SET_APPROVAL_FOR_ALL:
case APPROVE:
context->next_param = OPERATOR;
break;
case SAFE_TRANSFER:
case SAFE_TRANSFER_DATA:
case TRANSFER:
context->next_param = FROM;
break;
default:
PRINTF("Unsupported selector index: %d\n", context->selectorIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
static void handle_finalize(void *parameters) {
ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters;
erc721_context_t *context = (erc721_context_t *) msg->pluginContext;
msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination;
msg->tokenLookup2 = NULL;
switch (context->selectorIndex) {
case TRANSFER:
case SAFE_TRANSFER:
case SAFE_TRANSFER_DATA:
case APPROVE:
msg->numScreens = 4;
break;
case SET_APPROVAL_FOR_ALL:
msg->numScreens = 3;
break;
default:
msg->result = ETH_PLUGIN_RESULT_ERROR;
return;
}
// Check if some ETH is attached to this tx
if (!allzeroes((void *) &msg->pluginSharedRO->txContent->value,
sizeof(msg->pluginSharedRO->txContent->value))) {
// Set Approval for All is not payable
if (context->selectorIndex == SET_APPROVAL_FOR_ALL) {
msg->result = ETH_PLUGIN_RESULT_ERROR;
return;
} else {
// Add an additional screen
msg->numScreens++;
}
}
msg->uiType = ETH_UI_TYPE_GENERIC;
msg->result = ETH_PLUGIN_RESULT_OK;
}
static void handle_provide_info(void *parameters) {
ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters;
msg->result = ETH_PLUGIN_RESULT_OK;
}
static void handle_query_contract_id(void *parameters) {
ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters;
erc721_context_t *context = (erc721_context_t *) msg->pluginContext;
msg->result = ETH_PLUGIN_RESULT_OK;
strlcpy(msg->name, "NFT", msg->nameLength);
switch (context->selectorIndex) {
case SET_APPROVAL_FOR_ALL:
case APPROVE:
strlcpy(msg->version, "Allowance", msg->versionLength);
break;
case SAFE_TRANSFER:
case SAFE_TRANSFER_DATA:
case TRANSFER:
strlcpy(msg->version, "Transfer", msg->versionLength);
break;
default:
PRINTF("Unsupported selector %d\n", context->selectorIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
void erc721_plugin_call(int message, void *parameters) {
switch (message) {
case ETH_PLUGIN_INIT_CONTRACT: {
ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters;
erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext;
// enforce that ETH amount should be 0
if (!allzeroes(msg->pluginSharedRO->txContent->value.value, 32)) {
PRINTF("Err: Transaction amount is not 0 for erc721 approval\n");
msg->result = ETH_PLUGIN_RESULT_ERROR;
} else {
size_t i;
for (i = 0; i < NUM_ERC721_SELECTORS; i++) {
if (memcmp((uint8_t *) PIC(ERC721_SELECTORS[i]),
msg->selector,
SELECTOR_SIZE) == 0) {
context->selectorIndex = i;
break;
}
}
if (i == NUM_ERC721_SELECTORS) {
PRINTF("Unknown erc721 selector %.*H\n", SELECTOR_SIZE, msg->selector);
break;
}
if (msg->dataSize != 4 + 32 + 32) {
PRINTF("Invalid erc721 approval data size %d\n", msg->dataSize);
break;
}
PRINTF("erc721 plugin init\n");
msg->result = ETH_PLUGIN_RESULT_OK;
}
handle_init_contract(parameters);
} break;
case ETH_PLUGIN_PROVIDE_PARAMETER: {
ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters;
erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext;
PRINTF("erc721 plugin provide parameter %d %.*H\n",
msg->parameterOffset,
32,
msg->parameter);
switch (msg->parameterOffset) {
case 4:
memmove(context->address, msg->parameter + 32 - 20, 20);
msg->result = ETH_PLUGIN_RESULT_OK;
break;
case 4 + 32:
memmove(context->tokenId, msg->parameter, 32);
msg->result = ETH_PLUGIN_RESULT_OK;
break;
default:
PRINTF("Unhandled parameter offset\n");
break;
}
handle_provide_parameter_721(parameters);
} break;
case ETH_PLUGIN_FINALIZE: {
ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters;
erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext;
PRINTF("erc721 plugin finalize\n");
msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination;
msg->tokenLookup2 = context->address;
msg->numScreens = 3;
msg->uiType = ETH_UI_TYPE_GENERIC;
msg->result = ETH_PLUGIN_RESULT_OK;
handle_finalize(parameters);
} break;
case ETH_PLUGIN_PROVIDE_TOKEN: {
ethPluginProvideToken_t *msg = (ethPluginProvideToken_t *) parameters;
PRINTF("erc721 plugin provide token dest: %d - address: %d\n",
(msg->token1 != NULL),
(msg->token2 != NULL));
// context->tokenSelf = msg->token1;
// context->tokenAddress = msg->token2;
msg->result = ETH_PLUGIN_RESULT_OK;
case ETH_PLUGIN_PROVIDE_INFO: {
handle_provide_info(parameters);
} break;
case ETH_PLUGIN_QUERY_CONTRACT_ID: {
ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters;
strlcpy(msg->name, "Allowance", msg->nameLength);
strlcpy(msg->version, "", msg->versionLength);
msg->result = ETH_PLUGIN_RESULT_OK;
handle_query_contract_id(parameters);
} break;
case ETH_PLUGIN_QUERY_CONTRACT_UI: {
ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters;
erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext;
switch (msg->screenIndex) {
case 0:
strlcpy(msg->title, "Contract Name", msg->titleLength);
getEthDisplayableAddress(tmpContent.txContent.destination,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
msg->result = ETH_PLUGIN_RESULT_OK;
break;
case 1:
strlcpy(msg->title, "NFT Contract", msg->titleLength);
getEthDisplayableAddress(context->address,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
msg->result = ETH_PLUGIN_RESULT_OK;
break;
case 2:
strlcpy(msg->title, "TokenID", msg->titleLength);
snprintf(msg->msg, 70, "0x%.*H", 32, context->tokenId);
msg->result = ETH_PLUGIN_RESULT_OK;
break;
default:
break;
}
handle_query_contract_ui_721(parameters);
} break;
default:
PRINTF("Unhandled message %d\n", message);
break;
}
}
#endif // HAVE_NFT_SUPPORT

View File

@@ -0,0 +1,46 @@
#ifdef HAVE_NFT_SUPPORT
#pragma once
#include <string.h>
#include "eth_plugin_handler.h"
#include "shared_context.h"
#include "ethUtils.h"
#include "utils.h"
// Internal plugin for EIP 721: https://eips.ethereum.org/EIPS/eip-721
#define NUM_ERC721_SELECTORS 5
typedef enum {
APPROVE,
SET_APPROVAL_FOR_ALL,
TRANSFER,
SAFE_TRANSFER,
SAFE_TRANSFER_DATA,
} erc721_selector_t;
typedef enum {
FROM,
TO,
DATA,
TOKEN_ID,
OPERATOR,
APPROVED,
NONE,
} erc721_selector_field;
typedef struct erc721_context_t {
uint8_t address[ADDRESS_LENGTH];
uint8_t tokenId[INT256_LENGTH];
bool approved;
erc721_selector_field next_param;
uint8_t selectorIndex;
} erc721_context_t;
void handle_provide_parameter_721(void *parameters);
void handle_query_contract_ui_721(void *parameters);
#endif // HAVE_NFT_SUPPORT

View File

@@ -0,0 +1,97 @@
#ifdef HAVE_NFT_SUPPORT
#include "erc721_plugin.h"
#include "eth_plugin_internal.h"
static void handle_approve(ethPluginProvideParameter_t *msg, erc721_context_t *context) {
switch (context->next_param) {
case OPERATOR:
copy_address(context->address, msg->parameter, sizeof(context->address));
context->next_param = TOKEN_ID;
break;
case TOKEN_ID:
copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId));
context->next_param = NONE;
break;
default:
PRINTF("Unhandled parameter offset\n");
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
// `strict` will set msg->result to ERROR if parsing continues after `TOKEN_ID` has been parsed.
static void handle_transfer(ethPluginProvideParameter_t *msg,
erc721_context_t *context,
bool strict) {
switch (context->next_param) {
case FROM:
context->next_param = TO;
break;
case TO:
copy_address(context->address, msg->parameter, sizeof(context->address));
context->next_param = TOKEN_ID;
break;
case TOKEN_ID:
copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId));
context->next_param = NONE;
break;
default:
if (strict) {
PRINTF("Param %d not supported\n", context->next_param);
msg->result = ETH_PLUGIN_RESULT_ERROR;
}
break;
}
}
static void handle_approval_for_all(ethPluginProvideParameter_t *msg, erc721_context_t *context) {
switch (context->next_param) {
case OPERATOR:
context->next_param = APPROVED;
copy_address(context->address, msg->parameter, sizeof(context->address));
break;
case APPROVED:
context->next_param = NONE;
context->approved = msg->parameter[PARAMETER_LENGTH - 1];
break;
default:
PRINTF("Param %d not supported\n", context->next_param);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
void handle_provide_parameter_721(void *parameters) {
ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters;
erc721_context_t *context = (erc721_context_t *) msg->pluginContext;
PRINTF("erc721 plugin provide parameter %d %.*H\n",
msg->parameterOffset,
PARAMETER_LENGTH,
msg->parameter);
msg->result = ETH_PLUGIN_RESULT_SUCCESSFUL;
switch (context->selectorIndex) {
case APPROVE:
handle_approve(msg, context);
break;
case SAFE_TRANSFER:
case TRANSFER:
handle_transfer(msg, context, true);
break;
case SAFE_TRANSFER_DATA:
// Set `strict` to `false` because additional data might be present.
handle_transfer(msg, context, false);
break;
case SET_APPROVAL_FOR_ALL:
handle_approval_for_all(msg, context);
break;
default:
PRINTF("Selector index %d not supported\n", context->selectorIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
#endif // HAVE_NFT_SUPPORT

View File

@@ -0,0 +1,170 @@
#ifdef HAVE_NFT_SUPPORT
#include "erc721_plugin.h"
static void set_approval_ui(ethQueryContractUI_t *msg, erc721_context_t *context) {
switch (msg->screenIndex) {
case 0:
strlcpy(msg->title, "Allow", msg->titleLength);
getEthDisplayableAddress(context->address,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 1:
strlcpy(msg->title, "To Spend Your", msg->titleLength);
if (msg->item1) {
strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength);
} else {
strlcpy(msg->msg, "Not found", msg->msgLength);
}
break;
case 2:
strlcpy(msg->title, "NFT Address", msg->titleLength);
getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 3:
strlcpy(msg->title, "NFT ID", msg->titleLength);
uint256_to_decimal(context->tokenId,
sizeof(context->tokenId),
msg->msg,
msg->msgLength);
break;
case 4:
strlcpy(msg->title, "And send", msg->titleLength);
amountToString((uint8_t *) &msg->pluginSharedRO->txContent->value,
sizeof(msg->pluginSharedRO->txContent->value),
WEI_TO_ETHER,
msg->network_ticker,
msg->msg,
msg->msgLength);
default:
PRINTF("Unsupported screen index %d\n", msg->screenIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
static void set_approval_for_all_ui(ethQueryContractUI_t *msg, erc721_context_t *context) {
switch (msg->screenIndex) {
case 0:
if (context->approved) {
strlcpy(msg->title, "Allow", msg->titleLength);
} else {
strlcpy(msg->title, "Revoke", msg->titleLength);
}
getEthDisplayableAddress(context->address,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 1:
strlcpy(msg->title, "To Manage ALL", msg->titleLength);
if (msg->item1) {
strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength);
} else {
strlcpy(msg->msg, "Not found", msg->msgLength);
}
break;
case 2:
strlcpy(msg->title, "NFT Address", msg->titleLength);
getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 3:
strlcpy(msg->title, "And send", msg->titleLength);
amountToString((uint8_t *) &msg->pluginSharedRO->txContent->value,
sizeof(msg->pluginSharedRO->txContent->value),
WEI_TO_ETHER,
msg->network_ticker,
msg->msg,
msg->msgLength);
default:
PRINTF("Unsupported screen index %d\n", msg->screenIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
static void set_transfer_ui(ethQueryContractUI_t *msg, erc721_context_t *context) {
switch (msg->screenIndex) {
case 0:
strlcpy(msg->title, "To", msg->titleLength);
getEthDisplayableAddress(context->address,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 1:
strlcpy(msg->title, "Collection Name", msg->titleLength);
if (msg->item1) {
strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength);
} else {
strlcpy(msg->msg, "Not Found", msg->msgLength);
}
break;
case 2:
strlcpy(msg->title, "NFT Address", msg->titleLength);
getEthDisplayableAddress((uint8_t *) msg->item1->nft.contractAddress,
msg->msg,
msg->msgLength,
&global_sha3,
chainConfig->chainId);
break;
case 3:
strlcpy(msg->title, "NFT ID", msg->titleLength);
uint256_to_decimal(context->tokenId,
sizeof(context->tokenId),
msg->msg,
msg->msgLength);
break;
case 4:
strlcpy(msg->title, "And send", msg->titleLength);
amountToString((uint8_t *) &msg->pluginSharedRO->txContent->value,
sizeof(msg->pluginSharedRO->txContent->value),
WEI_TO_ETHER,
msg->network_ticker,
msg->msg,
msg->msgLength);
default:
PRINTF("Unsupported screen index %d\n", msg->screenIndex);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
void handle_query_contract_ui_721(void *parameters) {
ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters;
erc721_context_t *context = (erc721_context_t *) msg->pluginContext;
msg->result = ETH_PLUGIN_RESULT_OK;
switch (context->selectorIndex) {
case APPROVE:
set_approval_ui(msg, context);
break;
case SET_APPROVAL_FOR_ALL:
set_approval_for_all_ui(msg, context);
break;
case SAFE_TRANSFER_DATA:
case SAFE_TRANSFER:
case TRANSFER:
set_transfer_ui(msg, context);
break;
default:
msg->result = ETH_PLUGIN_RESULT_ERROR;
PRINTF("Unsupported selector index %d\n", context->selectorIndex);
break;
}
}
#endif // HAVE_NFT_SUPPORT

View File

@@ -188,8 +188,9 @@ bool starkware_verify_asset_id(uint8_t *tmp32, uint8_t *tokenId, bool assetTypeO
if (quantumSet) {
cx_sha3_t sha3;
tokenDefinition_t *currentToken = NULL;
if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) {
currentToken = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
currentToken =
&tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
}
cx_keccak_init(&sha3, 256);
compute_token_id(&sha3,
@@ -214,9 +215,9 @@ bool starkware_verify_asset_id(uint8_t *tmp32, uint8_t *tokenId, bool assetTypeO
bool starkware_verify_token(uint8_t *token) {
if (quantumSet) {
if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) {
if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
tokenDefinition_t *currentToken =
&tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
&tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
if (memcmp(token + 32 - 20, currentToken->address, 20) != 0) {
PRINTF("Token not matching got %.*H\n", 20, token + 32 - 20);
PRINTF("Current token %.*H\n", 20, currentToken->address);
@@ -235,7 +236,7 @@ bool starkware_verify_token(uint8_t *token) {
bool starkware_verify_quantum(uint8_t *quantum) {
if (quantumSet) {
if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) {
if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
if (memcmp(quantum, dataContext.tokenContext.quantum, 32) != 0) {
PRINTF("Quantum not matching got %.*H\n", 32, quantum);
PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum);
@@ -301,7 +302,7 @@ void starkware_print_amount(uint8_t *amountData,
char *ticker = chainConfig->coinName;
if ((amountData == NULL) ||
(forEscape && (dataContext.tokenContext.quantumIndex == MAX_TOKEN))) {
(forEscape && (dataContext.tokenContext.quantumIndex == MAX_ITEMS))) {
decimals = WEI_TO_ETHER;
if (!forEscape) {
convertUint256BE(tmpContent.txContent.value.value,
@@ -312,7 +313,7 @@ void starkware_print_amount(uint8_t *amountData,
}
} else {
tokenDefinition_t *token =
&tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
&tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
decimals = token->decimals;
ticker = token->ticker;
readu256BE(amountData, &amountPre);
@@ -334,9 +335,9 @@ void starkware_print_amount(uint8_t *amountData,
void starkware_print_ticker(char *destination, size_t destinationLength) {
char *ticker = chainConfig->coinName;
if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) {
if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
tokenDefinition_t *token =
&tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
&tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
ticker = token->ticker;
}
strlcpy(destination, ticker, destinationLength);
@@ -345,9 +346,9 @@ void starkware_print_ticker(char *destination, size_t destinationLength) {
// TODO : rewrite as independant code
void starkware_print_asset_contract(char *destination, size_t destinationLength) {
// token has been validated to be present previously
if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) {
if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) {
tokenDefinition_t *token =
&tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex];
&tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token;
getEthDisplayableAddress(token->address,
destination,
destinationLength,

View File

@@ -1,34 +1,44 @@
#!/bin/bash
#!/usr/bin/env bash
TESTS_FULL_PATH=$(dirname "$(realpath "$0")")
# FILL THESE WITH YOUR OWN SDKs PATHS
# NANOS_SDK=
# NANOX_SDK=
# list of apps required by tests that we want to build here
appnames=("ethereum" "ethereum_classic")
APPS=("ethereum" "ethereum_classic")
# create elfs folder if it doesn't exist
# list of SDKS
NANO_SDKS=("$NANOS_SDK" "$NANOX_SDK")
# list of target elf file name suffix
FILE_SUFFIXES=("nanos" "nanox")
# move to the tests directory
cd "$TESTS_FULL_PATH" || exit 1
# Do it only now since before the cd command, we might not have been inside the repository
GIT_REPO_ROOT=$(git rev-parse --show-toplevel)
TESTS_REL_PATH=$(realpath --relative-to="$GIT_REPO_ROOT" "$TESTS_FULL_PATH")
# create elfs directory if it doesn't exist
mkdir -p elfs
# move to repo's root to build apps
cd ..
cd "$GIT_REPO_ROOT" || exit 1
echo "*Building elfs for Nano S..."
for app in "${appnames[@]}"
for ((sdk_idx=0; sdk_idx < "${#NANO_SDKS[@]}"; sdk_idx++))
do
echo "**Building $app for Nano S..."
make clean BOLOS_SDK=$NANOS_SDK
make -j DEBUG=1 ALLOW_DATA=1 BOLOS_SDK=$NANOS_SDK CHAIN=$app
cp bin/app.elf "tests/elfs/${app}_nanos.elf"
done
echo "*Building elfs for Nano X..."
for app in "${appnames[@]}"
do
echo "**Building $app for Nano X..."
make clean BOLOS_SDK=$NANOX_SDK
make -j DEBUG=1 ALLOW_DATA=1 BOLOS_SDK=$NANOX_SDK CHAIN=$app
cp bin/app.elf "tests/elfs/${app}_nanox.elf"
nano_sdk="${NANO_SDKS[$sdk_idx]}"
elf_suffix="${FILE_SUFFIXES[$sdk_idx]}"
echo "* Building elfs for $(basename "$nano_sdk")..."
for appname in "${APPS[@]}"
do
echo "** Building app $appname..."
make clean BOLOS_SDK="$nano_sdk"
make -j DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK="$nano_sdk" CHAIN="$appname"
cp bin/app.elf "$TESTS_REL_PATH/elfs/${appname}_${elf_suffix}.elf"
done
done
echo "done"

View File

@@ -20,6 +20,7 @@
"bip32-path": "^0.4.2",
"core-js": "^3.7.0",
"ethereum-tx-decoder": "^3.0.0",
"ethers": "^5.5.1",
"fs-extra": "^10.0.0",
"google-protobuf": "^3.11.0",
"jest-serial-runner": "^1.1.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Some files were not shown because too many files have changed in this diff Show More