diff --git a/Makefile b/Makefile index 7b70775..31568e0 100755 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ APP_LOAD_PARAMS += --path "45'" #APP_LOAD_PARAMS += --path "1517992542'/1101353413'" APPVERSION_M=1 -APPVERSION_N=4 +APPVERSION_N=5 APPVERSION_P=0 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION) @@ -292,7 +292,7 @@ LDLIBS += -lm -lgcc -lc include $(BOLOS_SDK)/Makefile.glyphs ### variables processed by the common makefile.rules of the SDK to grab source files and include dirs -APP_SOURCE_PATH += src_common src src_features +APP_SOURCE_PATH += src_common src src_features src_plugins SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_u2f SDK_SOURCE_PATH += lib_ux ifeq ($(TARGET_NAME),TARGET_NANOX) diff --git a/examples/signTx.py b/examples/signTx.py index 4366b71..2f4d3d2 100755 --- a/examples/signTx.py +++ b/examples/signTx.py @@ -59,6 +59,7 @@ parser.add_argument('--amount', help="Amount to send in ether", required=True) parser.add_argument('--to', help="Destination address", type=str, required=True) parser.add_argument('--path', help="BIP 32 path to sign with") parser.add_argument('--data', help="Data to add, hex encoded") +parser.add_argument('--descriptor', help="Optional descriptor") args = parser.parse_args() if args.path == None: @@ -85,13 +86,19 @@ tx = Transaction( encodedTx = encode(tx, Transaction) +dongle = getDongle(True) + +if args.descriptor != None: + descriptor = binascii.unhexlify(args.descriptor) + apdu = struct.pack(">BBBBB", 0xE0, 0x0A, 0x00, 0x00, len(descriptor)) + descriptor + dongle.exchange(bytes(apdu)) + donglePath = parse_bip32_path(args.path) apdu = bytearray.fromhex("e0040000") apdu.append(len(donglePath) + 1 + len(encodedTx)) apdu.append(len(donglePath) // 4) apdu += donglePath + encodedTx -dongle = getDongle(True) result = dongle.exchange(bytes(apdu)) # Needs to recover (main.c:1121) diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..78bf86a --- /dev/null +++ b/src/base64.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* Base64 encoder/decoder. Originally Apache file ap_base64.c + */ + +#include + +#include "base64.h" + +#if 0 + +/* aaaack but it's fast and const should make it shared text page. */ +static const unsigned char pr2six[256] = +{ + /* ASCII table */ + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 +}; + +int Base64decode_len(const char *bufcoded) +{ + int nbytesdecoded; + register const unsigned char *bufin; + register int nprbytes; + + bufin = (const unsigned char *) bufcoded; + while (pr2six[*(bufin++)] <= 63); + + nprbytes = (bufin - (const unsigned char *) bufcoded) - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + + return nbytesdecoded + 1; +} + +int Base64decode(char *bufplain, const char *bufcoded) +{ + int nbytesdecoded; + register const unsigned char *bufin; + register unsigned char *bufout; + register int nprbytes; + + bufin = (const unsigned char *) bufcoded; + while (pr2six[*(bufin++)] <= 63); + nprbytes = (bufin - (const unsigned char *) bufcoded) - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + + bufout = (unsigned char *) bufplain; + bufin = (const unsigned char *) bufcoded; + + while (nprbytes > 4) { + *(bufout++) = + (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + *(bufout++) = + (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + *(bufout++) = + (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + bufin += 4; + nprbytes -= 4; + } + + /* Note: (nprbytes == 1) would be an error, so just ingore that case */ + if (nprbytes > 1) { + *(bufout++) = + (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + } + if (nprbytes > 2) { + *(bufout++) = + (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + } + if (nprbytes > 3) { + *(bufout++) = + (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + } + + *(bufout++) = '\0'; + nbytesdecoded -= (4 - nprbytes) & 3; + return nbytesdecoded; +} + +#endif + +static const char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int Base64encode_len(int len) +{ + return ((len + 2) / 3 * 4) + 1; +} + +int Base64encode(char *encoded, const char *string, int len) +{ + int i; + char *p; + + p = encoded; + for (i = 0; i < len - 2; i += 3) { + *p++ = basis_64[(string[i] >> 2) & 0x3F]; + *p++ = basis_64[((string[i] & 0x3) << 4) | + ((int) (string[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((string[i + 1] & 0xF) << 2) | + ((int) (string[i + 2] & 0xC0) >> 6)]; + *p++ = basis_64[string[i + 2] & 0x3F]; + } + if (i < len) { + *p++ = basis_64[(string[i] >> 2) & 0x3F]; + if (i == (len - 1)) { + *p++ = basis_64[((string[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = basis_64[((string[i] & 0x3) << 4) | + ((int) (string[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((string[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + *p++ = '\0'; + return p - encoded; +} diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..c69b93b --- /dev/null +++ b/src/base64.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + +#ifndef _BASE64_H_ +#define _BASE64_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int Base64encode_len(int len); +int Base64encode(char * coded_dst, const char *plain_src,int len_plain_src); + +#if 0 + +int Base64decode_len(const char * coded_src); +int Base64decode(char * plain_dst, const char *coded_src); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif //_BASE64_H_ diff --git a/src/eth_plugin_handler.c b/src/eth_plugin_handler.c new file mode 100644 index 0000000..4b9da53 --- /dev/null +++ b/src/eth_plugin_handler.c @@ -0,0 +1,250 @@ +#include +#include "eth_plugin_handler.h" +#include "eth_plugin_internal.h" +#include "shared_context.h" +#include "base64.h" + +void eth_plugin_prepare_init(ethPluginInitContract_t *init, uint8_t *selector, uint32_t dataSize) { + memset((uint8_t*)init, 0, sizeof(ethPluginInitContract_t)); + init->selector = selector; + init->dataSize = dataSize; +} + +void eth_plugin_prepare_provide_parameter(ethPluginProvideParameter_t *provideParameter, uint8_t *parameter, uint32_t parameterOffset) { + memset((uint8_t*)provideParameter, 0, sizeof(ethPluginProvideParameter_t)); + provideParameter->parameter = parameter; + provideParameter->parameterOffset = parameterOffset; +} + +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, tokenDefinition_t *token1, tokenDefinition_t *token2) { + memset((uint8_t*)provideToken, 0, sizeof(ethPluginProvideToken_t)); + provideToken->token1 = token1; + provideToken->token2 = token2; +} + +void eth_plugin_prepare_query_contract_ID(ethQueryContractID_t *queryContractID, char *name, uint32_t nameLength, char *version, uint32_t versionLength) { + memset((uint8_t*)queryContractID, 0, sizeof(ethQueryContractID_t)); + queryContractID->name = name; + queryContractID->nameLength = nameLength; + queryContractID->version = version; + queryContractID->versionLength = versionLength; +} + +void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI, uint8_t screenIndex, char *title, uint32_t titleLength, char *msg, uint32_t msgLength) { + memset((uint8_t*)queryContractUI, 0, sizeof(ethQueryContractUI_t)); + queryContractUI->screenIndex = screenIndex; + queryContractUI->title = title; + queryContractUI->titleLength = titleLength; + queryContractUI->msg = msg; + queryContractUI->msgLength = msgLength; +} + +int eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *init) { + uint8_t i; + dataContext.tokenContext.pluginAvailable = 0; + // Handle hardcoded plugin list + PRINTF("Selector %.*H\n", 4, init->selector); + for (i=0; iselector, PIC(selectors[j]), SELECTOR_SIZE) == 0) { + strcpy(dataContext.tokenContext.pluginName, INTERNAL_ETH_PLUGINS[i].alias); + dataContext.tokenContext.pluginAvailable = 1; + contractAddress = NULL; + break; + } + } + } + +#ifndef TARGET_BLUE + + // 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"); + return 0; + } + for (;;) { + PRINTF("eth_plugin_init\n"); + if (contractAddress != NULL) { + PRINTF("Trying address %.*H\n", 20, contractAddress); + } + else { + PRINTF("Trying alias %s\n", dataContext.tokenContext.pluginName); + } + int status = eth_plugin_call(contractAddress, ETH_PLUGIN_INIT_CONTRACT, (void*)init); + if (!status) { + return 0; + } + if (status == ETH_PLUGIN_RESULT_OK) { + break; + } + if (status == ETH_PLUGIN_RESULT_OK_ALIAS) { + contractAddress = NULL; + } + } + PRINTF("eth_plugin_init ok %s\n", dataContext.tokenContext.pluginName); + dataContext.tokenContext.pluginAvailable = 1; + return 1; + +#else + + // Disable plugins on Ledger Blue for the time being + + return 0; +#endif + +} + +int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) { + ethPluginSharedRW_t pluginRW; + ethPluginSharedRO_t pluginRO; + char tmp[PLUGIN_ID_LENGTH]; + char *alias; + uint8_t i; + + pluginRW.sha3 = &global_sha3; + pluginRO.txContent = &tmpContent.txContent; + + if (contractAddress == NULL) { + if (!dataContext.tokenContext.pluginAvailable) { + PRINTF("Cached plugin call but no plugin available\n"); + return 0; + } + alias = dataContext.tokenContext.pluginName; + } + else { + Base64encode(tmp, (char*)contractAddress, 20); + alias = tmp; + } + + // Prepare the call + + switch(method) { + case ETH_PLUGIN_INIT_CONTRACT: + ((ethPluginInitContract_t*)parameter)->pluginSharedRW = &pluginRW; + ((ethPluginInitContract_t*)parameter)->pluginSharedRO = &pluginRO; + ((ethPluginInitContract_t*)parameter)->pluginContext = (uint8_t*)&dataContext.tokenContext.pluginContext; + ((ethPluginInitContract_t*)parameter)->pluginContextLength = sizeof(dataContext.tokenContext.pluginContext); + ((ethPluginInitContract_t*)parameter)->alias = dataContext.tokenContext.pluginName; + break; + case ETH_PLUGIN_PROVIDE_PARAMETER: + ((ethPluginProvideParameter_t*)parameter)->pluginSharedRW = &pluginRW; + ((ethPluginProvideParameter_t*)parameter)->pluginSharedRO = &pluginRO; + ((ethPluginProvideParameter_t*)parameter)->pluginContext = (uint8_t*)&dataContext.tokenContext.pluginContext; + break; + case ETH_PLUGIN_FINALIZE: + ((ethPluginFinalize_t*)parameter)->pluginSharedRW = &pluginRW; + ((ethPluginFinalize_t*)parameter)->pluginSharedRO = &pluginRO; + ((ethPluginFinalize_t*)parameter)->pluginContext = (uint8_t*)&dataContext.tokenContext.pluginContext; + break; + case ETH_PLUGIN_PROVIDE_TOKEN: + ((ethPluginProvideToken_t*)parameter)->pluginSharedRW = &pluginRW; + ((ethPluginProvideToken_t*)parameter)->pluginSharedRO = &pluginRO; + ((ethPluginProvideToken_t*)parameter)->pluginContext = (uint8_t*)&dataContext.tokenContext.pluginContext; + break; + case ETH_PLUGIN_QUERY_CONTRACT_ID: + ((ethQueryContractID_t*)parameter)->pluginSharedRW = &pluginRW; + ((ethQueryContractID_t*)parameter)->pluginSharedRO = &pluginRO; + ((ethQueryContractID_t*)parameter)->pluginContext = (uint8_t*)&dataContext.tokenContext.pluginContext; + break; + case ETH_PLUGIN_QUERY_CONTRACT_UI: + ((ethQueryContractUI_t*)parameter)->pluginSharedRW = &pluginRW; + ((ethQueryContractUI_t*)parameter)->pluginSharedRO = &pluginRO; + ((ethQueryContractUI_t*)parameter)->pluginContext = (uint8_t*)&dataContext.tokenContext.pluginContext; + break; + default: + PRINTF("Unknown plugin method %d\n", method); + return 0; + } + + // Perform the call + + for (i=0; iresult) { + case ETH_PLUGIN_RESULT_OK: + if (contractAddress != NULL) { + strcpy(dataContext.tokenContext.pluginName, alias); + } + break; + case ETH_PLUGIN_RESULT_OK_ALIAS: + break; + default: + return 0; + } + break; + case ETH_PLUGIN_PROVIDE_PARAMETER: + switch (((ethPluginProvideParameter_t*)parameter)->result) { + case ETH_PLUGIN_RESULT_OK: + case ETH_PLUGIN_RESULT_FALLBACK: + break; + default: + return 0; + } + break; + case ETH_PLUGIN_FINALIZE: + switch (((ethPluginFinalize_t*)parameter)->result) { + case ETH_PLUGIN_RESULT_OK: + case ETH_PLUGIN_RESULT_FALLBACK: + break; + default: + return 0; + } + break; + case ETH_PLUGIN_PROVIDE_TOKEN: + switch (((ethPluginProvideToken_t*)parameter)->result) { + case ETH_PLUGIN_RESULT_OK: + case ETH_PLUGIN_RESULT_FALLBACK: + break; + default: + return 0; + } + break; + case ETH_PLUGIN_QUERY_CONTRACT_ID: + if (((ethQueryContractID_t*)parameter)->result != ETH_PLUGIN_RESULT_OK) { + return 0; + } + break; + case ETH_PLUGIN_QUERY_CONTRACT_UI: + if (((ethQueryContractUI_t*)parameter)->result != ETH_PLUGIN_RESULT_OK) { + return 0; + } + break; + default: + return 0; + } + + return 1; +} diff --git a/src/eth_plugin_handler.h b/src/eth_plugin_handler.h new file mode 100644 index 0000000..e8a1a25 --- /dev/null +++ b/src/eth_plugin_handler.h @@ -0,0 +1,19 @@ +#ifndef __ETH_PLUGIN_HANDLER_H__ + +#include "eth_plugin_interface.h" + +void eth_plugin_prepare_init(ethPluginInitContract_t *init, uint8_t *selector, uint32_t dataSize); +void eth_plugin_prepare_provide_parameter(ethPluginProvideParameter_t *provideParameter, uint8_t *parameter, uint32_t parameterOffset); +void eth_plugin_prepare_finalize(ethPluginFinalize_t *finalize); +void eth_plugin_prepare_provide_token(ethPluginProvideToken_t *provideToken, tokenDefinition_t *token1, tokenDefinition_t *token2); +void eth_plugin_prepare_query_contract_ID(ethQueryContractID_t *queryContractID, char *name, uint32_t nameLength, char *version, uint32_t versionLength); +void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI, uint8_t screenIndex, char *title, uint32_t titleLength, char *msg, uint32_t msgLength); + +int eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *init); +// NULL for cached address, or base contract address +int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter); + +void plugin_ui_start(void); + +#endif + diff --git a/src/eth_plugin_interface.h b/src/eth_plugin_interface.h new file mode 100644 index 0000000..ae51cbf --- /dev/null +++ b/src/eth_plugin_interface.h @@ -0,0 +1,171 @@ +#ifndef __ETH_PLUGIN_INTERFACE_H__ + +#define __ETH_PLUGIN_INTERFACE_H__ + +#include "os.h" +#include "cx.h" +#include "ethUstream.h" +#include "tokens.h" + +#define PLUGIN_ID_LENGTH 30 + +typedef enum { + + ETH_PLUGIN_INIT_CONTRACT = 0x0101, + ETH_PLUGIN_PROVIDE_PARAMETER = 0x0102, + ETH_PLUGIN_FINALIZE = 0x0103, + ETH_PLUGIN_PROVIDE_TOKEN = 0x0104, + ETH_PLUGIN_QUERY_CONTRACT_ID = 0x0105, + ETH_PLUGIN_QUERY_CONTRACT_UI = 0x0106 + +} eth_plugin_msg_t; + +typedef enum { + + ETH_PLUGIN_RESULT_ERROR = 0x00, + ETH_PLUGIN_RESULT_OK = 0x01, + ETH_PLUGIN_RESULT_OK_ALIAS = 0x02, + ETH_PLUGIN_RESULT_FALLBACK = 0x03 + +} eth_plugin_result_t; + +typedef enum { + + ETH_UI_TYPE_AMOUNT_ADDRESS = 0x01, + ETH_UI_TYPE_GENERIC = 0x02 + +} eth_ui_type_t; + +typedef void (*PluginCall)(int, void*); + +// Shared objects, read-write + +typedef struct ethPluginSharedRW_t { + + cx_sha3_t *sha3; + +} ethPluginSharedRW_t; + +// Shared objects, read-only + +typedef struct ethPluginSharedRO_t { + + txContent_t *txContent; + +} ethPluginSharedRO_t; + + +// Init Contract + +typedef struct ethPluginInitContract_t { + + // in + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + uint32_t pluginContextLength; + uint8_t *selector; // 4 bytes selector + uint32_t dataSize; + + char *alias; // 29 bytes alias if ETH_PLUGIN_RESULT_OK_ALIAS set + + uint8_t result; + +} ethPluginInitContract_t; + +// Provide parameter + +typedef struct ethPluginProvideParameter_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + uint8_t *parameter; // 32 bytes parameter + uint32_t parameterOffset; + + uint8_t result; + +} ethPluginProvideParameter_t; + +// Finalize + +typedef struct ethPluginFinalize_t { + + ethPluginSharedRW_t *pluginSharedRW; + 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 *amount; // set an uint256 pointer if uiType is UI_AMOUNT_ADDRESS + uint8_t *address; // set to a 20 bytes address pointer if uiType is UI_AMOUNT_ADDRESS + + uint8_t uiType; + uint8_t numScreens; // ignored if uiType is UI_AMOUNT_ADDRESS + uint8_t result; + +} ethPluginFinalize_t; + +// If uiType is UI_AMOUNT_ADDRESS, the amount and address provided by the plugin will be used +// If tokenLookup1 is set, the amount is provided for this token + +// if uiType is UI_TYPE_GENERIC, the ETH application provides tokens if requested then prompts +// for each UI field +// The first field is forced by the ETH app to be the name + version of the plugin handling the request +// The last field is the fee amount + +// Provide token + +typedef struct ethPluginProvideToken_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; + + uint8_t result; + +} ethPluginProvideToken_t; + +// Query Contract name and version + +// This is always called on the non aliased contract + +typedef struct ethQueryContractID_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + + char *name; + uint32_t nameLength; + char *version; + uint32_t versionLength; + + uint8_t result; + +} ethQueryContractID_t; + +// Query Contract UI + +typedef struct ethQueryContractUI_t { + + ethPluginSharedRW_t *pluginSharedRW; + ethPluginSharedRO_t *pluginSharedRO; + uint8_t *pluginContext; + uint8_t screenIndex; + char *title; + uint32_t titleLength; + char *msg; + uint32_t msgLength; + + uint8_t result; + +} ethQueryContractUI_t; + +#endif + diff --git a/src/eth_plugin_internal.c b/src/eth_plugin_internal.c new file mode 100644 index 0000000..a7023a9 --- /dev/null +++ b/src/eth_plugin_internal.c @@ -0,0 +1,52 @@ +#include "eth_plugin_internal.h" + +void erc20_plugin_call(int message, void *parameters); +void starkware_plugin_call(int message, void *parameters); + +static const uint8_t const ERC20_SELECTOR[SELECTOR_SIZE] = { 0xa9, 0x05, 0x9c, 0xbb }; + +static const uint8_t* const ERC20_SELECTORS[1] = { ERC20_SELECTOR }; + +#ifdef HAVE_STARKWARE + +static const uint8_t const STARKWARE_REGISTER_ID[SELECTOR_SIZE] = { 0x76, 0x57, 0x18, 0xd7 }; +static const uint8_t const STARKWARE_DEPOSIT_TOKEN_ID[SELECTOR_SIZE] = { 0x00, 0xae, 0xef, 0x8a }; +static const uint8_t const STARKWARE_DEPOSIT_ETH_ID[SELECTOR_SIZE] = { 0xe2, 0xbb, 0xb1, 0x58 }; +static const uint8_t const STARKWARE_DEPOSIT_CANCEL_ID[SELECTOR_SIZE] = { 0xc7, 0xfb, 0x11, 0x7c }; +static const uint8_t const STARKWARE_DEPOSIT_RECLAIM_ID[SELECTOR_SIZE] = { 0x4e, 0xab, 0x38, 0xf4 }; +static const uint8_t const STARKWARE_WITHDRAW_ID[SELECTOR_SIZE] = { 0x2e, 0x1a, 0x7d, 0x4d }; +static const uint8_t const STARKWARE_FULL_WITHDRAWAL_ID[SELECTOR_SIZE] = { 0x27, 0x6d, 0xd1, 0xde }; +static const uint8_t const STARKWARE_FREEZE_ID[SELECTOR_SIZE] = { 0xb9, 0x10, 0x72, 0x09 }; +static const uint8_t const STARKWARE_ESCAPE_ID[SELECTOR_SIZE] = { 0x9e, 0x3a, 0xda, 0xc4 }; +static const uint8_t const STARKWARE_VERIFY_ESCAPE_ID[SELECTOR_SIZE] = { 0x2d, 0xd5, 0x30, 0x06 }; + +const uint8_t* const STARKWARE_SELECTORS[NUM_STARKWARE_SELECTORS] = { + STARKWARE_REGISTER_ID, STARKWARE_DEPOSIT_TOKEN_ID, STARKWARE_DEPOSIT_ETH_ID, + STARKWARE_DEPOSIT_CANCEL_ID, STARKWARE_DEPOSIT_RECLAIM_ID, STARKWARE_WITHDRAW_ID, + STARKWARE_FULL_WITHDRAWAL_ID, STARKWARE_FREEZE_ID, STARKWARE_ESCAPE_ID, + STARKWARE_VERIFY_ESCAPE_ID +}; + +#endif + +// All internal alias names start with 'minus' + +const internalEthPlugin_t const INTERNAL_ETH_PLUGINS[NUM_INTERNAL_PLUGINS] = { + { + ERC20_SELECTORS, + 1, + "-erc20", + erc20_plugin_call + }, + +#ifdef HAVE_STARKWARE + + { + STARKWARE_SELECTORS, + 10, + "-strk", + starkware_plugin_call + }, + +#endif +}; diff --git a/src/eth_plugin_internal.h b/src/eth_plugin_internal.h new file mode 100644 index 0000000..59b76a5 --- /dev/null +++ b/src/eth_plugin_internal.h @@ -0,0 +1,30 @@ +#ifndef __ETH_PLUGIN_INTERNAL_H__ + +#include "eth_plugin_interface.h" + +#define SELECTOR_SIZE 4 + +typedef struct internalEthPlugin_t { + const uint8_t **selectors; + uint8_t num_selectors; + char alias[7]; + PluginCall impl; +} internalEthPlugin_t; + +#ifdef HAVE_STARKWARE + +#define NUM_INTERNAL_PLUGINS 2 + +#define NUM_STARKWARE_SELECTORS 10 + +extern const uint8_t* const STARKWARE_SELECTORS[NUM_STARKWARE_SELECTORS]; + +#else + +#define NUM_INTERNAL_PLUGINS 1 + +#endif + +extern internalEthPlugin_t const INTERNAL_ETH_PLUGINS[NUM_INTERNAL_PLUGINS]; + +#endif diff --git a/src/eth_plugin_ui.c b/src/eth_plugin_ui.c new file mode 100644 index 0000000..d63fb9e --- /dev/null +++ b/src/eth_plugin_ui.c @@ -0,0 +1,173 @@ +#include "shared_context.h" +#ifdef HAVE_UX_FLOW +#include "ui_flow.h" +#endif +#include "ui_callbacks.h" +#include "eth_plugin_handler.h" + +typedef enum { + + PLUGIN_UI_INSIDE = 0, + PLUGIN_UI_OUTSIDE + +} plugin_ui_state_t; + +void computeFees(char *displayBuffer, uint32_t displayBufferSize); + +void plugin_ui_get_id() { + ethQueryContractID_t pluginQueryContractID; + eth_plugin_prepare_query_contract_ID(&pluginQueryContractID, strings.tmp.tmp, sizeof(strings.tmp.tmp), strings.tmp.tmp2, sizeof(strings.tmp.tmp2)); + // Query the original contract for ID if it's not an internal alias + if (!eth_plugin_call( + (dataContext.tokenContext.pluginName[0] == '-' ? NULL : tmpContent.txContent.destination), + ETH_PLUGIN_QUERY_CONTRACT_ID, (void*)&pluginQueryContractID)) { + PRINTF("Plugin query contract ID call failed\n"); + io_seproxyhal_touch_tx_cancel(NULL); + } +} + +void plugin_ui_get_item() { + ethQueryContractUI_t pluginQueryContractUI; + eth_plugin_prepare_query_contract_UI(&pluginQueryContractUI, dataContext.tokenContext.pluginUiCurrentItem, strings.tmp.tmp, sizeof(strings.tmp.tmp), strings.tmp.tmp2, sizeof(strings.tmp.tmp2)); + if (!eth_plugin_call(NULL, ETH_PLUGIN_QUERY_CONTRACT_UI, (void*)&pluginQueryContractUI)) { + PRINTF("Plugin query contract UI call failed\n"); + io_seproxyhal_touch_tx_cancel(NULL); + } +} + +void display_next_plugin_item(bool entering) { + if (entering) { + if (dataContext.tokenContext.pluginUiState == PLUGIN_UI_OUTSIDE) { + dataContext.tokenContext.pluginUiState = PLUGIN_UI_INSIDE; + dataContext.tokenContext.pluginUiCurrentItem = 0; + plugin_ui_get_item(); + ux_flow_next(); + } + else { + if (dataContext.tokenContext.pluginUiCurrentItem > 0) { + dataContext.tokenContext.pluginUiCurrentItem--; + plugin_ui_get_item(); + ux_flow_next(); + } + else { + dataContext.tokenContext.pluginUiState = PLUGIN_UI_OUTSIDE; + dataContext.tokenContext.pluginUiCurrentItem = 0; + ux_flow_prev(); + } + } + } + else { + if (dataContext.tokenContext.pluginUiState == PLUGIN_UI_OUTSIDE) { + dataContext.tokenContext.pluginUiState = PLUGIN_UI_INSIDE; + plugin_ui_get_item(); + ux_flow_prev(); + // Reset multi page layout to the first page + G_ux.layout_paging.current = 0; + ux_layout_paging_redisplay(G_ux.stack_count-1); + } + else { + if (dataContext.tokenContext.pluginUiCurrentItem < dataContext.tokenContext.pluginUiMaxItems - 1) { + dataContext.tokenContext.pluginUiCurrentItem++; + plugin_ui_get_item(); + ux_flow_prev(); + // Reset multi page layout to the first page + G_ux.layout_paging.current = 0; + ux_layout_paging_redisplay(G_ux.stack_count-1); + } + else { + dataContext.tokenContext.pluginUiState = PLUGIN_UI_OUTSIDE; + ux_flow_next(); + } + } + } +} + +void plugin_ui_compute_fees() { + computeFees(strings.common.maxFee, sizeof(strings.common.maxFee)); +} + +UX_FLOW_DEF_NOCB( + ux_plugin_approval_intro_step, + pnn, + { + &C_icon_eye, + "Review", + "contract call", + }); + +UX_STEP_NOCB_INIT( + ux_plugin_approval_id_step, + bnnn_paging, + plugin_ui_get_id(), + { + .title = strings.tmp.tmp, + .text = strings.tmp.tmp2 + }); + +UX_STEP_INIT( + ux_plugin_approval_before_step, + NULL, + NULL, + { + display_next_plugin_item(true); + }); + +UX_FLOW_DEF_NOCB( + ux_plugin_approval_display_step, + bnnn_paging, + { + .title = strings.tmp.tmp, + .text = strings.tmp.tmp2 + }); + +UX_STEP_INIT( + ux_plugin_approval_after_step, + NULL, + NULL, + { + display_next_plugin_item(false); + }); + +UX_STEP_NOCB_INIT( + ux_plugin_approval_fees_step, + bnnn_paging, + plugin_ui_compute_fees(), + { + .title = "Max fees", + .text = strings.common.maxFee + }); + +UX_FLOW_DEF_VALID( + ux_plugin_approval_ok_step, + pbb, + io_seproxyhal_touch_tx_ok(NULL), + { + &C_icon_validate_14, + "Accept", + "and send", + }); +UX_FLOW_DEF_VALID( + ux_plugin_approval_cancel_step, + pb, + io_seproxyhal_touch_tx_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); + +const ux_flow_step_t * const ux_plugin_approval_flow [] = { + &ux_plugin_approval_intro_step, + &ux_plugin_approval_id_step, + &ux_plugin_approval_before_step, + &ux_plugin_approval_display_step, + &ux_plugin_approval_after_step, + &ux_plugin_approval_fees_step, + &ux_plugin_approval_ok_step, + &ux_plugin_approval_cancel_step +}; + +void plugin_ui_start() { + dataContext.tokenContext.pluginUiState = PLUGIN_UI_OUTSIDE; + dataContext.tokenContext.pluginUiCurrentItem = 0; + ux_flow_init(0, ux_plugin_approval_flow, NULL); +} diff --git a/src/main.c b/src/main.c index cf3d4df..24b5377 100644 --- a/src/main.c +++ b/src/main.c @@ -52,7 +52,6 @@ uint8_t dataAllowed; uint8_t contractDetails; uint8_t appState; bool dataPresent; -contract_call_t contractProvisioned; bool called_from_swap; #ifdef HAVE_STARKWARE bool quantumSet; @@ -70,7 +69,6 @@ void reset_app_context() { //PRINTF("!!RESET_APP_CONTEXT\n"); appState = APP_STATE_IDLE; os_memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN); - contractProvisioned = CONTRACT_NONE; called_from_swap = false; #ifdef HAVE_STARKWARE quantumSet = false; diff --git a/src/shared_context.h b/src/shared_context.h index d04cb6e..0fc4485 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -14,6 +14,7 @@ #include "uint256.h" #include "tokens.h" #include "chainConfig.h" +#include "eth_plugin_interface.h" #define MAX_BIP32_PATH 10 @@ -30,23 +31,25 @@ typedef struct internalStorage_t { } internalStorage_t; typedef struct tokenContext_t { -#ifdef HAVE_STARKWARE - uint8_t data[4 + 32 + 32 + 32 + 32]; -#else - uint8_t data[4 + 32 + 32]; -#endif - uint32_t dataFieldPos; + char pluginName[PLUGIN_ID_LENGTH]; + uint8_t pluginAvailable; + + uint8_t data[32]; + uint8_t fieldIndex; + uint8_t fieldOffset; + + uint8_t pluginUiMaxItems; + uint8_t pluginUiCurrentItem; + uint8_t pluginUiState; + + uint8_t pluginContext[3 * 32]; + #ifdef HAVE_STARKWARE uint8_t quantum[32]; uint8_t quantumIndex; #endif -} tokenContext_t; -typedef struct rawDataContext_t { - uint8_t data[32]; - uint8_t fieldIndex; - uint8_t fieldOffset; -} rawDataContext_t; +} tokenContext_t; typedef struct publicKeyContext_t { cx_ecfp_public_key_t publicKey; @@ -104,7 +107,6 @@ typedef struct starkContext_t { typedef union { tokenContext_t tokenContext; - rawDataContext_t rawDataContext; #ifdef HAVE_STARKWARE starkContext_t starkContext; #endif @@ -165,7 +167,6 @@ extern uint8_t dataAllowed; extern uint8_t contractDetails; extern bool dataPresent; extern uint8_t appState; -extern contract_call_t contractProvisioned; #ifdef HAVE_STARKWARE extern bool quantumSet; #endif diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 506a185..a3d12d0 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -34,7 +34,6 @@ void handleSign(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint16_t dataLength dataLength -= 4; } dataPresent = false; - contractProvisioned = CONTRACT_NONE; initTx(&txContext, &global_sha3, &tmpContent.txContent, customProcessor, NULL); } else diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index d72f7da..3c009a5 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -5,39 +5,10 @@ #ifdef HAVE_STARKWARE #include "stark_utils.h" #endif - -#define TOKEN_TRANSFER_DATA_SIZE 4 + 32 + 32 -static const uint8_t const TOKEN_TRANSFER_ID[] = { 0xa9, 0x05, 0x9c, 0xbb }; - -#define ALLOWANCE_DATA_SIZE 4 + 32 + 32 -static const uint8_t const ALLOWANCE_ID[] = { 0x09, 0x5e, 0xa7, 0xb3 }; +#include "eth_plugin_handler.h" #define ERR_SILENT_MODE_CHECK_FAILED 0x6001 -#ifdef HAVE_STARKWARE - -#define STARKWARE_REGISTER_DATA_SIZE 4 + 32 -static const uint8_t const STARKWARE_REGISTER_ID[] = { 0x76, 0x57, 0x18, 0xd7 }; -#define STARKWARE_DEPOSIT_TOKEN_DATA_SIZE 4 + 32 + 32 + 32 -static const uint8_t const STARKWARE_DEPOSIT_TOKEN_ID[] = { 0x00, 0xae, 0xef, 0x8a }; -#define STARKWARE_DEPOSIT_ETH_DATA_SIZE 4 + 32 + 32 -static const uint8_t const STARKWARE_DEPOSIT_ETH_ID[] = { 0xe2, 0xbb, 0xb1, 0x58 }; -#define STARKWARE_DEPOSIT_CANCEL_DATA_SIZE 4 + 32 + 32 -static const uint8_t const STARKWARE_DEPOSIT_CANCEL_ID[] = { 0xc7, 0xfb, 0x11, 0x7c }; -#define STARKWARE_DEPOSIT_RECLAIM_DATA_SIZE 4 + 32 + 32 -static const uint8_t const STARKWARE_DEPOSIT_RECLAIM_ID[] = { 0x4e, 0xab, 0x38, 0xf4 }; -#define STARKWARE_WITHDRAW_DATA_SIZE 4 + 32 -static const uint8_t const STARKWARE_WITHDRAW_ID[] = { 0x2e, 0x1a, 0x7d, 0x4d }; -#define STARKWARE_FULL_WITHDRAWAL_DATA_SIZE 4 + 32 -static const uint8_t const STARKWARE_FULL_WITHDRAWAL_ID[] = { 0x27, 0x6d, 0xd1, 0xde }; -#define STARKWARE_FREEZE_DATA_SIZE 4 + 32 -static const uint8_t const STARKWARE_FREEZE_ID[] = { 0xb9, 0x10, 0x72, 0x09 }; -#define STARKWARE_ESCAPE_DATA_SIZE 4 + 32 + 32 + 32 + 32 -static const uint8_t const STARKWARE_ESCAPE_ID[] = { 0x9e, 0x3a, 0xda, 0xc4 }; -static const uint8_t const STARKWARE_VERIFY_ESCAPE_ID[] = { 0x2d, 0xd5, 0x30, 0x06 }; - -#endif - uint32_t splitBinaryParameterPart(char *result, uint8_t *parameter) { uint32_t i; for (i=0; i<8; i++) { @@ -66,191 +37,108 @@ customStatus_e customProcessor(txContext_t *context) { return CUSTOM_NOT_HANDLED; } if (context->currentFieldPos == 0) { + ethPluginInitContract_t pluginInit; // If handling the beginning of the data field, assume that the function selector is present if (context->commandLength < 4) { PRINTF("Missing function selector\n"); return CUSTOM_FAULT; } - // Initial check to see if the call can be processed - if ((context->currentFieldLength == TOKEN_TRANSFER_DATA_SIZE) && - (os_memcmp(context->workBuffer, TOKEN_TRANSFER_ID, 4) == 0) && - (getKnownToken(tmpContent.txContent.destination) != NULL)) { - contractProvisioned = CONTRACT_ERC20; - } - else - if ((context->currentFieldLength == ALLOWANCE_DATA_SIZE) && - (os_memcmp(context->workBuffer, ALLOWANCE_ID, 4) == 0)) { - contractProvisioned = CONTRACT_ALLOWANCE; - } -#ifdef HAVE_STARKWARE - else - if ((context->currentFieldLength >= STARKWARE_REGISTER_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_REGISTER_ID, 4) == 0)) { - contractProvisioned = CONTRACT_STARKWARE_REGISTER; - } - else - if ((context->currentFieldLength == STARKWARE_DEPOSIT_ETH_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_ETH_ID, 4) == 0)) { - contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_ETH; - } - else - if ((context->currentFieldLength == STARKWARE_DEPOSIT_TOKEN_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_TOKEN_ID, 4) == 0) && - quantumSet) { - contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_TOKEN; - } - else - if ((context->currentFieldLength == STARKWARE_WITHDRAW_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_WITHDRAW_ID, 4) == 0) && - quantumSet) { - contractProvisioned = CONTRACT_STARKWARE_WITHDRAW; - } - else - if ((context->currentFieldLength == STARKWARE_DEPOSIT_CANCEL_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_CANCEL_ID, 4) == 0)) { - contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_CANCEL; - } - else - if ((context->currentFieldLength == STARKWARE_DEPOSIT_RECLAIM_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_DEPOSIT_RECLAIM_ID, 4) == 0)) { - contractProvisioned = CONTRACT_STARKWARE_DEPOSIT_RECLAIM; - } - else - if ((context->currentFieldLength == STARKWARE_FULL_WITHDRAWAL_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_FULL_WITHDRAWAL_ID, 4) == 0)) { - contractProvisioned = CONTRACT_STARKWARE_FULL_WITHDRAWAL; - } - else - if ((context->currentFieldLength == STARKWARE_FREEZE_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_FREEZE_ID, 4) == 0)) { - contractProvisioned = CONTRACT_STARKWARE_FREEZE; - } - else - if ((context->currentFieldLength == STARKWARE_ESCAPE_DATA_SIZE) && - (os_memcmp(context->workBuffer, STARKWARE_ESCAPE_ID, 4) == 0) && - quantumSet) { - contractProvisioned = CONTRACT_STARKWARE_ESCAPE; - } - else - if (os_memcmp(context->workBuffer, STARKWARE_VERIFY_ESCAPE_ID, 4) == 0) { - contractProvisioned = CONTRACT_STARKWARE_VERIFY_ESCAPE; + eth_plugin_prepare_init(&pluginInit, context->workBuffer, context->currentFieldLength); + dataContext.tokenContext.pluginAvailable = eth_plugin_perform_init(tmpContent.txContent.destination, &pluginInit); + PRINTF("pluginAvailable %d\n", dataContext.tokenContext.pluginAvailable); + if (dataContext.tokenContext.pluginAvailable) { + dataContext.tokenContext.fieldIndex = 0; + dataContext.tokenContext.fieldOffset = 0; + copyTxData(context, NULL, 4); } + } + uint32_t blockSize; + uint32_t copySize; + uint32_t fieldPos = context->currentFieldPos; + if (fieldPos == 0) { // not reached if a plugin is available + if (!N_storage.dataAllowed) { + PRINTF("Data field forbidden\n"); + return CUSTOM_FAULT; + } + if (!N_storage.contractDetails) { + return CUSTOM_NOT_HANDLED; + } + dataContext.tokenContext.fieldIndex = 0; + dataContext.tokenContext.fieldOffset = 0; + blockSize = 4; + } + else { + blockSize = 32 - (dataContext.tokenContext.fieldOffset % 32); + } -#endif - } - // Sanity check - // Also handle exception that only need to process the beginning of the data - if ((contractProvisioned != CONTRACT_NONE) && -#ifdef HAVE_STARKWARE - (contractProvisioned != CONTRACT_STARKWARE_VERIFY_ESCAPE) && - (contractProvisioned != CONTRACT_STARKWARE_REGISTER) && -#endif - (context->currentFieldLength > sizeof(dataContext.tokenContext.data))) { - PRINTF("Data field overflow - dropping customization\n"); - contractProvisioned = CONTRACT_NONE; - } - PRINTF("contractProvisioned %d\n", contractProvisioned); - if (contractProvisioned != CONTRACT_NONE) { - if (context->currentFieldPos < context->currentFieldLength) { - uint32_t copySize = MIN(context->commandLength, - context->currentFieldLength - context->currentFieldPos); - // Handle the case where we only need to handle the beginning of the data parameter - if ((context->currentFieldPos + copySize) < sizeof(dataContext.tokenContext.data)) { - copyTxData(context, - dataContext.tokenContext.data + context->currentFieldPos, + // Sanity check + if ((context->currentFieldLength - fieldPos) < blockSize) { + PRINTF("Unconsistent data\n"); + return CUSTOM_FAULT; + } + + copySize = (context->commandLength < blockSize ? context->commandLength : blockSize); + + PRINTF("currentFieldPos %d copySize %d\n", context->currentFieldPos, copySize); + + copyTxData(context, + dataContext.tokenContext.data + dataContext.tokenContext.fieldOffset, copySize); - } - else { - if (context->currentFieldPos < sizeof(dataContext.tokenContext.data)) { - uint32_t copySize2 = sizeof(dataContext.tokenContext.data) - context->currentFieldPos; - copyTxData(context, - dataContext.tokenContext.data + context->currentFieldPos, - copySize2); - copySize -= copySize2; - } - copyTxData(context, NULL, copySize); - } - } - if (context->currentFieldPos == context->currentFieldLength) { - context->currentField++; - context->processingField = false; - } - return CUSTOM_HANDLED; - } - else { - uint32_t blockSize; - uint32_t copySize; - uint32_t fieldPos = context->currentFieldPos; - if (fieldPos == 0) { - if (!N_storage.dataAllowed) { - PRINTF("Data field forbidden\n"); + + if (context->currentFieldPos == context->currentFieldLength) { + context->currentField++; + context->processingField = false; + } + + dataContext.tokenContext.fieldOffset += copySize; + + if (copySize == blockSize) { + // Can process or display + if (dataContext.tokenContext.pluginAvailable) { + ethPluginProvideParameter_t pluginProvideParameter; + eth_plugin_prepare_provide_parameter(&pluginProvideParameter, + dataContext.tokenContext.data, + dataContext.tokenContext.fieldIndex * 32 + 4); + if (!eth_plugin_call(NULL, ETH_PLUGIN_PROVIDE_PARAMETER, (void*)&pluginProvideParameter)) { + PRINTF("Plugin parameter call failed\n"); return CUSTOM_FAULT; } - if (!N_storage.contractDetails) { - return CUSTOM_NOT_HANDLED; - } - dataContext.rawDataContext.fieldIndex = 0; - dataContext.rawDataContext.fieldOffset = 0; - blockSize = 4; - } - else { - if (!N_storage.contractDetails) { - return CUSTOM_NOT_HANDLED; - } - blockSize = 32 - (dataContext.rawDataContext.fieldOffset % 32); - } - - // Sanity check - if ((context->currentFieldLength - fieldPos) < blockSize) { - PRINTF("Unconsistent data\n"); - return CUSTOM_FAULT; - } - - copySize = (context->commandLength < blockSize ? context->commandLength : blockSize); - copyTxData(context, - dataContext.rawDataContext.data + dataContext.rawDataContext.fieldOffset, - copySize); - - if (context->currentFieldPos == context->currentFieldLength) { - context->currentField++; - context->processingField = false; - } - - dataContext.rawDataContext.fieldOffset += copySize; - - if (copySize == blockSize) { - // Can display - if (fieldPos != 0) { - dataContext.rawDataContext.fieldIndex++; - } - dataContext.rawDataContext.fieldOffset = 0; - if (fieldPos == 0) { - array_hexstr(strings.tmp.tmp, dataContext.rawDataContext.data, 4); - ux_flow_init(0, ux_confirm_selector_flow, NULL); - } - else { - uint32_t offset = 0; - uint32_t i; - snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Field %d", dataContext.rawDataContext.fieldIndex); - for (i=0; i<4; i++) { - offset += splitBinaryParameterPart(strings.tmp.tmp + offset, dataContext.rawDataContext.data + 8 * i); - if (i != 3) { - strings.tmp.tmp[offset++] = ':'; - } - } - ux_flow_init(0, ux_confirm_parameter_flow, NULL); - } - } - else { + dataContext.tokenContext.fieldIndex++; + dataContext.tokenContext.fieldOffset = 0; return CUSTOM_HANDLED; - } + } - return CUSTOM_SUSPENDED; - } + if (fieldPos != 0) { + dataContext.tokenContext.fieldIndex++; + } + dataContext.tokenContext.fieldOffset = 0; + if (fieldPos == 0) { + array_hexstr(strings.tmp.tmp, dataContext.tokenContext.data, 4); + ux_flow_init(0, ux_confirm_selector_flow, NULL); + } + else { + uint32_t offset = 0; + uint32_t i; + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Field %d", dataContext.tokenContext.fieldIndex); + for (i=0; i<4; i++) { + offset += splitBinaryParameterPart(strings.tmp.tmp + offset, dataContext.tokenContext.data + 8 * i); + if (i != 3) { + strings.tmp.tmp[offset++] = ':'; + } + } + ux_flow_init(0, ux_confirm_parameter_flow, NULL); + } + } + else { + return CUSTOM_HANDLED; + } + + return CUSTOM_SUSPENDED; } return CUSTOM_NOT_HANDLED; } + void to_uppercase(char* str, unsigned char size){ for (unsigned char i = 0; i < size && str[i] != 0; i++) { @@ -273,117 +161,23 @@ void compareOrCopy(char* preapproved_string, char* parsed_string, bool silent_mo } } -void finalizeParsing(bool direct) { - uint256_t gasPrice, startGas, uint256; - uint32_t i; - char displayBuffer[50]; - uint8_t decimals = WEI_TO_ETHER; - uint8_t *ticker = (uint8_t *)PIC(chainConfig->coinName); - uint8_t *feeTicker = (uint8_t *)PIC(chainConfig->coinName); - uint8_t tickerOffset = 0; +void reportFinalizeError(bool direct) { + reset_app_context(); + if (direct) { + THROW(0x6A80); + } + else { + io_seproxyhal_send_status(0x6A80); + ui_idle(); + } +} + +void computeFees(char *displayBuffer, uint32_t displayBufferSize) { + uint256_t gasPrice, startGas, uint256; + uint8_t *feeTicker = (uint8_t *)PIC(chainConfig->coinName); + uint8_t tickerOffset = 0; + uint32_t i; - // Verify the chain - if (chainConfig->chainId != 0) { - uint32_t v = getV(&tmpContent.txContent); - if (chainConfig->chainId != v) { - reset_app_context(); - PRINTF("Invalid chainId %d expected %d\n", v, chainConfig->chainId); - if (direct) { - THROW(0x6A80); - } - else { - io_seproxyhal_send_status(0x6A80); - ui_idle(); - return; - } - } - } - // Store the hash - cx_hash((cx_hash_t *)&global_sha3, CX_LAST, tmpCtx.transactionContext.hash, 0, tmpCtx.transactionContext.hash, 32); -#ifdef HAVE_STARKWARE - if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) || - (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_TOKEN) || - (contractProvisioned == CONTRACT_STARKWARE_WITHDRAW) || - (contractProvisioned == CONTRACT_STARKWARE_ESCAPE)) { - // For a deposit / withdrawal / escape, check if the token ID is known or can't parse - uint8_t tokenIdOffset = (4 + ((contractProvisioned == CONTRACT_STARKWARE_ESCAPE) ? 32 + 32 : 0)); - if (quantumSet) { - tokenDefinition_t *currentToken = NULL; - if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { - currentToken = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; - } - compute_token_id(&global_sha3, - (currentToken != NULL ? currentToken->address : NULL), - dataContext.tokenContext.quantum, G_io_apdu_buffer + 100); - if (os_memcmp(dataContext.tokenContext.data + tokenIdOffset, G_io_apdu_buffer + 100, 32) != 0) { - PRINTF("Token ID not matching - computed %.*H\n", 32, G_io_apdu_buffer + 100); - PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum); - PRINTF("Requested %.*H\n", 32, dataContext.tokenContext.data + tokenIdOffset); - contractProvisioned = CONTRACT_NONE; - } - } - else { - PRINTF("Quantum not set\n"); - contractProvisioned = CONTRACT_NONE; - } - } -#endif - // If there is a token to process, check if it is well known - if ((contractProvisioned == CONTRACT_ERC20) || (contractProvisioned == CONTRACT_ALLOWANCE)) { - tokenDefinition_t *currentToken = getKnownToken(tmpContent.txContent.destination); - if (currentToken != NULL) { - dataPresent = false; - decimals = currentToken->decimals; - ticker = currentToken->ticker; - tmpContent.txContent.destinationLength = 20; - os_memmove(tmpContent.txContent.destination, dataContext.tokenContext.data + 4 + 12, 20); - os_memmove(tmpContent.txContent.value.value, dataContext.tokenContext.data + 4 + 32, 32); - tmpContent.txContent.value.length = 32; - } - } - else { - if (dataPresent && contractProvisioned == CONTRACT_NONE && !N_storage.dataAllowed) { - reset_app_context(); - PRINTF("Data field forbidden\n"); - if (direct) { - THROW(0x6A80); - } - else { - io_seproxyhal_send_status(0x6A80); - ui_idle(); - return; - } - } - } - // Add address - if (tmpContent.txContent.destinationLength != 0) { - displayBuffer[0] = '0'; - displayBuffer[1] = 'x'; - getEthAddressStringFromBinary(tmpContent.txContent.destination, (uint8_t*)displayBuffer+2, &global_sha3, chainConfig); - compareOrCopy(strings.common.fullAddress, displayBuffer, called_from_swap); - } - else - { - strcpy(strings.common.fullAddress, "Contract"); - } - if ((contractProvisioned == CONTRACT_NONE) || (contractProvisioned == CONTRACT_ERC20) || - (contractProvisioned == CONTRACT_ALLOWANCE)) { - // Add amount in ethers or tokens - if ((contractProvisioned == CONTRACT_ALLOWANCE) && ismaxint(tmpContent.txContent.value.value, 32)) { - i = 0; - tickerOffset = 0; - while (ticker[tickerOffset]) { - displayBuffer[tickerOffset] = ticker[tickerOffset]; - tickerOffset++; - } - strcpy(displayBuffer + tickerOffset, "Unlimited"); - } - else { - amountToString(tmpContent.txContent.value.value, tmpContent.txContent.value.length, decimals, (char*)ticker, displayBuffer, sizeof(displayBuffer)); - } - compareOrCopy(strings.common.fullAmount, displayBuffer, called_from_swap); - } - // Compute maximum fee PRINTF("Max fee\n"); PRINTF("Gasprice %.*H\n", tmpContent.txContent.gasprice.length, tmpContent.txContent.gasprice.value); PRINTF("Startgas %.*H\n", tmpContent.txContent.startgas.length, tmpContent.txContent.startgas.value); @@ -398,7 +192,7 @@ void finalizeParsing(bool direct) { adjustDecimals((char *)(G_io_apdu_buffer + 100), i, (char *)G_io_apdu_buffer, 100, WEI_TO_ETHER); i = 0; tickerOffset=0; - memset(displayBuffer, 0, sizeof(displayBuffer)); + memset(displayBuffer, 0, displayBufferSize); while (feeTicker[tickerOffset]) { displayBuffer[tickerOffset] = feeTicker[tickerOffset]; tickerOffset++; @@ -408,7 +202,128 @@ void finalizeParsing(bool direct) { i++; } displayBuffer[tickerOffset + i] = '\0'; - compareOrCopy(strings.common.maxFee, displayBuffer, called_from_swap); +} + +void finalizeParsing(bool direct) { + char displayBuffer[50]; + uint8_t decimals = WEI_TO_ETHER; + uint8_t *ticker = (uint8_t *)PIC(chainConfig->coinName); + ethPluginFinalize_t pluginFinalize; + tokenDefinition_t *token1 = NULL, *token2 = NULL; + bool genericUI = true; + + // Verify the chain + if (chainConfig->chainId != 0) { + uint32_t v = getV(&tmpContent.txContent); + if (chainConfig->chainId != v) { + reset_app_context(); + PRINTF("Invalid chainId %d expected %d\n", v, chainConfig->chainId); + reportFinalizeError(direct); + if (!direct) { + return; + } + } + } + // Store the hash + cx_hash((cx_hash_t *)&global_sha3, CX_LAST, tmpCtx.transactionContext.hash, 0, tmpCtx.transactionContext.hash, 32); + + // Finalize the plugin handling + if (dataContext.tokenContext.pluginAvailable) { + genericUI = false; + eth_plugin_prepare_finalize(&pluginFinalize); + if (!eth_plugin_call(NULL, ETH_PLUGIN_FINALIZE, (void*)&pluginFinalize)) { + PRINTF("Plugin finalize call failed\n"); + reportFinalizeError(direct); + if (!direct) { + return; + } + } + // Lookup tokens if requested + if (pluginFinalize.tokenLookup1 != NULL) { + ethPluginProvideToken_t pluginProvideToken; + token1 = getKnownToken(pluginFinalize.tokenLookup1); + if (pluginFinalize.tokenLookup2 != NULL) { + token2 = getKnownToken(pluginFinalize.tokenLookup2); + } + eth_plugin_prepare_provide_token(&pluginProvideToken, token1, token2); + if (!eth_plugin_call(NULL, ETH_PLUGIN_PROVIDE_TOKEN, (void*)&pluginProvideToken)) { + PRINTF("Plugin provide token call failed\n"); + reportFinalizeError(direct); + if (!direct) { + return; + } + } + pluginFinalize.result = pluginProvideToken.result; + } + if (pluginFinalize.result != ETH_PLUGIN_RESULT_FALLBACK) { + // Handle the right interface + switch(pluginFinalize.uiType) { + case ETH_UI_TYPE_GENERIC: + dataPresent = false; + dataContext.tokenContext.pluginUiMaxItems = pluginFinalize.numScreens; + break; + case ETH_UI_TYPE_AMOUNT_ADDRESS: + genericUI = true; + dataPresent = false; + if ((pluginFinalize.amount == NULL) || (pluginFinalize.address == NULL)) { + PRINTF("Incorrect amount/address set by plugin\n"); + reportFinalizeError(direct); + if (!direct) { + return; + } + } + memmove(tmpContent.txContent.value.value, pluginFinalize.amount, 32); + tmpContent.txContent.value.length = 32; + memmove(tmpContent.txContent.destination, pluginFinalize.address, 20); + tmpContent.txContent.destinationLength = 20; + if (token1 != NULL) { + decimals = token1->decimals; + ticker = token1->ticker; + } + break; + default: + PRINTF("ui type %d not supported\n", pluginFinalize.uiType); + reportFinalizeError(direct); + if (!direct) { + return; + } + } + } + else { + genericUI = true; + } + } + + if (dataPresent && !N_storage.dataAllowed) { + PRINTF("Data field forbidden\n"); + reportFinalizeError(direct); + if (!direct) { + return; + } + } + // Prepare destination address to display + if (genericUI) { + if (tmpContent.txContent.destinationLength != 0) { + displayBuffer[0] = '0'; + displayBuffer[1] = 'x'; + getEthAddressStringFromBinary(tmpContent.txContent.destination, (uint8_t*)displayBuffer+2, &global_sha3, chainConfig); + compareOrCopy(strings.common.fullAddress, displayBuffer, called_from_swap); + } + else + { + strcpy(strings.common.fullAddress, "Contract"); + } + } + // Prepare amount to display + if (genericUI) { + amountToString(tmpContent.txContent.value.value, tmpContent.txContent.value.length, decimals, (char*)ticker, displayBuffer, sizeof(displayBuffer)); + compareOrCopy(strings.common.fullAmount, displayBuffer, called_from_swap); + } + // Compute maximum fee + if (genericUI) { + computeFees(displayBuffer, sizeof(displayBuffer)); + compareOrCopy(strings.common.maxFee, displayBuffer, called_from_swap); + } bool no_consent = false; @@ -423,57 +338,14 @@ void finalizeParsing(bool direct) { } else{ -#ifdef HAVE_STARKWARE - - if (contractProvisioned == CONTRACT_STARKWARE_REGISTER) { - ux_flow_init(0, ux_approval_starkware_register_flow, NULL); - return; + if (genericUI) { + ux_flow_init(0, + ((dataPresent && !N_storage.contractDetails) ? ux_approval_tx_data_warning_flow : ux_approval_tx_flow), + NULL); } - else - if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_TOKEN) { - ux_flow_init(0, ux_approval_starkware_deposit_flow, NULL); - return; + else { + plugin_ui_start(); } - else - if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) { - ux_flow_init(0, ux_approval_starkware_deposit_flow, NULL); - return; - } - else - if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) || - (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM) || - (contractProvisioned == CONTRACT_STARKWARE_FULL_WITHDRAWAL) || - (contractProvisioned == CONTRACT_STARKWARE_FREEZE)) { - ux_flow_init(0, ux_approval_starkware_verify_vault_id_flow, NULL); - return; - } - else - if (contractProvisioned == CONTRACT_STARKWARE_WITHDRAW) { - ux_flow_init(0, ux_approval_starkware_withdraw_flow, NULL); - return; - } - else - if (contractProvisioned == CONTRACT_STARKWARE_ESCAPE) { - ux_flow_init(0, ux_approval_starkware_escape_flow, NULL); - return; - } - else - if (contractProvisioned == CONTRACT_STARKWARE_VERIFY_ESCAPE) { - ux_flow_init(0, ux_approval_starkware_verify_escape_flow, NULL); - return; - } - -#endif - - if (contractProvisioned == CONTRACT_ALLOWANCE) { - ux_flow_init(0, ux_approval_allowance_flow, NULL); - return; - } - - ux_flow_init(0, - ((dataPresent && !N_storage.contractDetails) ? ux_approval_tx_data_warning_flow : ux_approval_tx_flow), - NULL); } } - diff --git a/src_features/stark_contract_deposit/ui_flow_stark_deposit.c b/src_features/stark_contract_deposit/ui_flow_stark_deposit.c deleted file mode 100644 index 4ce69cf..0000000 --- a/src_features/stark_contract_deposit/ui_flow_stark_deposit.c +++ /dev/null @@ -1,125 +0,0 @@ -#ifdef HAVE_STARKWARE - -#include "shared_context.h" -#include "ui_callbacks.h" -#include "utils.h" - -void prepare_deposit_3() { - uint8_t address[41]; - getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &global_sha3, chainConfig); - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; -} - -void prepare_deposit_4() { - snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + 32 + 32 - 4)); -} - -void prepare_deposit_5() { - uint256_t amount, amountPre, quantum; - uint8_t decimals; - char *ticker = (char*)PIC(chainConfig->coinName); - - if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_ETH) { - decimals = WEI_TO_ETHER; - convertUint256BE(tmpContent.txContent.value.value, tmpContent.txContent.value.length, &amountPre); - } - else { - tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; - decimals = token->decimals; - ticker = (char*)token->ticker; - readu256BE(dataContext.tokenContext.data + 4 + 32 + 32, &amountPre); - } - readu256BE(dataContext.tokenContext.quantum, &quantum); - mul256(&amountPre, &quantum, &amount); - tostring256(&amount, 10, (char*)(G_io_apdu_buffer + 100), 100); - strcpy(strings.common.fullAmount, ticker); - adjustDecimals((char*)(G_io_apdu_buffer + 100), strlen((char*)(G_io_apdu_buffer + 100)), strings.common.fullAmount + strlen(ticker), 50 - strlen(ticker), decimals); -} - -UX_STEP_NOCB(ux_approval_starkware_deposit_1_step, - pnn, - { - &C_icon_eye, - "Review", - "transaction", - }); - -UX_STEP_NOCB( - ux_approval_starkware_deposit_2_step, - bnnn_paging, - { - .title = "Deposit", - .text = " " - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_deposit_3_step, - bnnn_paging, - prepare_deposit_3(), - { - .title = "Contract Name", - .text = strings.common.fullAddress, - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_deposit_4_step, - bnnn_paging, - prepare_deposit_4(), - { - .title = "Token Account", - .text = strings.common.fullAddress - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_deposit_5_step, - bnnn_paging, - prepare_deposit_5(), - { - .title = "Amount", - .text = strings.common.fullAmount - }); - - -UX_STEP_NOCB( - ux_approval_starkware_deposit_6_step, - bnnn_paging, - { - .title = "Max Fees", - .text = strings.common.maxFee, - }); - -UX_STEP_CB( - ux_approval_starkware_deposit_7_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); - -UX_STEP_CB( - ux_approval_starkware_deposit_8_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -UX_FLOW(ux_approval_starkware_deposit_flow, - &ux_approval_starkware_deposit_1_step, - &ux_approval_starkware_deposit_2_step, - &ux_approval_starkware_deposit_3_step, - &ux_approval_starkware_deposit_4_step, - &ux_approval_starkware_deposit_5_step, - &ux_approval_starkware_deposit_6_step, - &ux_approval_starkware_deposit_7_step, - &ux_approval_starkware_deposit_8_step -); - -#endif - diff --git a/src_features/stark_contract_escape/ui_flow_stark_escape.c b/src_features/stark_contract_escape/ui_flow_stark_escape.c deleted file mode 100644 index 7811125..0000000 --- a/src_features/stark_contract_escape/ui_flow_stark_escape.c +++ /dev/null @@ -1,135 +0,0 @@ -#ifdef HAVE_STARKWARE - -#include "shared_context.h" -#include "ui_callbacks.h" - -void prepare_escape_3() { - uint8_t address[41]; - getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &global_sha3, chainConfig); - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; -} - -void prepare_escape_4() { - uint256_t amount, amountPre, quantum; - uint8_t decimals; - char *ticker = (char*)PIC(chainConfig->coinName); - - if (dataContext.tokenContext.quantumIndex == MAX_TOKEN) { - decimals = WEI_TO_ETHER; - } - else { - tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; - decimals = token->decimals; - ticker = (char*)token->ticker; - } - readu256BE(dataContext.tokenContext.data + 4 + 32 + 32 + 32, &amountPre); - readu256BE(dataContext.tokenContext.quantum, &quantum); - mul256(&amountPre, &quantum, &amount); - tostring256(&amount, 10, (char*)(G_io_apdu_buffer + 100), 100); - strcpy(strings.common.fullAmount, ticker); - adjustDecimals((char*)(G_io_apdu_buffer + 100), strlen((char*)(G_io_apdu_buffer + 100)), strings.common.fullAmount + strlen(ticker), 50 - strlen(ticker), decimals); -} - -void prepare_escape_5() { - snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, dataContext.tokenContext.data + 4 + 32); -} - -void prepare_escape_6() { - snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + 32 - 4)); -} - -UX_STEP_NOCB(ux_approval_starkware_escape_1_step, - pnn, - { - &C_icon_eye, - "Review", - "transaction", - }); - -UX_STEP_NOCB( - ux_approval_starkware_escape_2_step, - bnnn_paging, - { - .title = "Escape", - .text = " " - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_escape_3_step, - bnnn_paging, - prepare_escape_3(), - { - .title = "Contract Name", - .text = strings.common.fullAddress, - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_escape_4_step, - bnnn_paging, - prepare_escape_4(), - { - .title = "Amount", - .text = strings.common.fullAmount - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_escape_5_step, - bnnn_paging, - prepare_escape_5(), - { - .title = "Master Account", - .text = strings.tmp.tmp - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_escape_6_step, - bnnn_paging, - prepare_escape_6(), - { - .title = "Token Account", - .text = strings.common.fullAddress - }); - -UX_STEP_NOCB( - ux_approval_starkware_escape_7_step, - bnnn_paging, - { - .title = "Max Fees", - .text = strings.common.maxFee, - }); - -UX_STEP_CB( - ux_approval_starkware_escape_8_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); - -UX_STEP_CB( - ux_approval_starkware_escape_9_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -UX_FLOW(ux_approval_starkware_escape_flow, - &ux_approval_starkware_escape_1_step, - &ux_approval_starkware_escape_2_step, - &ux_approval_starkware_escape_3_step, - &ux_approval_starkware_escape_4_step, - &ux_approval_starkware_escape_5_step, - &ux_approval_starkware_escape_6_step, - &ux_approval_starkware_escape_7_step, - &ux_approval_starkware_escape_8_step, - &ux_approval_starkware_escape_9_step -); - -#endif diff --git a/src_features/stark_contract_register/ui_flow_stark_register.c b/src_features/stark_contract_register/ui_flow_stark_register.c deleted file mode 100644 index 8080585..0000000 --- a/src_features/stark_contract_register/ui_flow_stark_register.c +++ /dev/null @@ -1,123 +0,0 @@ -#ifdef HAVE_STARKWARE - -#include "shared_context.h" -#include "ui_callbacks.h" - -void prepare_register_3() { - uint8_t address[41]; - getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &global_sha3, chainConfig); - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; -} - -void prepare_register_4() { - uint8_t privateKeyData[32]; - uint8_t address[41]; - cx_ecfp_private_key_t privateKey; - cx_ecfp_public_key_t publicKey; - os_perso_derive_node_bip32(CX_CURVE_256K1, tmpCtx.transactionContext.bip32Path, - tmpCtx.transactionContext.pathLength, - privateKeyData, NULL); - cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); - io_seproxyhal_io_heartbeat(); - cx_ecfp_generate_pair(CX_CURVE_256K1, &publicKey, &privateKey, 1); - os_memset(&privateKey, 0, sizeof(privateKey)); - os_memset(privateKeyData, 0, sizeof(privateKeyData)); - io_seproxyhal_io_heartbeat(); - getEthAddressStringFromKey(&publicKey, address, &global_sha3, chainConfig); - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; -} - -void prepare_register_5() { - snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, dataContext.tokenContext.data + 4); -} - -UX_STEP_NOCB(ux_approval_starkware_register_1_step, - pnn, - { - &C_icon_eye, - "Review", - "transaction", - }); - -UX_STEP_NOCB( - ux_approval_starkware_register_2_step, - bnnn_paging, - { - .title = "Registration", - .text = " " - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_register_3_step, - bnnn_paging, - prepare_register_3(), - { - .title = "Contract Name", - .text = strings.common.fullAddress, - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_register_4_step, - bnnn_paging, - prepare_register_4(), - { - .title = "From ETH address", - .text = strings.common.fullAddress - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_register_5_step, - bnnn_paging, - prepare_register_5(), - { - .title = "Master account", - .text = strings.tmp.tmp - }); - - -UX_STEP_NOCB( - ux_approval_starkware_register_6_step, - bnnn_paging, - { - .title = "Max Fees", - .text = strings.common.maxFee, - }); - -UX_STEP_CB( - ux_approval_starkware_register_7_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); - -UX_STEP_CB( - ux_approval_starkware_register_8_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -UX_FLOW(ux_approval_starkware_register_flow, - &ux_approval_starkware_register_1_step, - &ux_approval_starkware_register_2_step, - &ux_approval_starkware_register_3_step, - &ux_approval_starkware_register_4_step, - &ux_approval_starkware_register_5_step, - &ux_approval_starkware_register_6_step, - &ux_approval_starkware_register_7_step, - &ux_approval_starkware_register_8_step -); - -#endif - diff --git a/src_features/stark_contract_verifyEscape/ui_flow_stark_verify_escape.c b/src_features/stark_contract_verifyEscape/ui_flow_stark_verify_escape.c deleted file mode 100644 index 9dd732b..0000000 --- a/src_features/stark_contract_verifyEscape/ui_flow_stark_verify_escape.c +++ /dev/null @@ -1,76 +0,0 @@ -#ifdef HAVE_STARKWARE - -#include "shared_context.h" -#include "ui_callbacks.h" - -void prepare_verify_escape_3() { - uint8_t address[41]; - getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &global_sha3, chainConfig); - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; -} - -UX_STEP_NOCB(ux_approval_starkware_verify_escape_1_step, - pnn, - { - &C_icon_eye, - "Review", - "transaction", - }); - -UX_STEP_NOCB( - ux_approval_starkware_verify_escape_2_step, - bnnn_paging, - { - .title = "Verify Escape", - .text = " " - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_verify_escape_3_step, - bnnn_paging, - prepare_verify_escape_3(), - { - .title = "Contract Name", - .text = strings.common.fullAddress, - }); - -UX_STEP_NOCB( - ux_approval_starkware_verify_escape_4_step, - bnnn_paging, - { - .title = "Max Fees", - .text = strings.common.maxFee, - }); - -UX_STEP_CB( - ux_approval_starkware_verify_escape_5_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); - -UX_STEP_CB( - ux_approval_starkware_verify_escape_6_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -UX_FLOW(ux_approval_starkware_verify_escape_flow, - &ux_approval_starkware_verify_escape_1_step, - &ux_approval_starkware_verify_escape_2_step, - &ux_approval_starkware_verify_escape_3_step, - &ux_approval_starkware_verify_escape_4_step, - &ux_approval_starkware_verify_escape_5_step, - &ux_approval_starkware_verify_escape_6_step -); - -#endif diff --git a/src_features/stark_contract_verifyVaultId/ui_flow_stark_verifyVaultId.c b/src_features/stark_contract_verifyVaultId/ui_flow_stark_verifyVaultId.c deleted file mode 100644 index be3d529..0000000 --- a/src_features/stark_contract_verifyVaultId/ui_flow_stark_verifyVaultId.c +++ /dev/null @@ -1,115 +0,0 @@ -#ifdef HAVE_STARKWARE - -#include "shared_context.h" -#include "ui_callbacks.h" - -void prepare_verify_vault_id_2() { - if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) { - strcpy(strings.common.fullAddress, "Cancel Deposit"); - } - else - if (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM) { - strcpy(strings.common.fullAddress, "Reclaim Deposit"); - } - else - if (contractProvisioned == CONTRACT_STARKWARE_FULL_WITHDRAWAL) { - strcpy(strings.common.fullAddress, "Full Withdrawal"); - } - else - if (contractProvisioned == CONTRACT_STARKWARE_FREEZE) { - strcpy(strings.common.fullAddress, "Freeze"); - } -} - -void prepare_verify_vault_id_3() { - uint8_t address[41]; - getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &global_sha3, chainConfig); - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; -} - -void prepare_verify_vault_id_4() { - uint8_t offset = 0; - if ((contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_CANCEL) || (contractProvisioned == CONTRACT_STARKWARE_DEPOSIT_RECLAIM)) { - offset = 32; - } - snprintf(strings.common.fullAddress, 10, "%d", U4BE(dataContext.tokenContext.data, 4 + offset + 32 - 4)); -} - -UX_STEP_NOCB(ux_approval_starkware_verify_vault_id_1_step, - pnn, - { - &C_icon_eye, - "Review", - "transaction", - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_verify_vault_id_2_step, - bnnn_paging, - prepare_verify_vault_id_2(), - { - .title = strings.common.fullAddress, - .text = " " - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_verify_vault_id_3_step, - bnnn_paging, - prepare_verify_vault_id_3(), - { - .title = "Contract Name", - .text = strings.common.fullAddress, - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_verify_vault_id_4_step, - bnnn_paging, - prepare_verify_vault_id_4(), - { - .title = "Token Account", - .text = strings.common.fullAddress - }); - - -UX_STEP_NOCB( - ux_approval_starkware_verify_vault_id_5_step, - bnnn_paging, - { - .title = "Max Fees", - .text = strings.common.maxFee, - }); - -UX_STEP_CB( - ux_approval_starkware_verify_vault_id_6_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); - -UX_STEP_CB( - ux_approval_starkware_verify_vault_id_7_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -UX_FLOW(ux_approval_starkware_verify_vault_id_flow, - &ux_approval_starkware_verify_vault_id_1_step, - &ux_approval_starkware_verify_vault_id_2_step, - &ux_approval_starkware_verify_vault_id_3_step, - &ux_approval_starkware_verify_vault_id_4_step, - &ux_approval_starkware_verify_vault_id_5_step, - &ux_approval_starkware_verify_vault_id_6_step, - &ux_approval_starkware_verify_vault_id_7_step -); - -#endif - diff --git a/src_features/stark_contract_withdrawal/ui_flow_stark_withdrawal.c b/src_features/stark_contract_withdrawal/ui_flow_stark_withdrawal.c deleted file mode 100644 index 676bca2..0000000 --- a/src_features/stark_contract_withdrawal/ui_flow_stark_withdrawal.c +++ /dev/null @@ -1,109 +0,0 @@ -#ifdef HAVE_STARKWARE - -#include "shared_context.h" -#include "ui_callbacks.h" - -void prepare_register_4(); - -void prepare_withdraw_3() { - uint8_t address[41]; - getEthAddressStringFromBinary(tmpContent.txContent.destination, address, &global_sha3, chainConfig); - strings.common.fullAddress[0] = '0'; - strings.common.fullAddress[1] = 'x'; - os_memmove((unsigned char *)strings.common.fullAddress+2, address, 40); - strings.common.fullAddress[42] = '\0'; -} - -void prepare_withdraw_5() { - char *ticker = (char*)PIC(chainConfig->coinName); - - if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { - tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; - ticker = (char*)token->ticker; - } - strcpy(strings.common.fullAmount, ticker); -} - -UX_STEP_NOCB(ux_approval_starkware_withdraw_1_step, - pnn, - { - &C_icon_eye, - "Review", - "transaction", - }); - -UX_STEP_NOCB( - ux_approval_starkware_withdraw_2_step, - bnnn_paging, - { - .title = "Withdrawal", - .text = " " - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_withdraw_3_step, - bnnn_paging, - prepare_withdraw_3(), - { - .title = "Contract Name", - .text = strings.common.fullAddress, - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_withdraw_4_step, - bnnn_paging, - prepare_register_4(), - { - .title = "To Eth Address", - .text = strings.common.fullAddress - }); - -UX_STEP_NOCB_INIT( - ux_approval_starkware_withdraw_5_step, - bnnn_paging, - prepare_withdraw_5(), - { - .title = "Token Symbol", - .text = strings.common.fullAmount - }); - - -UX_STEP_NOCB( - ux_approval_starkware_withdraw_6_step, - bnnn_paging, - { - .title = "Max Fees", - .text = strings.common.maxFee, - }); - -UX_STEP_CB( - ux_approval_starkware_withdraw_7_step, - pbb, - io_seproxyhal_touch_tx_ok(NULL), - { - &C_icon_validate_14, - "Accept", - "and send", - }); - -UX_STEP_CB( - ux_approval_starkware_withdraw_8_step, - pb, - io_seproxyhal_touch_tx_cancel(NULL), - { - &C_icon_crossmark, - "Reject", - }); - -UX_FLOW(ux_approval_starkware_withdraw_flow, - &ux_approval_starkware_withdraw_1_step, - &ux_approval_starkware_withdraw_2_step, - &ux_approval_starkware_withdraw_3_step, - &ux_approval_starkware_withdraw_4_step, - &ux_approval_starkware_withdraw_5_step, - &ux_approval_starkware_withdraw_6_step, - &ux_approval_starkware_withdraw_7_step, - &ux_approval_starkware_withdraw_8_step -); - -#endif diff --git a/src_plugins/erc20/erc20_plugin.c b/src_plugins/erc20/erc20_plugin.c new file mode 100644 index 0000000..4bb3525 --- /dev/null +++ b/src_plugins/erc20/erc20_plugin.c @@ -0,0 +1,63 @@ +#include +#include "eth_plugin_interface.h" + +typedef struct erc20_parameters_t { + + uint8_t destinationAddress[20]; + uint8_t amount[32]; + +} erc20_parameters_t; + +void erc20_plugin_call(int message, void *parameters) { + + switch(message) { + case ETH_PLUGIN_INIT_CONTRACT: { + ethPluginInitContract_t *msg = (ethPluginInitContract_t*)parameters; + PRINTF("erc20 plugin init\n"); + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case ETH_PLUGIN_PROVIDE_PARAMETER : { + ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t*)parameters; + erc20_parameters_t *context = (erc20_parameters_t*)msg->pluginContext; + PRINTF("erc20 plugin provide parameter %d %.*H\n", msg->parameterOffset, 32, msg->parameter); + switch(msg->parameterOffset) { + case 4: + memmove(context->destinationAddress, msg->parameter + 12, 20); + msg->result = ETH_PLUGIN_RESULT_OK; + break; + case 4 + 32: + memmove(context->amount, msg->parameter, 32); + msg->result = ETH_PLUGIN_RESULT_OK; + break; + default: + PRINTF("Unhandled parameter offset\n"); + break; + } + } + break; + + case ETH_PLUGIN_FINALIZE: { + ethPluginFinalize_t *msg = (ethPluginFinalize_t*)parameters; + erc20_parameters_t *context = (erc20_parameters_t*)msg->pluginContext; + PRINTF("erc20 plugin finalize\n"); + msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination; + msg->amount = context->amount; + msg->address = context->destinationAddress; + msg->uiType = ETH_UI_TYPE_AMOUNT_ADDRESS; + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case ETH_PLUGIN_PROVIDE_TOKEN: { + ethPluginProvideToken_t *msg = (ethPluginProvideToken_t*)parameters; + PRINTF("erc20 plugin provide token %d\n", (msg->token1 != NULL)); + msg->result = (msg->token1 != NULL ? ETH_PLUGIN_RESULT_OK : ETH_PLUGIN_RESULT_FALLBACK); + } + break; + + default: + PRINTF("Unhandled message %d\n", message); + } +} diff --git a/src_plugins/starkware/starkware_plugin.c b/src_plugins/starkware/starkware_plugin.c new file mode 100644 index 0000000..555e62f --- /dev/null +++ b/src_plugins/starkware/starkware_plugin.c @@ -0,0 +1,444 @@ +#include +#include "eth_plugin_interface.h" +#include "shared_context.h" // TODO : rewrite as independant code +#include "eth_plugin_internal.h" // TODO : rewrite as independant code + +typedef enum { + + STARKWARE_REGISTER = 0, + STARKWARE_DEPOSIT_TOKEN, + STARKWARE_DEPOSIT_ETH, + STARKWARE_DEPOSIT_CANCEL, + STARKWARE_DEPOSIT_RECLAIM, + STARKWARE_WITHDRAW, + STARKWARE_FULL_WITHDRAW, + STARKWARE_FREEZE, + STARKWARE_ESCAPE, + STARKWARE_VERIFY_ESCAPE + +} starkwareSelector_t; + +// register : starkkey (32), drop param 2 + // Registration + // Contract Name + // From ETH address + // Master account +// deposit token : verify tokenId (32), vaultId (4), quantized Amount (32) + // Deposit + // Contract Name + // Token Account + // Amount +// deposit : verify tokenId (32), vaultId (4) + // Flow similar to deposit +// deposit cancel, deposit reclaim : tokenId (32) ignored, vaultId(4) +// full withdrawal, freeze : vaultId (4) + // Cancel Deposit | Reclaim Deposit | Full Withdrawal | Freeze + // Contract Name + // Token Account +// withdrawal : verify tokenId (32) + // Withdrawal + // Contract Name + // To Eth Address + // Token Symbol +// escape : starkkey (32), vaultId (4), verify tokenId (32), quantized Amount (32) + // Escape + // Contract Name + // Amount + // Master Account + // Token Account +// verify escape : escapeProof (ignore) + // Verify Escape + // Contract Name + +static const uint8_t STARKWARE_EXPECTED_DATA_SIZE[] = { + 4 + 32, + 4 + 32 + 32 + 32, + 4 + 32 + 32, + 4 + 32 + 32, + 4 + 32 + 32, + 4 + 32, + 4 + 32, + 4 + 32, + 4 + 32 + 32 + 32 + 32, + 0 +}; + +static const uint8_t STARKWARE_NUM_SCREENS[] = { + 4 - 1, + 4 - 1, + 4 - 1, + 3 - 1, + 3 - 1, + 4 - 1, + 3 - 1, + 3 - 1, + 5 - 1, + 2 - 1 +}; + +typedef struct starkware_parameters_t { + + uint8_t vaultId[4]; + uint8_t selectorIndex; + uint8_t starkKey[32]; + uint8_t amount[32]; + uint8_t validToken; + +} starkware_parameters_t; + +// TODO : rewrite as independant code +bool starkware_verify_token_id(uint8_t *tmp32, uint8_t *tokenId) { + if (quantumSet) { + tokenDefinition_t *currentToken = NULL; + if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + currentToken = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + } + compute_token_id(&global_sha3, + (currentToken != NULL ? currentToken->address : NULL), + dataContext.tokenContext.quantum, tmp32); + if (memcmp(tokenId, tmp32, 32) != 0) { + PRINTF("Token ID not matching - computed %.*H\n", 32, tmp32); + PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum); + PRINTF("Requested %.*H\n", 32, tokenId); + return false; + } + } + else { + PRINTF("Quantum not set\n"); + return false; + } + return true; +} + +void starkware_print_vault_id(uint32_t vaultId, char *destination) { + snprintf(destination, 10, "%d", vaultId); +} + +void starkware_print_stark_key(uint8_t *starkKey, char *destination) { + snprintf(destination, 70, "0x%.*H", 32, starkKey); +} + +// TODO : rewrite as independant code +void starkware_print_eth_address(uint8_t *address, char *destination) { + destination[0] = '0'; + destination[1] = 'x'; + getEthAddressStringFromBinary(address, destination + 2, &global_sha3, chainConfig); + destination[42] = '\0'; +} + +// TODO : rewrite as independant code +void starkware_print_amount(uint8_t *amountData, char *destination, bool forEscape) { + uint256_t amount, amountPre, quantum; + uint8_t decimals; + char *ticker = (char*)PIC(chainConfig->coinName); + + if ((amountData == NULL) || (forEscape && (dataContext.tokenContext.quantumIndex == MAX_TOKEN))) { + decimals = WEI_TO_ETHER; + if (!forEscape) { + convertUint256BE(tmpContent.txContent.value.value, tmpContent.txContent.value.length, &amountPre); + } + else { + readu256BE(amountData, &amountPre); + } + } + else { + tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + decimals = token->decimals; + ticker = (char*)token->ticker; + readu256BE(amountData, &amountPre); + } + readu256BE(dataContext.tokenContext.quantum, &quantum); + mul256(&amountPre, &quantum, &amount); + tostring256(&amount, 10, (char*)(G_io_apdu_buffer + 100), 100); + strcpy(destination, ticker); + adjustDecimals((char*)(G_io_apdu_buffer + 100), strlen((char*)(G_io_apdu_buffer + 100)), destination + strlen(ticker), 50 - strlen(ticker), decimals); +} + +// TODO : rewrite as independant code +void starkware_print_ticker(char *destination) { + char *ticker = (char*)PIC(chainConfig->coinName); + + if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + tokenDefinition_t *token = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + ticker = (char*)token->ticker; + } + strcpy(destination, ticker); +} + +// TODO : rewrite as independant code +void starkware_get_source_address(char *destination) { + uint8_t privateKeyData[32]; + cx_ecfp_private_key_t privateKey; + cx_ecfp_public_key_t publicKey; + os_perso_derive_node_bip32(CX_CURVE_256K1, tmpCtx.transactionContext.bip32Path, + tmpCtx.transactionContext.pathLength, + privateKeyData, NULL); + cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); + io_seproxyhal_io_heartbeat(); + cx_ecfp_generate_pair(CX_CURVE_256K1, &publicKey, &privateKey, 1); + os_memset(&privateKey, 0, sizeof(privateKey)); + os_memset(privateKeyData, 0, sizeof(privateKeyData)); + io_seproxyhal_io_heartbeat(); + destination[0] = '0'; + destination[1] = 'x'; + getEthAddressStringFromKey(&publicKey, destination + 2, &global_sha3, chainConfig); + destination[42] = '\0'; +} + +void starkware_plugin_call(int message, void *parameters) { + + switch(message) { + case ETH_PLUGIN_INIT_CONTRACT: { + uint8_t i; + ethPluginInitContract_t *msg = (ethPluginInitContract_t*)parameters; + starkware_parameters_t *context = (starkware_parameters_t*)msg->pluginContext; + PRINTF("starkware plugin init\n"); + for (i=0; iselector, SELECTOR_SIZE) == 0) { + context->selectorIndex = i; + break; + } + } + if (i == NUM_STARKWARE_SELECTORS) { + PRINTF("Unknown selector %.*H\n", SELECTOR_SIZE, msg->selector); + break; + } + if (STARKWARE_EXPECTED_DATA_SIZE[context->selectorIndex] != 0) { + if (msg->dataSize != STARKWARE_EXPECTED_DATA_SIZE[context->selectorIndex]) { + PRINTF("Unexpected data size for command %d expected %d got %d\n", context->selectorIndex, + STARKWARE_EXPECTED_DATA_SIZE[context->selectorIndex], msg->dataSize); + break; + } + } + context->validToken = true; + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case ETH_PLUGIN_PROVIDE_PARAMETER : { + ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t*)parameters; + starkware_parameters_t *context = (starkware_parameters_t*)msg->pluginContext; + PRINTF("starkware plugin provide parameter %d %.*H\n", msg->parameterOffset, 32, msg->parameter); + // Ignore for verify escape + if (context->selectorIndex == STARKWARE_VERIFY_ESCAPE) { + msg->result = ETH_PLUGIN_RESULT_OK; + break; + } + switch(msg->parameterOffset) { + case 4: + switch(context->selectorIndex) { + case STARKWARE_REGISTER: + case STARKWARE_ESCAPE: + memmove(context->starkKey, msg->parameter, 32); + break; + case STARKWARE_DEPOSIT_CANCEL: + case STARKWARE_DEPOSIT_RECLAIM: + break; + case STARKWARE_FULL_WITHDRAW: + case STARKWARE_FREEZE: + memmove(context->vaultId, msg->parameter + 32 - 4, 4); + break; + case STARKWARE_DEPOSIT_ETH: + case STARKWARE_DEPOSIT_TOKEN: + case STARKWARE_WITHDRAW: + context->validToken = starkware_verify_token_id(context->amount, msg->parameter); + break; + default: + break; + } + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + case 4 + 32: + switch(context->selectorIndex) { + case STARKWARE_DEPOSIT_CANCEL: + case STARKWARE_DEPOSIT_RECLAIM: + case STARKWARE_DEPOSIT_ETH: + case STARKWARE_DEPOSIT_TOKEN: + case STARKWARE_ESCAPE: + memmove(context->vaultId, msg->parameter + 32 - 4, 4); + break; + default: + break; + } + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + case 4 + 32 + 32: + switch(context->selectorIndex) { + case STARKWARE_DEPOSIT_TOKEN: + memmove(context->amount, msg->parameter, 32); + break; + case STARKWARE_ESCAPE: + context->validToken = starkware_verify_token_id(context->amount, msg->parameter); + break; + default: + break; + } + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + case 4 + 32 + 32 + 32: + switch(context->selectorIndex) { + case STARKWARE_ESCAPE: + memmove(context->amount, msg->parameter, 32); + break; + default: + break; + } + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + default: + PRINTF("Unhandled parameter offset\n"); + break; + } + } + break; + + case ETH_PLUGIN_FINALIZE: { + ethPluginFinalize_t *msg = (ethPluginFinalize_t*)parameters; + starkware_parameters_t *context = (starkware_parameters_t*)msg->pluginContext; + PRINTF("starkware plugin finalize\n"); + if (!context->validToken) { + msg->result = ETH_PLUGIN_RESULT_FALLBACK; + } + else { + msg->uiType = ETH_UI_TYPE_GENERIC; + msg->numScreens = STARKWARE_NUM_SCREENS[context->selectorIndex]; + msg->result = ETH_PLUGIN_RESULT_OK; + } + } + break; + + case ETH_PLUGIN_QUERY_CONTRACT_ID: { + ethQueryContractID_t *msg = (ethQueryContractID_t*)parameters; + starkware_parameters_t *context = (starkware_parameters_t*)msg->pluginContext; + PRINTF("starkware query contract id\n"); + switch(context->selectorIndex) { + case STARKWARE_REGISTER: + strcpy(msg->name, "Register"); + break; + case STARKWARE_DEPOSIT_TOKEN: + case STARKWARE_DEPOSIT_ETH: + strcpy(msg->name, "Deposit"); + break; + case STARKWARE_DEPOSIT_CANCEL: + strcpy(msg->name, "Cancel Deposit"); + break; + case STARKWARE_DEPOSIT_RECLAIM: + strcpy(msg->name, "Reclaim Deposit"); + break; + case STARKWARE_WITHDRAW: + strcpy(msg->name, "Withdrawal"); + break; + case STARKWARE_FULL_WITHDRAW: + strcpy(msg->name, "Full Withdrawal"); + break; + case STARKWARE_FREEZE: + strcpy(msg->name, "Freeze"); + break; + case STARKWARE_ESCAPE: + strcpy(msg->name, "Escape"); + break; + case STARKWARE_VERIFY_ESCAPE: + strcpy(msg->name, "Verify Escape"); + break; + default: + break; + } + strcpy(msg->version, "Starkware"); + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case ETH_PLUGIN_QUERY_CONTRACT_UI: { + ethQueryContractUI_t *msg = (ethQueryContractUI_t*)parameters; + starkware_parameters_t *context = (starkware_parameters_t*)msg->pluginContext; + switch(msg->screenIndex) { + case 0: + strcpy(msg->title, "Contract Name"); + starkware_print_eth_address(tmpContent.txContent.destination, msg->msg); + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + case 1: + switch(context->selectorIndex) { + case STARKWARE_REGISTER: + strcpy(msg->title, "From ETH Address"); + starkware_get_source_address(msg->msg); + break; + case STARKWARE_DEPOSIT_TOKEN: + case STARKWARE_DEPOSIT_ETH: + case STARKWARE_DEPOSIT_CANCEL: + case STARKWARE_DEPOSIT_RECLAIM: + case STARKWARE_FULL_WITHDRAW: + case STARKWARE_FREEZE: + strcpy(msg->title, "Token Account"); + starkware_print_vault_id(U4BE(context->vaultId, 0), msg->msg); + break; + case STARKWARE_WITHDRAW: + strcpy(msg->title, "To ETH Address"); + starkware_print_eth_address(tmpContent.txContent.destination, msg->msg); + break; + case STARKWARE_ESCAPE: + strcpy(msg->title, "Amount"); + starkware_print_amount(context->amount, msg->msg, true); + break; + default: + PRINTF("Unexpected screen %d for %d\n", msg->screenIndex, context->selectorIndex); + break; + } + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + case 2: + switch(context->selectorIndex) { + case STARKWARE_REGISTER: + case STARKWARE_ESCAPE: + strcpy(msg->title, "Master Account"); + PRINTF("Master account %s\n", msg->msg); + starkware_print_stark_key(context->starkKey, msg->msg); + break; + case STARKWARE_DEPOSIT_TOKEN: + case STARKWARE_DEPOSIT_ETH: + strcpy(msg->title, "Amount"); + starkware_print_amount( + (context->selectorIndex == STARKWARE_DEPOSIT_ETH ? NULL : context->amount), + msg->msg, false); + break; + case STARKWARE_WITHDRAW: + strcpy(msg->title, "Token Symbol"); + starkware_print_ticker(msg->msg); + break; + default: + PRINTF("Unexpected screen %d for %d\n", msg->screenIndex, context->selectorIndex); + break; + } + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + case 3: + switch(context->selectorIndex) { + case STARKWARE_ESCAPE: + strcpy(msg->title, "Token Account"); + starkware_print_vault_id(U4BE(context->vaultId, 0), msg->msg); + break; + default: + PRINTF("Unexpected screen %d for %d\n", msg->screenIndex, context->selectorIndex); + break; + } + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + default: + PRINTF("Unexpected screen %d for %d\n", msg->screenIndex, context->selectorIndex); + break; + } + } + break; + + default: + PRINTF("Unhandled message %d\n", message); + } +}