diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 61618ce..9ae8086 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -25,23 +25,25 @@ jobs: - name: Build an altcoin run: | make DEBUG=1 ALLOW_DATA=1 CHAIN=ethereum_classic + mv bin/app.elf ethereum_classic_nanos.elf - name: Upload altcoin binary uses: actions/upload-artifact@v2 with: name: ethereum_classic_nanos - path: bin + path: ./ethereum_classic_nanos.elf - name: Build Ethereum run: | make clean make DEBUG=1 ALLOW_DATA=1 + mv bin/app.elf ethereum_nanos.elf - name: Upload app binary uses: actions/upload-artifact@v2 with: name: ethereum_nanos - path: bin + path: ./ethereum_nanos.elf job_build_debug_nano_x: name: Build debug Nano X @@ -60,23 +62,25 @@ jobs: run: | make clean make BOLOS_SDK=$NANOX_SDK DEBUG=1 ALLOW_DATA=1 CHAIN=ethereum_classic + mv bin/app.elf ethereum_classic_nanox.elf - name: Upload altcoin binary uses: actions/upload-artifact@v2 with: name: ethereum_classic_nanox - path: bin + path: ./ethereum_classic_nanox.elf - name: Build Ethereum Nano X run: | make clean make BOLOS_SDK=$NANOX_SDK DEBUG=1 ALLOW_DATA=1 + mv bin/app.elf ethereum_nanox.elf - name: Upload app binary uses: actions/upload-artifact@v2 with: name: ethereum_nanox - path: bin + path: ./ethereum_nanox.elf jobs-e2e-tests: needs: [job_build_debug_nano_s, job_build_debug_nano_x] @@ -104,6 +108,9 @@ jobs: uses: actions/download-artifact@v2 with: path: tests/elfs + - name: Gather elfs + run: | + cp `find . -name "*.elf"` ./tests/elfs - name: Run zemu tests run: | cd tests && yarn test diff --git a/Makefile b/Makefile index bfd27bf..6edd453 100755 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ endif ifeq ($(CHAIN),ethereum) # Lock the application on its standard path for 1.5. Please complain if non compliant APP_LOAD_PARAMS += --path "44'/60'" -DEFINES += CHAINID_UPCASE=\"ETHEREUM\" CHAINID_COINNAME=\"ETH\" CHAIN_KIND=CHAIN_KIND_ETHEREUM CHAIN_ID=0 +DEFINES += CHAINID_UPCASE=\"ETHEREUM\" CHAINID_COINNAME=\"ETH\" CHAIN_KIND=CHAIN_KIND_ETHEREUM CHAIN_ID=1 # Starkware integration APP_LOAD_PARAMS += --path "2645'/579218131'" DEFINES += HAVE_STARKWARE diff --git a/examples/ethBase.py b/examples/ethBase.py index b66b116..4fc1998 100755 --- a/examples/ethBase.py +++ b/examples/ethBase.py @@ -59,6 +59,9 @@ class UnsignedTransaction(Serializable): ('to', address), ('value', big_endian_int), ('data', binary), + ('chainid', big_endian_int), + ('dummy1', big_endian_int), + ('dummy2', big_endian_int), ] def unsigned_tx_from_tx(tx): diff --git a/examples/signTx.py b/examples/signTx.py index 9f0c4a0..f1d0337 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('--chainid', help="Chain ID (1 for Ethereum mainnet, 137 for Polygon, etc)", type=int) parser.add_argument('--descriptor', help="Optional descriptor") args = parser.parse_args() @@ -71,6 +72,10 @@ if args.data == None: else: args.data = decode_hex(args.data[2:]) +# default to Ethereum mainnet +if args.chainid == None: + args.chainid = 1 + amount = Decimal(args.amount) * 10**18 tx = UnsignedTransaction( @@ -80,10 +85,16 @@ tx = UnsignedTransaction( to=decode_hex(args.to[2:]), value=int(amount), data=args.data, + chainid=args.chainid, + dummy1=0, + dummy2=0 ) encodedTx = encode(tx, UnsignedTransaction) +# To test an EIP-2930 transaction, uncomment this line +#encodedTx = bytearray.fromhex("01f8e60380018402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0a4693c61390000000000000000000000000000000000000000000000000000000000000002f85bf859940000000000000000000000000000000000000102f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a780a09b8adcd2a4abd34b42d56fcd90b949f74ca9696dfe2b427bc39aa280bbf1924ca029af4a471bb2953b4e7933ea95880648552a9345424a1ac760189655ceb1832a") + dongle = getDongle(True) if args.descriptor != None: diff --git a/src/shared_context.h b/src/shared_context.h index f36c131..ea5e2b4 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -162,13 +162,14 @@ typedef enum { #endif } contract_call_t; +#define NETWORK_NAME_MAX_SIZE 12 + typedef struct txStringProperties_t { char fullAddress[43]; char fullAmount[50]; char maxFee[50]; - char nonce[8]; // 10M tx per account ought to be enough for everybody - char chainID[8]; // 10M different chainID ought to be enough for people to find a unique - // chainID for their token / chain. + char nonce[8]; // 10M tx per account ought to be enough for everybody + char network_name[NETWORK_NAME_MAX_SIZE]; } txStringProperties_t; #define SHARED_CTX_FIELD_1_SIZE 100 diff --git a/src/stark_crypto.c b/src/stark_crypto.c index 9217567..cb7ee65 100644 --- a/src/stark_crypto.c +++ b/src/stark_crypto.c @@ -60,7 +60,7 @@ void stark_get_amount_string(uint8_t *contractAddress, char *target100) { uint256_t amountPre, quantum, amount; uint8_t decimals; - char *ticker = (char *) PIC(chainConfig->coinName); + char *ticker = chainConfig->coinName; PRINTF("stark_get_amount_string %.*H\n", 20, contractAddress); diff --git a/src/utils.c b/src/utils.c index 401de81..e662209 100644 --- a/src/utils.c +++ b/src/utils.c @@ -57,20 +57,24 @@ int local_strchr(char *string, char ch) { // Almost like U4BE except that it takes `size` as a parameter. // The `strict` parameter defines whether we should throw in case of a length > 4. uint32_t u32_from_BE(uint8_t *in, uint8_t size, bool strict) { - uint32_t res = 0; - if (size == 1) { - res = in[0]; - } else if (size == 2) { - res = (in[0] << 8) | in[1]; - } else if (size == 3) { - res = (in[0] << 16) | (in[1] << 8) | in[2]; - } else if (size == 4) { - res = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3]; - } else if (strict && size != 0) { - PRINTF("Unexpected format\n"); - THROW(EXCEPTION); + switch (size) { + case 0: + return 0; + case 1: + return in[0]; + case 2: + return (in[0] << 8) | in[1]; + case 3: + return (in[0] << 16) | (in[1] << 8) | in[2]; + case 4: + return (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3]; + default: + if (strict) { + PRINTF("Unexpected format\n"); + THROW(EXCEPTION); + } + return (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3]; } - return res; } bool uint256_to_decimal(const uint8_t *value, size_t value_len, char *out, size_t out_len) { diff --git a/src_common/network.c b/src_common/network.c new file mode 100644 index 0000000..096f56d --- /dev/null +++ b/src_common/network.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include "network.h" +#include "os.h" +#include "shared_context.h" +#include "utils.h" + +// Mappping of chain ids to networks. +const network_info_t NETWORK_MAPPING[] = { + {.chain_id = 1, .name = "Ethereum", .ticker = "ETH "}, + {.chain_id = 3, .name = "Ropsten", .ticker = "ETH "}, + {.chain_id = 4, .name = "Rinkeby", .ticker = "ETH "}, + {.chain_id = 5, .name = "Goerli", .ticker = "ETH "}, + {.chain_id = 10, .name = "Optimism", .ticker = "ETH "}, + {.chain_id = 42, .name = "Kovan", .ticker = "ETH "}, + {.chain_id = 56, .name = "BSC", .ticker = "BNB "}, + {.chain_id = 100, .name = "xDai", .ticker = "xDAI "}, + {.chain_id = 137, .name = "Polygon", .ticker = "MATIC "}, + {.chain_id = 250, .name = "Fantom", .ticker = "FTM "}, + {.chain_id = 43114, .name = "Avalanche", .ticker = "AVAX "}}; + +uint32_t get_chain_id(void) { + uint32_t chain_id = 0; + + switch (txContext.txType) { + case LEGACY: + chain_id = u32_from_BE(txContext.content->v, txContext.content->vLength, true); + break; + case EIP2930: + chain_id = u32_from_BE(tmpContent.txContent.chainID.value, + tmpContent.txContent.chainID.length, + true); + break; + default: + PRINTF("Txtype `%d` not supported while generating chainID\n", txContext.txType); + break; + } + PRINTF("ChainID: %d\n", chain_id); + return chain_id; +} + +network_info_t *get_network(void) { + uint32_t chain_id = get_chain_id(); + for (uint8_t i = 0; i < sizeof(NETWORK_MAPPING) / sizeof(*NETWORK_MAPPING); i++) { + if (NETWORK_MAPPING[i].chain_id == chain_id) { + return (network_info_t *) PIC(&NETWORK_MAPPING[i]); + } + } + return NULL; +} + +char *get_network_name(void) { + network_info_t *network = get_network(); + if (network == NULL) { + return NULL; + } else { + return (char *) PIC(network->name); + } +} + +char *get_network_ticker(void) { + network_info_t *network = get_network(); + if (network == NULL) { + return chainConfig->coinName; + } else { + return (char *) PIC(network->ticker); + } +} diff --git a/src_common/network.h b/src_common/network.h new file mode 100644 index 0000000..e8c4f7a --- /dev/null +++ b/src_common/network.h @@ -0,0 +1,19 @@ +#include +#include "tokens.h" + +#define NETWORK_STRING_MAX_SIZE 12 + +typedef struct network_info_s { + const char name[NETWORK_STRING_MAX_SIZE]; + const char ticker[MAX_TICKER_LEN]; + uint32_t chain_id; +} network_info_t; + +// Returns the current chain id. Defaults to 0 if txType was not found. +uint32_t get_chain_id(void); +// Returns a pointer to the network struct, or NULL if there is none. +network_info_t *get_network(void); +// Returns a pointer to the network name, or NULL if there is none. +char *get_network_name(void); +// Returns a pointer to the network ticker, or chainConfig->coinName if there is none. +char *get_network_ticker(void); \ No newline at end of file diff --git a/src_features/signTx/feature_signTx.h b/src_features/signTx/feature_signTx.h index a25a650..fdcab3e 100644 --- a/src_features/signTx/feature_signTx.h +++ b/src_features/signTx/feature_signTx.h @@ -2,3 +2,5 @@ customStatus_e customProcessor(txContext_t *context); void finalizeParsing(bool direct); + +void ux_approve_tx(bool dataPresent); \ No newline at end of file diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 259ee78..14a889c 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -6,6 +6,7 @@ #include "stark_utils.h" #endif #include "eth_plugin_handler.h" +#include "network.h" #define ERR_SILENT_MODE_CHECK_FAILED 0x6001 @@ -195,7 +196,7 @@ void reportFinalizeError(bool direct) { void computeFees(char *displayBuffer, uint32_t displayBufferSize) { uint256_t gasPrice, startGas, uint256; - uint8_t *feeTicker = (uint8_t *) PIC(chainConfig->coinName); + char *feeTicker = get_network_ticker(); uint8_t tickerOffset = 0; uint32_t i; @@ -240,25 +241,14 @@ void computeFees(char *displayBuffer, uint32_t displayBufferSize) { void finalizeParsing(bool direct) { char displayBuffer[50]; uint8_t decimals = WEI_TO_ETHER; - uint8_t *ticker = (uint8_t *) PIC(chainConfig->coinName); + char *ticker = get_network_ticker(); ethPluginFinalize_t pluginFinalize; tokenDefinition_t *token1 = NULL, *token2 = NULL; bool genericUI = true; // Verify the chain - if (chainConfig->chainId != 0) { - uint32_t id = 0; - - if (txContext.txType == LEGACY) { - id = u32_from_BE(txContext.content->v, txContext.content->vLength, true); - } else if (txContext.txType == EIP2930) { - id = u32_from_BE(txContext.content->chainID.value, - txContext.content->chainID.length, - false); - } else { - PRINTF("TxType `%u` not supported while checking for chainID\n", txContext.txType); - return; - } + if (chainConfig->chainId != ETHEREUM_MAINNET_CHAINID) { + uint32_t id = get_chain_id(); if (chainConfig->chainId != id) { PRINTF("Invalid chainID %u expected %u\n", id, chainConfig->chainId); @@ -342,7 +332,7 @@ void finalizeParsing(bool direct) { tmpContent.txContent.destinationLength = 20; if (token1 != NULL) { decimals = token1->decimals; - ticker = token1->ticker; + ticker = (char *) token1->ticker; } break; default: @@ -404,26 +394,22 @@ void finalizeParsing(bool direct) { // Prepare chainID field if (genericUI) { - if (txContext.txType == LEGACY) { - uint32_t id = u32_from_BE(txContext.content->v, txContext.content->vLength, true); - PRINTF("Chain ID: %u\n", id); - uint8_t res = - snprintf(strings.common.chainID, sizeof(strings.common.chainID), "%d", id); - if (res >= sizeof(strings.common.chainID)) { + char *name = get_network_name(); + if (name == NULL) { + // No network name found so simply copy the chain ID as the network name. + uint32_t chain_id = get_chain_id(); + uint8_t res = snprintf(strings.common.network_name, + sizeof(strings.common.network_name), + "%d", + chain_id); + if (res >= sizeof(strings.common.network_name)) { // If the return value is higher or equal to the size passed in as parameter, then // the output was truncated. Return the appropriate error code. THROW(0x6502); } - } else if (txContext.txType == EIP2930) { - uint256_t chainID; - convertUint256BE(tmpContent.txContent.chainID.value, - tmpContent.txContent.chainID.length, - &chainID); - tostring256(&chainID, 10, displayBuffer, sizeof(displayBuffer)); - strncpy(strings.common.chainID, displayBuffer, sizeof(strings.common.chainID)); } else { - PRINTF("Txtype `%u` not supported while generating chainID\n", txContext.txType); - return; + // Network name found, simply copy it. + strncpy(strings.common.network_name, name, sizeof(strings.common.network_name)); } } diff --git a/src_features/signTx/ui_flow_signTx.c b/src_features/signTx/ui_flow_signTx.c index 31de33d..6a8aaf4 100644 --- a/src_features/signTx/ui_flow_signTx.c +++ b/src_features/signTx/ui_flow_signTx.c @@ -2,6 +2,8 @@ #include "ui_callbacks.h" #include "chainConfig.h" #include "utils.h" +#include "feature_signTx.h" +#include "network.h" // clang-format off UX_STEP_NOCB( @@ -116,11 +118,11 @@ UX_STEP_NOCB( .text = strings.common.maxFee, }); UX_STEP_NOCB( - ux_approval_chainid_step, + ux_approval_network_step, bnnn_paging, { - .title = "Chain ID", - .text = strings.common.chainID, + .title = "Network", + .text = strings.common.network_name, }); UX_STEP_CB( ux_approval_accept_step, @@ -171,18 +173,9 @@ void ux_approve_tx(bool dataPresent) { ux_approval_tx_flow_[step++] = &ux_approval_nonce_step; } - uint32_t id; - if (txContext.txType == LEGACY) { - id = u32_from_BE(txContext.content->v, txContext.content->vLength, true); - } else if (txContext.txType == EIP2930) { - id = - u32_from_BE(txContext.content->chainID.value, txContext.content->chainID.length, false); - } else { - PRINTF("TxType `%u` not supported while preparing to approve tx\n", txContext.txType); - THROW(0x6501); - } - if (id != ETHEREUM_MAINNET_CHAINID) { - ux_approval_tx_flow_[step++] = &ux_approval_chainid_step; + uint32_t chain_id = get_chain_id(); + if (chainConfig->chainId == ETHEREUM_MAINNET_CHAINID && chain_id != chainConfig->chainId) { + ux_approval_tx_flow_[step++] = &ux_approval_network_step; } ux_approval_tx_flow_[step++] = &ux_approval_fees_step; ux_approval_tx_flow_[step++] = &ux_approval_accept_step; diff --git a/src_plugins/eth2/eth2_plugin.c b/src_plugins/eth2/eth2_plugin.c index 6687902..6c18826 100644 --- a/src_plugins/eth2/eth2_plugin.c +++ b/src_plugins/eth2/eth2_plugin.c @@ -217,12 +217,12 @@ void eth2_plugin_call(int message, void *parameters) { switch (msg->screenIndex) { case 0: { // Amount screen uint8_t decimals = WEI_TO_ETHER; - uint8_t *ticker = (uint8_t *) PIC(chainConfig->coinName); + char *ticker = chainConfig->coinName; strcpy(msg->title, "Amount"); amountToString(tmpContent.txContent.value.value, tmpContent.txContent.value.length, decimals, - (char *) ticker, + ticker, msg->msg, 100); msg->result = ETH_PLUGIN_RESULT_OK; diff --git a/src_plugins/starkware/starkware_plugin.c b/src_plugins/starkware/starkware_plugin.c index cb4d584..6f0ffe5 100644 --- a/src_plugins/starkware/starkware_plugin.c +++ b/src_plugins/starkware/starkware_plugin.c @@ -294,7 +294,7 @@ void starkware_print_eth_address(uint8_t *address, char *destination) { 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); + char *ticker = chainConfig->coinName; if ((amountData == NULL) || (forEscape && (dataContext.tokenContext.quantumIndex == MAX_TOKEN))) { @@ -328,7 +328,7 @@ void starkware_print_amount(uint8_t *amountData, char *destination, bool forEsca // TODO : rewrite as independant code void starkware_print_ticker(char *destination) { - char *ticker = (char *) PIC(chainConfig->coinName); + char *ticker = chainConfig->coinName; if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { tokenDefinition_t *token = diff --git a/tests/snapshots/send/nanos/network.png b/tests/snapshots/send/nanos/network.png new file mode 100644 index 0000000..9dde424 Binary files /dev/null and b/tests/snapshots/send/nanos/network.png differ diff --git a/tests/snapshots/send/nanox/network.png b/tests/snapshots/send/nanox/network.png new file mode 100644 index 0000000..eb4b7b8 Binary files /dev/null and b/tests/snapshots/send/nanox/network.png differ diff --git a/tests/snapshots/send_bsc/nanos/amount_1.png b/tests/snapshots/send_bsc/nanos/amount_1.png index 4cd3e38..a25e91e 100644 Binary files a/tests/snapshots/send_bsc/nanos/amount_1.png and b/tests/snapshots/send_bsc/nanos/amount_1.png differ diff --git a/tests/snapshots/send_bsc/nanos/chainid.png b/tests/snapshots/send_bsc/nanos/chainid.png deleted file mode 100644 index acfe3d3..0000000 Binary files a/tests/snapshots/send_bsc/nanos/chainid.png and /dev/null differ diff --git a/tests/snapshots/send_bsc/nanos/fees.png b/tests/snapshots/send_bsc/nanos/fees.png index 2c12e0a..6a16a42 100644 Binary files a/tests/snapshots/send_bsc/nanos/fees.png and b/tests/snapshots/send_bsc/nanos/fees.png differ diff --git a/tests/snapshots/send_bsc/nanos/network.png b/tests/snapshots/send_bsc/nanos/network.png new file mode 100644 index 0000000..ef98ec6 Binary files /dev/null and b/tests/snapshots/send_bsc/nanos/network.png differ diff --git a/tests/snapshots/send_bsc/nanox/amount.png b/tests/snapshots/send_bsc/nanox/amount.png index c4f9260..9911bf3 100644 Binary files a/tests/snapshots/send_bsc/nanox/amount.png and b/tests/snapshots/send_bsc/nanox/amount.png differ diff --git a/tests/snapshots/send_bsc/nanox/chainid.png b/tests/snapshots/send_bsc/nanox/chainid.png deleted file mode 100644 index 53424d9..0000000 Binary files a/tests/snapshots/send_bsc/nanox/chainid.png and /dev/null differ diff --git a/tests/snapshots/send_bsc/nanox/fees.png b/tests/snapshots/send_bsc/nanox/fees.png index 71a1ef9..ac5d9db 100644 Binary files a/tests/snapshots/send_bsc/nanox/fees.png and b/tests/snapshots/send_bsc/nanox/fees.png differ diff --git a/tests/snapshots/send_bsc/nanox/network.png b/tests/snapshots/send_bsc/nanox/network.png new file mode 100644 index 0000000..b021901 Binary files /dev/null and b/tests/snapshots/send_bsc/nanox/network.png differ diff --git a/tests/snapshots/send_etc/nanos/accept.png b/tests/snapshots/send_etc/nanos/accept.png new file mode 100644 index 0000000..3158ea6 Binary files /dev/null and b/tests/snapshots/send_etc/nanos/accept.png differ diff --git a/tests/snapshots/send_etc/nanos/address_1.png b/tests/snapshots/send_etc/nanos/address_1.png new file mode 100644 index 0000000..f979f71 Binary files /dev/null and b/tests/snapshots/send_etc/nanos/address_1.png differ diff --git a/tests/snapshots/send_etc/nanos/address_2.png b/tests/snapshots/send_etc/nanos/address_2.png new file mode 100644 index 0000000..93c90c5 Binary files /dev/null and b/tests/snapshots/send_etc/nanos/address_2.png differ diff --git a/tests/snapshots/send_etc/nanos/address_3.png b/tests/snapshots/send_etc/nanos/address_3.png new file mode 100644 index 0000000..402c20d Binary files /dev/null and b/tests/snapshots/send_etc/nanos/address_3.png differ diff --git a/tests/snapshots/send_etc/nanos/amount_1.png b/tests/snapshots/send_etc/nanos/amount_1.png new file mode 100644 index 0000000..660399c Binary files /dev/null and b/tests/snapshots/send_etc/nanos/amount_1.png differ diff --git a/tests/snapshots/send_etc/nanos/amount_2.png b/tests/snapshots/send_etc/nanos/amount_2.png new file mode 100644 index 0000000..11ae75e Binary files /dev/null and b/tests/snapshots/send_etc/nanos/amount_2.png differ diff --git a/tests/snapshots/send_etc/nanos/amount_3.png b/tests/snapshots/send_etc/nanos/amount_3.png new file mode 100644 index 0000000..e042010 Binary files /dev/null and b/tests/snapshots/send_etc/nanos/amount_3.png differ diff --git a/tests/snapshots/send_etc/nanos/fees.png b/tests/snapshots/send_etc/nanos/fees.png new file mode 100644 index 0000000..85a8960 Binary files /dev/null and b/tests/snapshots/send_etc/nanos/fees.png differ diff --git a/tests/snapshots/send_etc/nanos/review.png b/tests/snapshots/send_etc/nanos/review.png new file mode 100644 index 0000000..2994983 Binary files /dev/null and b/tests/snapshots/send_etc/nanos/review.png differ diff --git a/tests/snapshots/send_etc/nanox/accept.png b/tests/snapshots/send_etc/nanox/accept.png new file mode 100644 index 0000000..7f2b8c8 Binary files /dev/null and b/tests/snapshots/send_etc/nanox/accept.png differ diff --git a/tests/snapshots/send_etc/nanox/address.png b/tests/snapshots/send_etc/nanox/address.png new file mode 100644 index 0000000..ce3e586 Binary files /dev/null and b/tests/snapshots/send_etc/nanox/address.png differ diff --git a/tests/snapshots/send_etc/nanox/amount.png b/tests/snapshots/send_etc/nanox/amount.png new file mode 100644 index 0000000..e83449a Binary files /dev/null and b/tests/snapshots/send_etc/nanox/amount.png differ diff --git a/tests/snapshots/send_etc/nanox/fees.png b/tests/snapshots/send_etc/nanox/fees.png new file mode 100644 index 0000000..2a101ca Binary files /dev/null and b/tests/snapshots/send_etc/nanox/fees.png differ diff --git a/tests/snapshots/send_etc/nanox/review.png b/tests/snapshots/send_etc/nanox/review.png new file mode 100644 index 0000000..8794afe Binary files /dev/null and b/tests/snapshots/send_etc/nanox/review.png differ diff --git a/tests/src/generic.js b/tests/src/generic.js index a470507..2fdcd2b 100644 --- a/tests/src/generic.js +++ b/tests/src/generic.js @@ -15,14 +15,24 @@ const sim_options_nanox = { }; const Resolve = require("path").resolve; -const NANOS_ELF_PATH = Resolve("elfs/ethereum_nanos/app.elf"); -const NANOX_ELF_PATH = Resolve("elfs/ethereum_nanox/app.elf"); +const NANOS_ELF_PATH = Resolve("elfs/ethereum_nanos.elf"); +const NANOX_ELF_PATH = Resolve("elfs/ethereum_nanox.elf"); + +const NANOS_ETH_LIB = { "Ethereum": NANOS_ELF_PATH }; +const NANOX_ETH_LIB = { "Ethereum": NANOX_ELF_PATH }; + +const NANOS_CLONE_ELF_PATH = Resolve("elfs/ethereum_classic_nanos.elf"); +const NANOX_CLONE_ELF_PATH = Resolve("elfs/ethereum_classic_nanox.elf"); const TIMEOUT = 1000000; module.exports = { NANOS_ELF_PATH, NANOX_ELF_PATH, + NANOS_ETH_LIB, + NANOX_ETH_LIB, + NANOS_CLONE_ELF_PATH, + NANOX_CLONE_ELF_PATH, sim_options_nanos, sim_options_nanox, TIMEOUT, diff --git a/tests/src/send.test.js b/tests/src/send.test.js index 37cbdee..7deb7c7 100644 --- a/tests/src/send.test.js +++ b/tests/src/send.test.js @@ -17,7 +17,7 @@ const ORIGINAL_SNAPSHOT_PATH_NANOX = ORIGINAL_SNAPSHOT_PATH_PREFIX + "nanox/"; const SNAPSHOT_PATH_NANOS = SNAPSHOT_PATH_PREFIX + "nanos/"; const SNAPSHOT_PATH_NANOX = SNAPSHOT_PATH_PREFIX + "nanox/"; -test("Transfer nanos", async () => { +test("Transfer Ether on Ethereum app nanos", async () => { jest.setTimeout(TIMEOUT); const sim = new Zemu(NANOS_ELF_PATH); @@ -106,7 +106,103 @@ test("Transfer nanos", async () => { } }); -test("Transfer nanox", async () => { +test("Transfer on network 5234 on Ethereum nanos", async () => { + jest.setTimeout(TIMEOUT); + const sim = new Zemu(NANOS_ELF_PATH); + + try { + await sim.start(sim_options_nanos); + + let transport = await sim.getTransport(); + + let buffer = Buffer.from("058000002C8000003C800000010000000000000000EB44850306DC4200825208945A321744667052AFFA8386ED49E00EF223CBFFC3876F9C9E7BF61818808214728080", "hex"); + + // Send transaction + let tx = transport.send(0xe0, 0x04, 0x00, 0x00, buffer); + let filename; + + await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()); + // Review tx + filename = "review.png"; + await sim.snapshot(SNAPSHOT_PATH_NANOS + filename); + const review = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_review = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(review).toEqual(expected_review); + + // Amount 1/3 + filename = "amount_1.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const amount_1 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_amount_1 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(amount_1).toEqual(expected_amount_1); + + // Amount 2/3 + filename = "amount_2.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const amount_2 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_amount_2 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(amount_2).toEqual(expected_amount_2); + + // Amount 3/3 + filename = "amount_3.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const amount_3 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_amount_3 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(amount_3).toEqual(expected_amount_3); + + // Address 1/3 + filename = "address_1.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const address_1 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_address_1 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(address_1).toEqual(expected_address_1); + + // Address 2/3 + filename = "address_2.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const address_2 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_address_2 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(address_2).toEqual(expected_address_2); + + // Address 3/3 + filename = "address_3.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const address_3 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_address_3 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(address_3).toEqual(expected_address_3); + + // Network + filename = "network.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const network = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_network = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(network).toEqual(expected_network); + + // Max Fees + filename = "fees.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const fees = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_fees = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(fees).toEqual(expected_fees); + + // Accept + filename = "accept.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const accept = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_accept = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(accept).toEqual(expected_accept); + + await sim.clickBoth(); + + await expect(tx).resolves.toEqual( + Buffer.from("08f3449bbc245669e26dd076986e11aa3117e2405ffe2ddc7a7e220f81326fbd91150515605c78119367be311345e9ff40c4e4ddb9ec0fd81f37035c3828f4c8b29000", 'hex') + ); + } finally { + await sim.close(); + } +}); + +test("Transfer Ether on Ethereum nanox", async () => { jest.setTimeout(TIMEOUT); const sim = new Zemu(NANOX_ELF_PATH); @@ -164,4 +260,73 @@ test("Transfer nanox", async () => { } finally { await sim.close(); } +}); + + +test("Transfer on network 5234 on Ethereum nanox", async () => { + jest.setTimeout(TIMEOUT); + const sim = new Zemu(NANOX_ELF_PATH); + + try { + await sim.start(sim_options_nanox); + + let transport = await sim.getTransport(); + + let buffer = Buffer.from("058000002C8000003C800000010000000000000000EB44850306DC4200825208945A321744667052AFFA8386ED49E00EF223CBFFC3876F9C9E7BF61818808214728080", "hex"); + + // Send transaction + let tx = transport.send(0xe0, 0x04, 0x00, 0x00, buffer); + let filename; + + await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()); + // Review tx + filename = "review.png"; + await sim.snapshot(SNAPSHOT_PATH_NANOX + filename); + const review = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_review = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(review).toEqual(expected_review); + + // Amount + filename = "amount.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const amount = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_amount = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(amount).toEqual(expected_amount); + + // Address + filename = "address.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const address = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_address = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(address).toEqual(expected_address); + + // Network + filename = "network.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const network = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_network = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(network).toEqual(expected_network); + + // Max Fees + filename = "fees.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const fees = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_fees = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(fees).toEqual(expected_fees); + + // Accept + filename = "accept.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const accept = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_accept = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(accept).toEqual(expected_accept); + + await sim.clickBoth(); + + await expect(tx).resolves.toEqual( + Buffer.from("08f3449bbc245669e26dd076986e11aa3117e2405ffe2ddc7a7e220f81326fbd91150515605c78119367be311345e9ff40c4e4ddb9ec0fd81f37035c3828f4c8b29000", 'hex') + ); + } finally { + await sim.close(); + } }); \ No newline at end of file diff --git a/tests/src/send_bsc.test.js b/tests/src/send_bsc.test.js index dda50a3..028f6cb 100644 --- a/tests/src/send_bsc.test.js +++ b/tests/src/send_bsc.test.js @@ -83,8 +83,8 @@ test("Transfer bsc nanos", async () => { const expected_address_3 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); expect(address_3).toEqual(expected_address_3); - // Chain ID - filename = "chainid.png"; + // Network name + filename = "network.png"; await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); const chainid = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); const expected_chainid = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); @@ -151,8 +151,8 @@ test("Transfer bsc nanox", async () => { const expected_address = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); expect(address).toEqual(expected_address); - // Chain ID - filename = "chainid.png"; + // Network name + filename = "network.png"; await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); const chainid = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); const expected_chainid = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); diff --git a/tests/src/send_etc.test.js b/tests/src/send_etc.test.js new file mode 100644 index 0000000..39da31c --- /dev/null +++ b/tests/src/send_etc.test.js @@ -0,0 +1,208 @@ +import "core-js/stable"; +import "regenerator-runtime/runtime"; +import Eth from "@ledgerhq/hw-app-eth"; +import { byContractAddress } from "@ledgerhq/hw-app-eth/erc20"; +import Zemu from "@zondax/zemu"; +import { TransportStatusError } from "@ledgerhq/errors"; +import { expect } from "../jest"; + +const {NANOS_ETH_LIB, NANOX_ETH_LIB, NANOS_CLONE_ELF_PATH, NANOX_CLONE_ELF_PATH, sim_options_nanos, sim_options_nanox, TIMEOUT} = require("generic.js"); + +const ORIGINAL_SNAPSHOT_PATH_PREFIX = "snapshots/send_etc/"; +const SNAPSHOT_PATH_PREFIX = "snapshots/tmp/"; + +const ORIGINAL_SNAPSHOT_PATH_NANOS = ORIGINAL_SNAPSHOT_PATH_PREFIX + "nanos/"; +const ORIGINAL_SNAPSHOT_PATH_NANOX = ORIGINAL_SNAPSHOT_PATH_PREFIX + "nanox/"; + +const SNAPSHOT_PATH_NANOS = SNAPSHOT_PATH_PREFIX + "nanos/"; +const SNAPSHOT_PATH_NANOX = SNAPSHOT_PATH_PREFIX + "nanox/"; + +test("Transfer on Ethereum clone app nanos", async () => { + jest.setTimeout(TIMEOUT); + const sim = new Zemu(NANOS_CLONE_ELF_PATH, NANOS_ETH_LIB); + + try { + await sim.start(sim_options_nanos); + + let transport = await sim.getTransport(); + + let buffer = Buffer.from("058000002C8000003C800000010000000000000000EB44850306DC4200825208945A321744667052AFFA8386ED49E00EF223CBFFC3876F9C9E7BF61818803D8080", "hex"); + + // Send transaction + let tx = transport.send(0xe0, 0x04, 0x00, 0x00, buffer); + let filename; + + await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()); + // Review tx + filename = "review.png"; + await sim.snapshot(SNAPSHOT_PATH_NANOS + filename); + const review = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_review = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(review).toEqual(expected_review); + + // Amount 1/3 + filename = "amount_1.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const amount_1 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_amount_1 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(amount_1).toEqual(expected_amount_1); + + // Amount 2/3 + filename = "amount_2.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const amount_2 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_amount_2 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(amount_2).toEqual(expected_amount_2); + + // Amount 3/3 + filename = "amount_3.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const amount_3 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_amount_3 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(amount_3).toEqual(expected_amount_3); + + // Address 1/3 + filename = "address_1.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const address_1 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_address_1 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(address_1).toEqual(expected_address_1); + + // Address 2/3 + filename = "address_2.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const address_2 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_address_2 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(address_2).toEqual(expected_address_2); + + // Address 3/3 + filename = "address_3.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const address_3 = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_address_3 = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(address_3).toEqual(expected_address_3); + + // Max Fees + filename = "fees.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const fees = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_fees = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(fees).toEqual(expected_fees); + + // Accept + filename = "accept.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOS + filename); + const accept = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOS + filename); + const expected_accept = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOS + filename); + expect(accept).toEqual(expected_accept); + + await sim.clickBoth(); + await expect(tx).resolves.toEqual( + Buffer.from("9e52b80e10cb82f3dc8345005e3da3f9cae1fb3f2b9a5df05b7cedba786c685fed381875af27d121beaa9efd8a7450975f9d45a26ba5aa331b7a8b26bcce95e6d09000", 'hex') + ); + } finally { + await sim.close(); + } +}); + +test("Transfer on network 5234 on Ethereum clone nanos", async () => { + jest.setTimeout(TIMEOUT); + const sim = new Zemu(NANOS_CLONE_ELF_PATH, NANOS_ETH_LIB); + + try { + await sim.start(sim_options_nanos); + + let transport = await sim.getTransport(); + + let buffer = Buffer.from("058000002C8000003C800000010000000000000000EB44850306DC4200825208945A321744667052AFFA8386ED49E00EF223CBFFC3876F9C9E7BF61818808214728080", "hex"); + + // Send transaction + let tx = transport.send(0xe0, 0x04, 0x00, 0x00, buffer); + + await expect(tx).rejects.toEqual(new TransportStatusError(0x6a80)); + + } finally { + await sim.close(); + } +}); + +test("Transfer on Ethereum clone nanox", async () => { + jest.setTimeout(TIMEOUT); + const sim = new Zemu(NANOX_CLONE_ELF_PATH, NANOX_ETH_LIB); + + try { + await sim.start(sim_options_nanox); + + let transport = await sim.getTransport(); + let buffer = Buffer.from("058000002C8000003C800000010000000000000000EB44850306DC4200825208945A321744667052AFFA8386ED49E00EF223CBFFC3876F9C9E7BF61818803D8080", "hex"); + + // Send transaction + let tx = transport.send(0xe0, 0x04, 0x00, 0x00, buffer); + let filename; + + await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()); + // Review tx + filename = "review.png"; + await sim.snapshot(SNAPSHOT_PATH_NANOX + filename); + const review = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_review = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(review).toEqual(expected_review); + + // Amount + filename = "amount.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const amount = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_amount = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(amount).toEqual(expected_amount); + + // Address + filename = "address.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const address = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_address = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(address).toEqual(expected_address); + + // Max Fees + filename = "fees.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const fees = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_fees = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(fees).toEqual(expected_fees); + + // Accept + filename = "accept.png"; + await sim.clickRight(SNAPSHOT_PATH_NANOX + filename); + const accept = Zemu.LoadPng2RGB(SNAPSHOT_PATH_NANOX + filename); + const expected_accept = Zemu.LoadPng2RGB(ORIGINAL_SNAPSHOT_PATH_NANOX + filename); + expect(accept).toEqual(expected_accept); + + await sim.clickBoth(); + + await expect(tx).resolves.toEqual( + Buffer.from("9e52b80e10cb82f3dc8345005e3da3f9cae1fb3f2b9a5df05b7cedba786c685fed381875af27d121beaa9efd8a7450975f9d45a26ba5aa331b7a8b26bcce95e6d09000", 'hex') + ); + } finally { + await sim.close(); + } +}); + + +test("Transfer on network 5234 on Ethereum clone nanox", async () => { + jest.setTimeout(TIMEOUT); + const sim = new Zemu(NANOX_CLONE_ELF_PATH, NANOX_ETH_LIB); + + try { + await sim.start(sim_options_nanox); + + let transport = await sim.getTransport(); + + let buffer = Buffer.from("058000002C8000003C800000010000000000000000EB44850306DC4200825208945A321744667052AFFA8386ED49E00EF223CBFFC3876F9C9E7BF61818808214728080", "hex"); + + // Send transaction + let tx = transport.send(0xe0, 0x04, 0x00, 0x00, buffer); + + await expect(tx).rejects.toEqual(new TransportStatusError(0x6a80)); + } finally { + await sim.close(); + } +}); \ No newline at end of file