diff --git a/.github/workflows/swap-ci-workflow.yml b/.github/workflows/swap-ci-workflow.yml new file mode 100644 index 00000000..24037753 --- /dev/null +++ b/.github/workflows/swap-ci-workflow.yml @@ -0,0 +1,18 @@ +--- +name: Swap functional tests + +on: + workflow_dispatch: + push: + branches: + - master + - develop + pull_request: + +jobs: + job_functional_tests: + uses: LedgerHQ/app-exchange/.github/workflows/reusable_swap_functional_tests.yml@develop + with: + repo_for_ton: ${{ github.repository }} + branch_for_ton: ${{ github.ref }} + test_filter: '"Ton or ton"' diff --git a/Makefile b/Makefile index 34b76508..1f5c8119 100644 --- a/Makefile +++ b/Makefile @@ -27,8 +27,8 @@ endif APPNAME = "TON" APPVERSION_M = 2 -APPVERSION_N = 3 -APPVERSION_P = 0 +APPVERSION_N = 4 +APPVERSION_P = 1 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" APP_SOURCE_PATH += src @@ -53,6 +53,7 @@ VARIANT_VALUES = TON # HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1 # HAVE_APPLICATION_FLAG_LIBRARY = 1 +ENABLE_SWAP = 1 ENABLE_BLUETOOTH = 1 # ENABLE_NFC = 1 @@ -60,7 +61,7 @@ ENABLE_NBGL_QRCODE = 1 # ENABLE_NBGL_KEYBOARD = 1 # ENABLE_NBGL_KEYPAD = 1 -# DISABLE_STANDARD_APP_FILES = 1 +# DISABLE_STANDARD_APP_FILES = 1 # DISABLE_DEFAULT_IO_SEPROXY_BUFFER_SIZE = 1 # To allow custom size declaration # DISABLE_STANDARD_APP_DEFINES = 1 # Will set all the following disablers # DISABLE_STANDARD_SNPRINTF = 1 @@ -71,3 +72,5 @@ ENABLE_NBGL_QRCODE = 1 # DISABLE_DEBUG_THROW = 1 include $(BOLOS_SDK)/Makefile.standard_app + +DEFINES += HAVE_BOLOS_APP_STACK_CANARY diff --git a/src/common/base64.c b/src/common/base64.c index 1e5f30fa..0895dba1 100644 --- a/src/common/base64.c +++ b/src/common/base64.c @@ -1,6 +1,7 @@ #include // size_t #include // uint*_t +#include #include "base64.h" static const char base64_alphabet[] = @@ -31,3 +32,100 @@ int base64_encode(const uint8_t *data, size_t data_length, char *out, size_t out out[out_length] = '\0'; return out_length; } + +const uint8_t pr2six[256] = { + 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, 62, 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, 63, // + 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 // +}; + +// Taken from Exchange application implementation +int base64url_decode(const char *src, size_t src_len, uint8_t *dest, size_t dest_len) { + const uint8_t *original_dest; + + original_dest = dest; + + if (src_len % 4 == 1) { + return -1; + } else if (src_len == 0) { + return 0; + } + + while (src_len > 4) { + if (dest_len < 3) { + return -1; + } + *(dest++) = (pr2six[(uint8_t) src[0]] << 2 | pr2six[(uint8_t) src[1]] >> 4); + *(dest++) = (pr2six[(uint8_t) src[1]] << 4 | pr2six[(uint8_t) src[2]] >> 2); + *(dest++) = (pr2six[(uint8_t) src[2]] << 6 | pr2six[(uint8_t) src[3]] >> 0); + src += 4; + src_len -= 4; + dest_len -= 3; + } + + if (src_len > 1) { + if (dest_len < 1) { + return -1; + } + *(dest++) = (pr2six[(uint8_t) src[0]] << 2 | pr2six[(uint8_t) src[1]] >> 4); + dest_len--; + } + + if (src_len > 2) { + if (dest_len < 1) { + return -1; + } + *(dest++) = (pr2six[(uint8_t) src[1]] << 4 | pr2six[(uint8_t) src[2]] >> 2); + dest_len--; + } + + if (src_len > 3) { + if (dest_len < 1) { + return -1; + } + *(dest++) = (pr2six[(uint8_t) src[2]] << 6 | pr2six[(uint8_t) src[3]] >> 0); + } + + return dest - original_dest; +} + +int base64_to_base64url(const char *src, size_t src_len, char *dest, size_t dest_len) { + if (dest_len < src_len) { + return -1; + } + explicit_bzero(dest, dest_len); + + size_t i; + for (i = 0; i < src_len; i++) { + switch (src[i]) { + case '+': + dest[i] = '-'; + break; + case '/': + dest[i] = '_'; + break; + case '=': + // Padding character only found at the end, we simply remove it + dest[i] = '\0'; + return i; + default: + dest[i] = src[i]; // Copy other characters directly + break; + } + } + + return src_len; +} diff --git a/src/common/base64.h b/src/common/base64.h index e90ead7c..e49386d0 100644 --- a/src/common/base64.h +++ b/src/common/base64.h @@ -21,3 +21,57 @@ * */ int base64_encode(const uint8_t *data, size_t data_length, char *out, size_t out_len); + +/** + * Decode a Base64URL encoded string. + * + * This function decodes a Base64URL encoded string into its original binary form. + * The Base64URL encoding is similar to standard Base64, but uses different characters + * for URL-safe encoding, as defined in RFC 4648. + * + * @see https://datatracker.ietf.org/doc/html/rfc4648 + * + * @param[in] src + * Pointer to the input Base64URL encoded string. + * @param[in] src_len + * The length of the Base64URL encoded string. + * @param[out] dest + * Pointer to the output buffer where the decoded bytes will be stored. + * @param[in] dest_len + * The maximum size of the output buffer. + * + * @return The number of bytes successfully decoded and stored in the output buffer, + * or -1 if an error occurs + * + */ +int base64url_decode(const char *src, size_t src_len, uint8_t *dest, size_t dest_len); + +/** + * Convert a Base64 encoded string to a Base64URL encoded string. + * + * This function converts a given Base64 encoded string to a Base64URL encoded + * string by replacing certain characters and removing any padding characters ('=') + * at the end of the string. The result is stored in the provided destination buffer. + * + * Base64URL encoding is defined in RFC 4648 and is a URL-safe variant of Base64 encoding, + * where '+' is replaced with '-' and '/' is replaced with '_'. + * + * @see https://datatracker.ietf.org/doc/html/rfc4648 + * + * @param[in] src + * Pointer to the buffer containing the Base64 encoded string. + * @param[in] src_len + * Length of the Base64 encoded string in the source buffer. + * @param[out] dest + * Pointer to the buffer where the Base64URL encoded string will be stored. + * The buffer should be large enough to hold the result, which is typically + * the same size as the source buffer. + * + * @param[in] dest_len + * Maximum length of the destination buffer. + * + * @return Number of characters written to the destination buffer, excluding the null terminator, + * or -1 if the destination buffer is too small. + * + */ +int base64_to_base64url(const char *src, size_t src_len, char *dest, size_t dest_len); diff --git a/src/common/bip32_check.c b/src/common/bip32_check.c index 2dcdcb23..025c2e7c 100644 --- a/src/common/bip32_check.c +++ b/src/common/bip32_check.c @@ -4,8 +4,12 @@ #include "bip32_check.h" -bool check_global_bip32_path() { - if (G_context.bip32_path_len <= 2) return false; +bool check_bip32_path(uint8_t bip32_path_len, const uint32_t bip32_path[MAX_BIP32_PATH]) { + if (bip32_path_len <= 2) return false; + + return bip32_path[0] == 0x8000002c && bip32_path[1] == 0x8000025f; +} - return G_context.bip32_path[0] == 0x8000002c && G_context.bip32_path[1] == 0x8000025f; +bool check_global_bip32_path() { + return check_bip32_path(G_context.bip32_path_len, G_context.bip32_path); } diff --git a/src/common/bip32_check.h b/src/common/bip32_check.h index 4647628b..2c197e7c 100644 --- a/src/common/bip32_check.h +++ b/src/common/bip32_check.h @@ -2,10 +2,23 @@ #include // bool +#include "../types.h" // MAX_BIP32_PATH + +/** + * Check if the given BIP32 path length and path array are valid. + * + * @param bip32_path_len The length of the BIP32 path. + * @param bip32_path The array containing the BIP32 path. + * + * @return true if the BIP32 path is valid, false otherwise. + * + */ +bool check_bip32_path(uint8_t bip32_path_len, const uint32_t bip32_path[MAX_BIP32_PATH]); + /** * Check the bip32 path stored in G_context. * - * @return true if bip32 path is valid, false otherwise + * @return true if the BIP32 path is valid, false otherwise. * */ bool check_global_bip32_path(); diff --git a/src/common/format_address.c b/src/common/format_address.c index 7ba292bf..45b4e913 100644 --- a/src/common/format_address.c +++ b/src/common/format_address.c @@ -21,12 +21,12 @@ bool address_to_friendly(const uint8_t chain, // Address Tag if (bounceable) { - out[0] = 0x11; // Bounceable + out[0] = BOUNCEABLE; } else { - out[0] = 0x51; // Non-Bounceable + out[0] = NON_BOUNCEABLE; } if (testOnly) { - out[0] = out[0] | 0x80; + out[0] = out[0] | TESTNET_ONLY; } // Workchain diff --git a/src/constants.h b/src/constants.h index ec0ca6e7..05f2a97d 100644 --- a/src/constants.h +++ b/src/constants.h @@ -94,3 +94,7 @@ * Max length for cell_inline types */ #define MAX_CELL_INLINE_LEN 32 + +#define BOUNCEABLE 0x11 +#define NON_BOUNCEABLE 0x51 +#define TESTNET_ONLY 0x80 diff --git a/src/handler/get_public_key.c b/src/handler/get_public_key.c index 81e2e983..b7243c3d 100644 --- a/src/handler/get_public_key.c +++ b/src/handler/get_public_key.c @@ -34,34 +34,53 @@ #include "../helper/send_response.h" #include "../apdu/params.h" -int handler_get_public_key(uint8_t flags, buffer_t *cdata, bool display) { - explicit_bzero(&G_context, sizeof(G_context)); - G_context.req_type = CONFIRM_ADDRESS; - G_context.state = STATE_NONE; - - if (!buffer_read_u8(cdata, &G_context.bip32_path_len) || - !buffer_read_bip32_path(cdata, G_context.bip32_path, (size_t) G_context.bip32_path_len)) { - return io_send_sw(SW_WRONG_DATA_LENGTH); +int get_public_key_helper(uint8_t flags, + buffer_t *cdata, + uint8_t *bip32_path_len, + uint32_t bip32_path[MAX_BIP32_PATH], + pubkey_ctx_t *pk_info) { + if (!buffer_read_u8(cdata, bip32_path_len) || + !buffer_read_bip32_path(cdata, bip32_path, (size_t) *bip32_path_len)) { + PRINTF("Error SW_WRONG_DATA_LENGTH\n"); + return SW_WRONG_DATA_LENGTH; } - if (!check_global_bip32_path()) { - return io_send_sw(SW_BAD_BIP32_PATH); + if (!check_bip32_path(*bip32_path_len, bip32_path)) { + PRINTF("Error SW_BAD_BIP32_PATH\n"); + return SW_BAD_BIP32_PATH; } - if (crypto_derive_public_key(G_context.bip32_path, - G_context.bip32_path_len, - G_context.pk_info.raw_public_key) < 0) { - return io_send_sw(SW_BAD_STATE); + if (crypto_derive_public_key(bip32_path, *bip32_path_len, pk_info->raw_public_key) < 0) { + PRINTF("Error SW_BAD_STATE\n"); + return SW_BAD_STATE; } if (flags & P2_ADDR_FLAG_WALLET_SPECIFIERS) { - if (!buffer_read_bool(cdata, &G_context.pk_info.is_v3r2) || - !buffer_read_u32(cdata, &G_context.pk_info.subwallet_id, BE)) { - return io_send_sw(SW_WRONG_DATA_LENGTH); + if (!buffer_read_bool(cdata, &pk_info->is_v3r2) || + !buffer_read_u32(cdata, &pk_info->subwallet_id, BE)) { + PRINTF("Error SW_WRONG_DATA_LENGTH\n"); + return SW_WRONG_DATA_LENGTH; } } else { - G_context.pk_info.subwallet_id = 698983191; - G_context.pk_info.is_v3r2 = false; + pk_info->subwallet_id = 698983191; + pk_info->is_v3r2 = false; + } + + return 0; +} + +int handler_get_public_key(uint8_t flags, buffer_t *cdata, bool display) { + explicit_bzero(&G_context, sizeof(G_context)); + G_context.req_type = CONFIRM_ADDRESS; + G_context.state = STATE_NONE; + + int ret = get_public_key_helper(flags, + cdata, + &G_context.bip32_path_len, + G_context.bip32_path, + &G_context.pk_info); + if (ret != 0) { + return io_send_sw(ret); } if (display) { diff --git a/src/handler/get_public_key.h b/src/handler/get_public_key.h index e22be8c1..188aef0a 100644 --- a/src/handler/get_public_key.h +++ b/src/handler/get_public_key.h @@ -3,8 +3,32 @@ #include // bool #include // uint*_t +#include "../types.h" #include "common/mybuffer.h" +/** + * Helper for GET_PUBLIC_KEY handler. If successfully parse BIP32 path and derive the public key. + * + * @param[in] flags + * Address display flags + * @param[in,out] cdata + * Command data with BIP32 path. + * @param[out] bip32_path_len + * Requested bip32_path to derive the public key on length + * @param[out] bip32_path + * Requested bip32_path to derive the public key on + * @param[out] pk_info + * Additional info to derive the public key with + * + * @return zero if success, error code otherwise. + * + */ +int get_public_key_helper(uint8_t flags, + buffer_t *cdata, + uint8_t *bip32_path_len, + uint32_t bip32_path[MAX_BIP32_PATH], + pubkey_ctx_t *pk_info); + /** * Handler for GET_PUBLIC_KEY command. If successfully parse BIP32 path, * derive public key and send APDU response. diff --git a/src/handler/sign_tx.c b/src/handler/sign_tx.c index 9f98a879..844c4a00 100644 --- a/src/handler/sign_tx.c +++ b/src/handler/sign_tx.c @@ -22,7 +22,9 @@ #include "os.h" #include "cx.h" +#include "swap.h" +#include "validate.h" #include "sign_tx.h" #include "../sw.h" #include "../globals.h" @@ -33,6 +35,7 @@ #include "../transaction/types.h" #include "../transaction/deserialize.h" #include "../transaction/hash.h" +#include "handle_swap_sign_transaction.h" int handler_sign_tx(buffer_t *cdata, bool first, bool more) { if (first) { // first APDU, parse BIP32 path @@ -98,5 +101,34 @@ int handler_sign_tx(buffer_t *cdata, bool first, bool more) { PRINTF("Hash: %.*H\n", sizeof(G_context.tx_info.m_hash), G_context.tx_info.m_hash); - return ui_display_transaction(); + // If we are in swap context, do not redisplay the message data + // Instead, ensure they are identical with what was previously displayed + if (G_called_from_swap) { + if (G_swap_response_ready) { + // Safety against trying to make the app sign multiple TX + // This code should never be triggered as the app is supposed to exit after + // sending the signed transaction + PRINTF("Safety against double signing triggered\n"); + os_sched_exit(-1); + } else { + // We will quit the app after this transaction, whether it succeeds or fails + PRINTF("Swap response is ready, the app will quit after the next send\n"); + // This boolean will make the io_send_sw family instant reply + return to exchange + G_swap_response_ready = true; + } + + if (swap_check_validity()) { + PRINTF("Swap response validated\n"); + ui_action_validate_transaction(true); + } else { + // Unreachable due to io_send_sw instant replying and quitting to Exchange in Swap mode + PRINTF("!swap_check_validity\n"); + // Failsafe + swap_finalize_exchange_sign_transaction(false); + } + + return 0; + } else { + return ui_display_transaction(); + } } diff --git a/src/offsets.h b/src/offsets.h deleted file mode 100644 index 3db4bc14..00000000 --- a/src/offsets.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/** - * Offset of instruction class. - */ -#define OFFSET_CLA 0 -/** - * Offset of instruction code. - */ -#define OFFSET_INS 1 -/** - * Offset of instruction parameter 1. - */ -#define OFFSET_P1 2 -/** - * Offset of instruction parameter 2. - */ -#define OFFSET_P2 3 -/** - * Offset of command data length. - */ -#define OFFSET_LC 4 -/** - * Offset of command data. - */ -#define OFFSET_CDATA 5 diff --git a/src/sw.h b/src/sw.h index de226a3a..99742ee5 100644 --- a/src/sw.h +++ b/src/sw.h @@ -60,6 +60,10 @@ * Status word for signature fail. */ #define SW_SIGNATURE_FAIL 0xB008 +/** + * Status word for a failed swap operation + */ +#define SW_SWAP_FAILURE 0xB009 /** * Status word for a request that is too long. */ diff --git a/src/swap/handle_check_address.c b/src/swap/handle_check_address.c new file mode 100644 index 00000000..79d9b652 --- /dev/null +++ b/src/swap/handle_check_address.c @@ -0,0 +1,147 @@ +#ifdef HAVE_SWAP + +#include + +#include "os.h" +#include "crypto_helpers.h" +#include "swap.h" +#include "../types.h" +#include "common/mybuffer.h" +#include "get_public_key.h" +#include "base64.h" +#include "address.h" +#include "constants.h" +#include "crc16.h" + +#define BASE_CHAIN 0x00 +#define MASTER_CHAIN 0xFF +#define ADDRESS_BASE64_LENGTH 48 +#define ADDRESS_DECODED_LENGTH 36 + +/* Set params.result to 0 on error, 1 otherwise */ +void swap_handle_check_address(check_address_parameters_t *params) { + PRINTF("Inside Ton swap_handle_check_address\n"); + params->result = 0; + + if (params->address_parameters == NULL) { + PRINTF("derivation path expected\n"); + return; + } + PRINTF("address_parameters %.*H\n", + params->address_parameters_length, + params->address_parameters); + + if (params->address_to_check == NULL) { + PRINTF("Address to check expected\n"); + return; + } + PRINTF("Address to check %s\n", params->address_to_check); + if (strlen(params->address_to_check) != ADDRESS_BASE64_LENGTH) { + PRINTF("Address to check expected length %d, not %d\n", + ADDRESS_BASE64_LENGTH, + strlen(params->address_to_check)); + return; + } + + buffer_t cdata; + cdata.ptr = params->address_parameters; + cdata.size = params->address_parameters_length; + cdata.offset = 0; + + uint8_t bip32_path_len; + uint32_t bip32_path[MAX_BIP32_PATH]; + pubkey_ctx_t pk_info; + pk_info.subwallet_id = 698983191; + pk_info.is_v3r2 = false; + if (get_public_key_helper(0, &cdata, &bip32_path_len, bip32_path, &pk_info) != 0) { + PRINTF("Failed to read public key\n"); + return; + } + PRINTF("Derived on path %.*H\n", 4 * bip32_path_len, bip32_path); + PRINTF("Public key %.*H\n", PUBKEY_LEN, pk_info.raw_public_key); + + uint8_t hash[HASH_LEN] = {0}; + if (!pubkey_to_hash(pk_info.raw_public_key, + pk_info.subwallet_id, + pk_info.is_v3r2, + hash, + sizeof(hash))) { + return; + } + PRINTF("hash %.*H\n", HASH_LEN, hash); + + char address_base64url[ADDRESS_BASE64_LENGTH]; + if (base64_to_base64url(params->address_to_check, + ADDRESS_BASE64_LENGTH, + address_base64url, + ADDRESS_BASE64_LENGTH) < 0) { + PRINTF("Failed to convert to base64url\n"); + return; + } + uint8_t address_decoded[ADDRESS_DECODED_LENGTH]; + int ret = base64url_decode(address_base64url, + ADDRESS_BASE64_LENGTH, + address_decoded, + ADDRESS_DECODED_LENGTH); + if (ret != ADDRESS_DECODED_LENGTH) { + PRINTF("%d\n", ret); + PRINTF("Failed to decode\n"); + return; + } + PRINTF("address_decoded = %.*H\n", ADDRESS_DECODED_LENGTH, address_decoded); + + uint8_t flag = address_decoded[0]; + uint8_t workchain_id = address_decoded[1]; + uint8_t *account_id = &address_decoded[2]; + uint16_t address_verification = U2BE(address_decoded, ADDRESS_DECODED_LENGTH - 2); + + if (flag & NON_BOUNCEABLE) { + PRINTF("Setting mode NON_BOUNCEABLE\n"); + flag &= ~NON_BOUNCEABLE; + } else if (flag & BOUNCEABLE) { + PRINTF("Setting mode BOUNCEABLE\n"); + flag &= ~BOUNCEABLE; + } else { + PRINTF("Invalid flag value for bounceability %d\n", flag); + return; + } + + if (flag & TESTNET_ONLY) { + PRINTF("Testnet only address refused, flag: %d\n", flag); + return; + } + + if (flag != 0) { + PRINTF("Unknown flag refused: %d\n", flag); + return; + } + + if (workchain_id != BASE_CHAIN && workchain_id != MASTER_CHAIN) { + PRINTF("Unknown workchain_id refused: %d\n", workchain_id); + return; + } + + if (memcmp(account_id, hash, HASH_LEN) != 0) { + PRINTF("Different account id value: received %.*H, derived %.*H\n", + HASH_LEN, + account_id, + HASH_LEN, + hash); + PRINTF("Path was %.*H\n", params->address_parameters_length, params->address_parameters); + return; + } + + uint16_t calculated_address_verification = crc16(address_decoded, ADDRESS_DECODED_LENGTH - 2); + if (calculated_address_verification != address_verification) { + PRINTF("Wrong address verification: calculated %d, received %d\n", + calculated_address_verification, + address_verification); + return; + } + + PRINTF("Addresses match\n"); + + params->result = 1; +} + +#endif // HAVE_SWAP diff --git a/src/swap/handle_get_printable_amount.c b/src/swap/handle_get_printable_amount.c new file mode 100644 index 00000000..6d6bb31b --- /dev/null +++ b/src/swap/handle_get_printable_amount.c @@ -0,0 +1,54 @@ +#ifdef HAVE_SWAP + +#include // memset, explicit_bzero +#include "handle_swap_sign_transaction.h" +#include "swap.h" +#include "os.h" +#include "format_bigint.h" +#include "constants.h" + +/* Set empty printable_amount on error, printable amount otherwise */ +void swap_handle_get_printable_amount(get_printable_amount_parameters_t* params) { + PRINTF("coin_configuration %.*H\n", + params->coin_configuration_length, + params->coin_configuration); + PRINTF("amount %.*H\n", params->amount_length, params->amount); + uint8_t decimals; + char ticker[MAX_SWAP_TOKEN_LENGTH] = {0}; + + PRINTF("Inside Ton swap_handle_get_printable_amount\n"); + + // If the amount is a fee, its value is nominated in TON even if we're doing an TRC20 swap + // If there is no coin_configuration, consider that we are doing a TON swap + if (params->is_fee || params->coin_configuration == NULL) { + memcpy(ticker, "TON", sizeof("TON")); + decimals = EXPONENT_SMALLEST_UNIT; + } else { + if (!swap_parse_config(params->coin_configuration, + params->coin_configuration_length, + ticker, + sizeof(ticker), + &decimals)) { + PRINTF("Fail to parse coin_configuration\n"); + goto error; + } + } + + if (!amountToString(params->amount, + params->amount_length, + decimals, + ticker, + params->printable_amount, + sizeof(params->printable_amount))) { + PRINTF("print_amount failed\n"); + goto error; + } + + PRINTF("Amount %s\n", params->printable_amount); + return; + +error: + explicit_bzero(params->printable_amount, sizeof(params->printable_amount)); +} + +#endif // HAVE_SWAP diff --git a/src/swap/handle_swap_sign_transaction.c b/src/swap/handle_swap_sign_transaction.c new file mode 100644 index 00000000..8bb9584c --- /dev/null +++ b/src/swap/handle_swap_sign_transaction.c @@ -0,0 +1,223 @@ +#ifdef HAVE_SWAP + +#include "handle_swap_sign_transaction.h" +#include "display.h" +#include "swap.h" +#include "string.h" +#include "os_lib.h" +#include "constants.h" +#include "os_utils.h" +#include "globals.h" +#include "sw.h" +#include "os.h" +#include "display_transaction.h" +#include "base64.h" +#include "format_address.h" +#include "transaction_hints.h" + +// Error codes for swap, to be moved in SDK +#define ERROR_INTERNAL 0x00 +#define ERROR_WRONG_AMOUNT 0x01 +#define ERROR_WRONG_DESTINATION 0x02 +#define ERROR_WRONG_FEES 0x03 +#define ERROR_WRONG_METHOD 0x04 +#define ERROR_CROSSCHAIN_WRONG_MODE 0x05 +#define ERROR_CROSSCHAIN_WRONG_METHOD 0x06 +#define ERROR_GENERIC 0xFF + +typedef struct swap_validated_s { + bool initialized; + uint8_t decimals; + char ticker[MAX_SWAP_TOKEN_LENGTH]; + uint8_t amount_length; + uint8_t amount[MAX_VALUE_BYTES_LEN]; + char recipient[G_ADDRESS_LEN]; +} swap_validated_t; + +static swap_validated_t G_swap_validated; + +// Save the BSS address where we will write the return value when finished +static uint8_t* G_swap_sign_return_value_address; + +// Save the data validated during the Exchange app flow +bool swap_copy_transaction_parameters(create_transaction_parameters_t* params) { + PRINTF("Inside Ton swap_copy_transaction_parameters\n"); + + // Ensure no extraid + if (params->destination_address_extra_id == NULL) { + PRINTF("destination_address_extra_id expected\n"); + return false; + } else if (params->destination_address_extra_id[0] != '\0') { + PRINTF("destination_address_extra_id expected empty, not '%s'\n", + params->destination_address_extra_id); + return false; + } + + if (params->destination_address == NULL) { + PRINTF("Destination address expected\n"); + return false; + } + + if (params->amount == NULL) { + PRINTF("Amount expected\n"); + return false; + } + + // first copy parameters to stack, and then to global data. + // We need this "trick" as the input data position can overlap with app globals + // and also because we want to memset the whole bss segment as it is not done + // when an app is called as a lib. + // This is necessary as many part of the code expect bss variables to + // initialized at 0. + swap_validated_t swap_validated; + memset(&swap_validated, 0, sizeof(swap_validated)); + + // Parse config and save decimals and ticker + // If there is no coin_configuration, consider that we are doing a TRX swap + if (params->coin_configuration == NULL) { + memcpy(swap_validated.ticker, "TON", sizeof("TON")); + swap_validated.decimals = EXPONENT_SMALLEST_UNIT; + } else { + if (!swap_parse_config(params->coin_configuration, + params->coin_configuration_length, + swap_validated.ticker, + sizeof(swap_validated.ticker), + &swap_validated.decimals)) { + PRINTF("Fail to parse coin_configuration\n"); + return false; + } + } + + // Save recipient + strlcpy(swap_validated.recipient, + params->destination_address, + sizeof(swap_validated.recipient)); + if (swap_validated.recipient[sizeof(swap_validated.recipient) - 1] != '\0') { + PRINTF("Address copy error\n"); + return false; + } + + // Save amount + if (params->amount_length > sizeof(swap_validated.amount)) { + PRINTF("Amount too big\n"); + return false; + } else { + swap_validated.amount_length = params->amount_length; + memcpy(swap_validated.amount, params->amount, params->amount_length); + } + + swap_validated.initialized = true; + + // Full reset the global variables + os_explicit_zero_BSS_segment(); + + // Keep the address at which we'll reply the signing status + G_swap_sign_return_value_address = ¶ms->result; + + // Commit from stack to global data, params becomes tainted but we won't access it anymore + memcpy(&G_swap_validated, &swap_validated, sizeof(swap_validated)); + return true; +} + +bool swap_check_validity() { + PRINTF("Inside Ton swap_check_validity\n"); + + if (!G_swap_validated.initialized) { + PRINTF("Swap structure is not initialized\n"); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } + + if (G_context.tx_info.transaction.is_blind) { + PRINTF("Blind operation not allowed in swap mode\n"); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } + + if (G_context.tx_info.transaction.hints_type != TRANSACTION_COMMENT) { + PRINTF("Wrong operation %d\n", G_context.tx_info.transaction.hints_type); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } else if (G_context.tx_info.transaction.hints_len != 0) { + PRINTF("Hint length %d refused\n", G_context.tx_info.transaction.hints_len); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } else { + PRINTF("Valid operation %d\n", G_context.tx_info.transaction.hints_type); + } + + if (G_context.tx_info.transaction.send_mode & 128) { + PRINTF("Amount MAX is refused\n"); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } + + if (G_swap_validated.amount_length != G_context.tx_info.transaction.value_len) { + PRINTF("Amount length does not match, promised %d, received %d\n", + G_swap_validated.amount_length, + G_context.tx_info.transaction.value_len); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } else if (memcmp(G_swap_validated.amount, + G_context.tx_info.transaction.value_buf, + G_swap_validated.amount_length) != 0) { + PRINTF("Amount does not match, promised %.*H, received %.*H\n", + G_swap_validated.amount_length, + G_swap_validated.amount, + G_swap_validated.amount_length, + G_context.tx_info.transaction.value_buf); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } else { + PRINTF("Amounts match %.*H\n", + G_context.tx_info.transaction.value_len, + G_context.tx_info.transaction.value_buf); + } + + char encoded_address[G_ADDRESS_LEN]; + uint8_t decoded_address[ADDRESS_LEN] = {0}; + if (!address_to_friendly(G_context.tx_info.transaction.to.chain, + G_context.tx_info.transaction.to.hash, + G_context.tx_info.transaction.bounce, + false, + decoded_address, + sizeof(decoded_address))) { + PRINTF("!address_to_friendly\n"); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } + memset(encoded_address, 0, sizeof(encoded_address)); + base64_encode(decoded_address, + sizeof(decoded_address), + encoded_address, + sizeof(encoded_address)); + + if (strcmp(G_swap_validated.recipient, encoded_address) != 0) { + PRINTF("Destination does not match, promised %s, received %s\n", + G_swap_validated.recipient, + encoded_address); + io_send_sw(SW_SWAP_FAILURE); + // unreachable + os_sched_exit(0); + } else { + PRINTF("Destination %s is valid\n", encoded_address); + } + + return true; +} + +void __attribute__((noreturn)) swap_finalize_exchange_sign_transaction(bool is_success) { + PRINTF("Returning to Exchange with status %d\n", is_success); + *G_swap_sign_return_value_address = is_success; + os_lib_end(); +} + +#endif // HAVE_SWAP diff --git a/src/swap/handle_swap_sign_transaction.h b/src/swap/handle_swap_sign_transaction.h new file mode 100644 index 00000000..f9031aa9 --- /dev/null +++ b/src/swap/handle_swap_sign_transaction.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#define MAX_SWAP_TOKEN_LENGTH 15 + +bool swap_check_validity(); diff --git a/src/transaction/transaction_hints.c b/src/transaction/transaction_hints.c index 8e0b1c0d..d4f2c159 100644 --- a/src/transaction/transaction_hints.c +++ b/src/transaction/transaction_hints.c @@ -402,6 +402,7 @@ bool process_hints(transaction_t* tx) { &master, jettons[jetton_id].state_assembler_idx)); + // TODO: implem Jetton swap if (memcmp(state_init.hash, G_context.tx_info.transaction.to.hash, 32) != 0) { return false; } diff --git a/tests/setup.cfg b/tests/setup.cfg index 026fedaa..3fc12541 100644 --- a/tests/setup.cfg +++ b/tests/setup.cfg @@ -7,7 +7,8 @@ disable = C0114, # missing-module-docstring C0116, # missing-function-docstring C0103, # invalid-name R0801, # duplicate-code - R0913 # too-many-arguments + R0913, # too-many-arguments + R0917, # too-many-positional-arguments max-line-length=100 extension-pkg-whitelist=hid @@ -18,4 +19,4 @@ max-line-length = 100 ignore_missing_imports = True [mypy-pytest.*] -ignore_missing_imports = True \ No newline at end of file +ignore_missing_imports = True diff --git a/tests/snapshots/flex/test_app_mainmenu/00002.png b/tests/snapshots/flex/test_app_mainmenu/00002.png index 1dd4bcc8..528a4f3f 100644 Binary files a/tests/snapshots/flex/test_app_mainmenu/00002.png and b/tests/snapshots/flex/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/flex/test_app_settings/00004.png b/tests/snapshots/flex/test_app_settings/00004.png index 1dd4bcc8..528a4f3f 100644 Binary files a/tests/snapshots/flex/test_app_settings/00004.png and b/tests/snapshots/flex/test_app_settings/00004.png differ diff --git a/tests/snapshots/flex/test_get_proof_accepted/00000.png b/tests/snapshots/flex/test_get_proof_accepted/00000.png index caa7045b..47f3e122 100644 Binary files a/tests/snapshots/flex/test_get_proof_accepted/00000.png and b/tests/snapshots/flex/test_get_proof_accepted/00000.png differ diff --git a/tests/snapshots/flex/test_get_proof_accepted/00001.png b/tests/snapshots/flex/test_get_proof_accepted/00001.png index 06d39b8e..0f96e14c 100644 Binary files a/tests/snapshots/flex/test_get_proof_accepted/00001.png and b/tests/snapshots/flex/test_get_proof_accepted/00001.png differ diff --git a/tests/snapshots/flex/test_get_proof_accepted/00003.png b/tests/snapshots/flex/test_get_proof_accepted/00003.png index 06d39b8e..0f96e14c 100644 Binary files a/tests/snapshots/flex/test_get_proof_accepted/00003.png and b/tests/snapshots/flex/test_get_proof_accepted/00003.png differ diff --git a/tests/snapshots/flex/test_get_proof_accepted_v3r2/00000.png b/tests/snapshots/flex/test_get_proof_accepted_v3r2/00000.png index caa7045b..47f3e122 100644 Binary files a/tests/snapshots/flex/test_get_proof_accepted_v3r2/00000.png and b/tests/snapshots/flex/test_get_proof_accepted_v3r2/00000.png differ diff --git a/tests/snapshots/flex/test_get_proof_accepted_v3r2/00001.png b/tests/snapshots/flex/test_get_proof_accepted_v3r2/00001.png index 7815ac1b..13e7d65c 100644 Binary files a/tests/snapshots/flex/test_get_proof_accepted_v3r2/00001.png and b/tests/snapshots/flex/test_get_proof_accepted_v3r2/00001.png differ diff --git a/tests/snapshots/flex/test_get_proof_accepted_v3r2/00003.png b/tests/snapshots/flex/test_get_proof_accepted_v3r2/00003.png index 7815ac1b..13e7d65c 100644 Binary files a/tests/snapshots/flex/test_get_proof_accepted_v3r2/00003.png and b/tests/snapshots/flex/test_get_proof_accepted_v3r2/00003.png differ diff --git a/tests/snapshots/flex/test_get_proof_refused/part0/00000.png b/tests/snapshots/flex/test_get_proof_refused/part0/00000.png index caa7045b..47f3e122 100644 Binary files a/tests/snapshots/flex/test_get_proof_refused/part0/00000.png and b/tests/snapshots/flex/test_get_proof_refused/part0/00000.png differ diff --git a/tests/snapshots/flex/test_get_proof_refused/part1/00000.png b/tests/snapshots/flex/test_get_proof_refused/part1/00000.png index caa7045b..47f3e122 100644 Binary files a/tests/snapshots/flex/test_get_proof_refused/part1/00000.png and b/tests/snapshots/flex/test_get_proof_refused/part1/00000.png differ diff --git a/tests/snapshots/flex/test_get_proof_refused/part1/00001.png b/tests/snapshots/flex/test_get_proof_refused/part1/00001.png index 06d39b8e..0f96e14c 100644 Binary files a/tests/snapshots/flex/test_get_proof_refused/part1/00001.png and b/tests/snapshots/flex/test_get_proof_refused/part1/00001.png differ diff --git a/tests/snapshots/flex/test_get_proof_refused/part2/00000.png b/tests/snapshots/flex/test_get_proof_refused/part2/00000.png index caa7045b..47f3e122 100644 Binary files a/tests/snapshots/flex/test_get_proof_refused/part2/00000.png and b/tests/snapshots/flex/test_get_proof_refused/part2/00000.png differ diff --git a/tests/snapshots/flex/test_get_proof_refused/part2/00001.png b/tests/snapshots/flex/test_get_proof_refused/part2/00001.png index 06d39b8e..0f96e14c 100644 Binary files a/tests/snapshots/flex/test_get_proof_refused/part2/00001.png and b/tests/snapshots/flex/test_get_proof_refused/part2/00001.png differ diff --git a/tests/snapshots/flex/test_sign_data/part0/00000.png b/tests/snapshots/flex/test_sign_data/part0/00000.png index 0835bfb4..e91e2ce8 100644 Binary files a/tests/snapshots/flex/test_sign_data/part0/00000.png and b/tests/snapshots/flex/test_sign_data/part0/00000.png differ diff --git a/tests/snapshots/flex/test_sign_data/part1/00000.png b/tests/snapshots/flex/test_sign_data/part1/00000.png index 6d8d7141..25a09679 100644 Binary files a/tests/snapshots/flex/test_sign_data/part1/00000.png and b/tests/snapshots/flex/test_sign_data/part1/00000.png differ diff --git a/tests/snapshots/flex/test_sign_data_refused/part0/00000.png b/tests/snapshots/flex/test_sign_data_refused/part0/00000.png index fbe5484b..ddc5a8bb 100644 Binary files a/tests/snapshots/flex/test_sign_data_refused/part0/00000.png and b/tests/snapshots/flex/test_sign_data_refused/part0/00000.png differ diff --git a/tests/snapshots/flex/test_sign_data_refused/part1/00000.png b/tests/snapshots/flex/test_sign_data_refused/part1/00000.png index fbe5484b..ddc5a8bb 100644 Binary files a/tests/snapshots/flex/test_sign_data_refused/part1/00000.png and b/tests/snapshots/flex/test_sign_data_refused/part1/00000.png differ diff --git a/tests/snapshots/flex/test_sign_data_refused/part1/00001.png b/tests/snapshots/flex/test_sign_data_refused/part1/00001.png index 63e180db..6c0d5118 100644 Binary files a/tests/snapshots/flex/test_sign_data_refused/part1/00001.png and b/tests/snapshots/flex/test_sign_data_refused/part1/00001.png differ diff --git a/tests/snapshots/flex/test_sign_data_refused/part2/00000.png b/tests/snapshots/flex/test_sign_data_refused/part2/00000.png index fbe5484b..ddc5a8bb 100644 Binary files a/tests/snapshots/flex/test_sign_data_refused/part2/00000.png and b/tests/snapshots/flex/test_sign_data_refused/part2/00000.png differ diff --git a/tests/snapshots/flex/test_sign_data_refused/part2/00001.png b/tests/snapshots/flex/test_sign_data_refused/part2/00001.png index 63e180db..6c0d5118 100644 Binary files a/tests/snapshots/flex/test_sign_data_refused/part2/00001.png and b/tests/snapshots/flex/test_sign_data_refused/part2/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00001.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00001.png index bc4fe6cf..108e31ee 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00001.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00002.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00002.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part0/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00001.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00001.png index 01af61fe..db67dc26 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00001.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00002.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00002.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part1/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00001.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00001.png index 69e757a7..a4b60dbf 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00001.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00002.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00002.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part2/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00001.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00001.png index 2b0920b3..a4712278 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00001.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00002.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00002.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part3/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00001.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00001.png index 070ab5f6..2ef661be 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00001.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00002.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00002.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part4/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00001.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00001.png index e5a3435d..73bc1691 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00001.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00002.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00002.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part5/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00001.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00001.png index 9c952b97..5c057d1d 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00001.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00002.png b/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00002.png and b/tests/snapshots/flex/test_sign_tx_clear_jetton/part6/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_no_payload/00000.png b/tests/snapshots/flex/test_sign_tx_no_payload/00000.png index 2543156f..c71f87a7 100644 Binary files a/tests/snapshots/flex/test_sign_tx_no_payload/00000.png and b/tests/snapshots/flex/test_sign_tx_no_payload/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_refused/part0/00000.png b/tests/snapshots/flex/test_sign_tx_refused/part0/00000.png index fca09ad2..8239e351 100644 Binary files a/tests/snapshots/flex/test_sign_tx_refused/part0/00000.png and b/tests/snapshots/flex/test_sign_tx_refused/part0/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_refused/part1/00000.png b/tests/snapshots/flex/test_sign_tx_refused/part1/00000.png index fca09ad2..8239e351 100644 Binary files a/tests/snapshots/flex/test_sign_tx_refused/part1/00000.png and b/tests/snapshots/flex/test_sign_tx_refused/part1/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_refused/part1/00001.png b/tests/snapshots/flex/test_sign_tx_refused/part1/00001.png index 2543156f..c71f87a7 100644 Binary files a/tests/snapshots/flex/test_sign_tx_refused/part1/00001.png and b/tests/snapshots/flex/test_sign_tx_refused/part1/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_refused/part2/00000.png b/tests/snapshots/flex/test_sign_tx_refused/part2/00000.png index fca09ad2..8239e351 100644 Binary files a/tests/snapshots/flex/test_sign_tx_refused/part2/00000.png and b/tests/snapshots/flex/test_sign_tx_refused/part2/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_refused/part2/00001.png b/tests/snapshots/flex/test_sign_tx_refused/part2/00001.png index 2543156f..c71f87a7 100644 Binary files a/tests/snapshots/flex/test_sign_tx_refused/part2/00001.png and b/tests/snapshots/flex/test_sign_tx_refused/part2/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_subwallet_id/part0/00000.png b/tests/snapshots/flex/test_sign_tx_subwallet_id/part0/00000.png index d2ebc65a..334a51d7 100644 Binary files a/tests/snapshots/flex/test_sign_tx_subwallet_id/part0/00000.png and b/tests/snapshots/flex/test_sign_tx_subwallet_id/part0/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_subwallet_id/part1/00000.png b/tests/snapshots/flex/test_sign_tx_subwallet_id/part1/00000.png index 1f6f8932..c970f283 100644 Binary files a/tests/snapshots/flex/test_sign_tx_subwallet_id/part1/00000.png and b/tests/snapshots/flex/test_sign_tx_subwallet_id/part1/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part0/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part0/00000.png index 1f6f8932..c970f283 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part0/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part0/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part1/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part1/00000.png index d2ebc65a..334a51d7 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part1/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part1/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part10/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part10/00000.png index 98590d15..f8255347 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part10/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part10/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part11/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part11/00000.png index 98590d15..f8255347 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part11/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part11/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part12/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part12/00000.png index 4d077f7a..56b39e74 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part12/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part12/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part13/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part13/00000.png index 1219d92b..21140878 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part13/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part13/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part2/00001.png b/tests/snapshots/flex/test_sign_tx_with_payload/part2/00001.png index 8e42b40d..85a51ffb 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part2/00001.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part2/00001.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part2/00002.png b/tests/snapshots/flex/test_sign_tx_with_payload/part2/00002.png index 0d6eb34b..805889d0 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part2/00002.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part2/00002.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part3/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part3/00000.png index 963f58f6..4486ed1e 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part3/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part3/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part4/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part4/00000.png index 1219d92b..21140878 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part4/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part4/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part5/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part5/00000.png index 6125666d..1688b379 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part5/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part5/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part6/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part6/00000.png index 93acbf85..f7913a32 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part6/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part6/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part7/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part7/00000.png index 641ee7ca..a5207cd2 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part7/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part7/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part8/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part8/00000.png index 6e7a1b33..f61b428c 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part8/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part8/00000.png differ diff --git a/tests/snapshots/flex/test_sign_tx_with_payload/part9/00000.png b/tests/snapshots/flex/test_sign_tx_with_payload/part9/00000.png index 1f98739f..3c104a89 100644 Binary files a/tests/snapshots/flex/test_sign_tx_with_payload/part9/00000.png and b/tests/snapshots/flex/test_sign_tx_with_payload/part9/00000.png differ diff --git a/tests/snapshots/nanos/test_app_mainmenu/00020.png b/tests/snapshots/nanos/test_app_mainmenu/00020.png index dbe35d60..793aa381 100644 Binary files a/tests/snapshots/nanos/test_app_mainmenu/00020.png and b/tests/snapshots/nanos/test_app_mainmenu/00020.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00020.png b/tests/snapshots/nanosp/test_app_mainmenu/00020.png index be36adcf..d5fb044b 100644 Binary files a/tests/snapshots/nanosp/test_app_mainmenu/00020.png and b/tests/snapshots/nanosp/test_app_mainmenu/00020.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00020.png b/tests/snapshots/nanox/test_app_mainmenu/00020.png index be36adcf..d5fb044b 100644 Binary files a/tests/snapshots/nanox/test_app_mainmenu/00020.png and b/tests/snapshots/nanox/test_app_mainmenu/00020.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00002.png b/tests/snapshots/stax/test_app_mainmenu/00002.png index 7119e08c..5c9f84d2 100644 Binary files a/tests/snapshots/stax/test_app_mainmenu/00002.png and b/tests/snapshots/stax/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/stax/test_app_settings/00004.png b/tests/snapshots/stax/test_app_settings/00004.png index 7119e08c..5c9f84d2 100644 Binary files a/tests/snapshots/stax/test_app_settings/00004.png and b/tests/snapshots/stax/test_app_settings/00004.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png index bfb2a655..33509d1b 100644 Binary files a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png index bfb2a655..33509d1b 100644 Binary files a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00001.png index 94a028f1..cb34daa6 100644 Binary files a/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00001.png and b/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00003.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00003.png index 94a028f1..cb34daa6 100644 Binary files a/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00003.png and b/tests/snapshots/stax/test_get_public_key_confirm_accepted_v3r2/00003.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00001.png index bfb2a655..33509d1b 100644 Binary files a/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00001.png and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00001.png differ diff --git a/tests/test_name_version.py b/tests/test_name_version.py index d8edee2a..dc3c29f9 100644 --- a/tests/test_name_version.py +++ b/tests/test_name_version.py @@ -1,9 +1,37 @@ +import re +from pathlib import Path from application_client.ton_command_sender import BoilerplateCommandSender from application_client.ton_response_unpacker import unpack_get_app_and_version_response +def check_version(root_path: Path, target_version: str): + """Extract and check if the version in the Makefile matches the target version.""" + version_re = re.compile(r"^APPVERSION_(?P[MNP])\s*=\s*(?P\d+)", re.I) + vers_dict = {} + makefile = f"{root_path.parent.resolve()}/Makefile" + + # Read the file and extract the version + with open(makefile, "r", encoding="utf-8") as f: + for line in f: + match = version_re.match(line) + if match: + part = match.group("part") + vers_dict[part] = int(match.group("val")) + + # Ensure all parts (M, N, P) are present + try: + major = vers_dict['M'] + minor = vers_dict['N'] + patch = vers_dict['P'] + except KeyError: + raise ValueError("The version in the Makefile is incomplete.") + + extracted_version = f"{major}.{minor}.{patch}" + + print(f"Makefile version: {extracted_version}, Target version: {target_version}") + assert extracted_version == target_version # Test a specific APDU asking BOLOS (and not the app) the name and version of the current app -def test_get_app_and_version(backend, backend_name): +def test_get_app_and_version(backend, default_screenshot_path): # Use the app interface instead of raw interface client = BoilerplateCommandSender(backend) # Send the special instruction to BOLOS @@ -11,5 +39,6 @@ def test_get_app_and_version(backend, backend_name): # Use an helper to parse the response, assert the values app_name, version = unpack_get_app_and_version_response(response.data) + check_version(default_screenshot_path, version) assert app_name == "TON" - assert version == "2.3.0" + diff --git a/tests/test_version_cmd.py b/tests/test_version_cmd.py deleted file mode 100644 index e12c26d4..00000000 --- a/tests/test_version_cmd.py +++ /dev/null @@ -1,15 +0,0 @@ -from application_client.ton_command_sender import BoilerplateCommandSender -from application_client.ton_response_unpacker import unpack_get_version_response - -MAJOR = 2 -MINOR = 3 -PATCH = 0 - -# In this test we check the behavior of the device when asked to provide the app version -def test_version(backend): - # Use the app interface instead of raw interface - client = BoilerplateCommandSender(backend) - # Send the GET_VERSION instruction - rapdu = client.get_version() - # Use an helper to parse the response, assert the values - assert unpack_get_version_response(rapdu.data) == (MAJOR, MINOR, PATCH)