From 5c4c1c6195b9ed305e834286daa47bf21dc46f7a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 1 Aug 2024 10:20:54 +0200 Subject: [PATCH 1/6] Fixed path start CRC-32 value --- src_features/signMessageEIP712/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index bf7d95a0d..c0527d8f8 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -655,7 +655,7 @@ uint8_t path_get_depth_count(void) { * @return CRC-32 checksum */ uint32_t get_path_crc(void) { - uint32_t value = CX_CRC32_INIT; + uint32_t value = 0; value = cx_crc32_update(value, &path_struct->root_struct, sizeof(path_struct->root_struct)); value = cx_crc32_update(value, &path_struct->depth_count, sizeof(path_struct->depth_count)); From 8a08f158ebc2a126cc19c3f24e4b99ec7f34f773 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 5 Aug 2024 17:21:07 +0200 Subject: [PATCH 2/6] Replaced breakpoint in client code by proper assert --- client/src/ledger_app_clients/ethereum/eip712/InputData.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/ledger_app_clients/ethereum/eip712/InputData.py b/client/src/ledger_app_clients/ethereum/eip712/InputData.py index 966c47b6c..8683f1443 100644 --- a/client/src/ledger_app_clients/ethereum/eip712/InputData.py +++ b/client/src/ledger_app_clients/ethereum/eip712/InputData.py @@ -195,9 +195,8 @@ def encode_bytes_dyn(value: str, typesize: int) -> bytes: def send_struct_impl_field(value, field): - # Something wrong happened if this triggers - if isinstance(value, list) or (field["enum"] == EIP712FieldType.CUSTOM): - breakpoint() + assert not isinstance(value, list) + assert field["enum"] != EIP712FieldType.CUSTOM data = encoding_functions[field["enum"]](value, field["typesize"]) From 9b71792390495b780b55b0f6c0966a64589548ae Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 1 Aug 2024 10:21:05 +0200 Subject: [PATCH 3/6] Updated APDU doc --- doc/ethapp.adoc | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 618b422b5..cb77ae79f 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -45,6 +45,9 @@ Application version 1.9.19 - 2022-05-17 - Add EIP-712 amount & date/time filtering - PROVIDE ERC 20 TOKEN INFORMATION & PROVIDE NFT INFORMATION now send back the index where the asset has been stored +### 1.12.0 + - Add EIP-712 discarded filter path support + ## About This application describes the APDU messages interface to communicate with the Ethereum application. @@ -828,6 +831,12 @@ Field substitution will be ignored if the full filtering is not activated. This command should come before the domain & message implementations. If activated, fields will be by default hidden unless they receive a field name substitution. +##### Discarded filter path + +This command gives the app the absolute path of the upcoming filter which will be discarded (because it targets a field within an empty array). + +The next filter should be marked as discarded (with P1) to be able to use this given filter path. + ##### Message info This command should come right after the implementation of the domain has been sent with *SEND STRUCT IMPLEMENTATION*, just before sending the message implementation. @@ -880,8 +889,11 @@ _Command_ [width="80%"] |========================================================================= | *CLA* | *INS* | *P1* | *P2* | *LC* | *Le* -| E0 | 1E | 00 - | 00 : activation +| E0 | 1E | 00 : standard + + 01 : discarded | 00 : activation + + 01 : discarded filter path 0F : message info @@ -901,6 +913,16 @@ _Input data_ None +##### If P2 == discarded filter path + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Path length | 1 +| Path | variable +|========================================== + + ##### If P2 == message info [width="80%"] From 79f7676b39f6ed83badec675bb07701b059f8053 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 Sep 2024 17:39:04 +0200 Subject: [PATCH 4/6] EIP-712 discarded filter path support in app --- src/main.c | 2 +- src_features/signMessageEIP712/commands_712.c | 55 +++--- src_features/signMessageEIP712/commands_712.h | 15 +- src_features/signMessageEIP712/filtering.c | 161 ++++++++++++++---- src_features/signMessageEIP712/filtering.h | 9 +- src_features/signMessageEIP712/path.c | 141 +++++++++++++-- src_features/signMessageEIP712/path.h | 3 + src_features/signMessageEIP712/ui_logic.c | 24 +++ src_features/signMessageEIP712/ui_logic.h | 2 + 9 files changed, 336 insertions(+), 76 deletions(-) diff --git a/src/main.c b/src/main.c index 95cda3e08..83a5d0b05 100644 --- a/src/main.c +++ b/src/main.c @@ -214,7 +214,7 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) { break; case INS_EIP712_FILTERING: - sw = handle_eip712_filtering(cmd->p2, cmd->data, cmd->lc, flags); + sw = handle_eip712_filtering(cmd->p1, cmd->p2, cmd->data, cmd->lc, flags); break; #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 70509e633..05438e6c9 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -27,6 +27,7 @@ #define P2_IMPL_ARRAY 0x0F #define P2_IMPL_FIELD P2_DEF_FIELD #define P2_FILT_ACTIVATE 0x00 +#define P2_FILT_DISCARDED_PATH 0x01 #define P2_FILT_MESSAGE_INFO 0x0F #define P2_FILT_DATE_TIME 0xFC #define P2_FILT_AMOUNT_JOIN_TOKEN 0xFD @@ -70,10 +71,12 @@ void handle_eip712_return_code(bool success) { /** * Process the EIP712 struct definition command * - * @param[in] apdu_buf the APDU payload + * @param[in] p2 instruction parameter 2 + * @param[in] cdata command data + * @param[in] length length of the command data * @return whether the command was successful or not */ -uint16_t handle_eip712_struct_def(uint8_t p2, const uint8_t *dataBuffer, uint8_t dataLength) { +uint16_t handle_eip712_struct_def(uint8_t p2, const uint8_t *cdata, uint8_t length) { bool ret = true; if (eip712_context == NULL) { @@ -86,10 +89,10 @@ uint16_t handle_eip712_struct_def(uint8_t p2, const uint8_t *dataBuffer, uint8_t if (ret) { switch (p2) { case P2_DEF_NAME: - ret = set_struct_name(dataLength, dataBuffer); + ret = set_struct_name(length, cdata); break; case P2_DEF_FIELD: - ret = set_struct_field(dataLength, dataBuffer); + ret = set_struct_field(length, cdata); break; default: PRINTF("Unknown P2 0x%x\n", p2); @@ -104,13 +107,16 @@ uint16_t handle_eip712_struct_def(uint8_t p2, const uint8_t *dataBuffer, uint8_t /** * Process the EIP712 struct implementation command * - * @param[in] apdu_buf the APDU payload + * @param[in] p1 instruction parameter 1 + * @param[in] p2 instruction parameter 2 + * @param[in] cdata command data + * @param[in] length length of the command data * @return whether the command was successful or not */ uint16_t handle_eip712_struct_impl(uint8_t p1, uint8_t p2, - const uint8_t *dataBuffer, - uint8_t dataLength, + const uint8_t *cdata, + uint8_t length, uint32_t *flags) { bool ret = false; bool reply_apdu = true; @@ -121,7 +127,7 @@ uint16_t handle_eip712_struct_impl(uint8_t p1, switch (p2) { case P2_IMPL_NAME: // set root type - ret = path_set_root((char *) dataBuffer, dataLength); + ret = path_set_root((char *) cdata, length); if (ret) { if (N_storage.verbose_eip712) { ui_712_review_struct(path_get_root()); @@ -131,12 +137,12 @@ uint16_t handle_eip712_struct_impl(uint8_t p1, } break; case P2_IMPL_FIELD: - if ((ret = field_hash(dataBuffer, dataLength, p1 != P1_COMPLETE))) { + if ((ret = field_hash(cdata, length, p1 != P1_COMPLETE))) { reply_apdu = false; } break; case P2_IMPL_ARRAY: - ret = path_new_array_depth(dataBuffer, dataLength); + ret = path_new_array_depth(cdata, length); break; default: PRINTF("Unknown P2 0x%x\n", p2); @@ -154,12 +160,16 @@ uint16_t handle_eip712_struct_impl(uint8_t p1, /** * Process the EIP712 filtering command * - * @param[in] apdu_buf the APDU payload + * @param[in] p1 instruction parameter 1 + * @param[in] p2 instruction parameter 2 + * @param[in] cdata command data + * @param[in] length length of the command data * @return whether the command was successful or not */ -uint16_t handle_eip712_filtering(uint8_t p2, - const uint8_t *dataBuffer, - uint8_t dataLength, +uint16_t handle_eip712_filtering(uint8_t p1, + uint8_t p2, + const uint8_t *cdata, + uint8_t length, uint32_t *flags) { bool ret = true; bool reply_apdu = true; @@ -179,23 +189,26 @@ uint16_t handle_eip712_filtering(uint8_t p2, } forget_known_assets(); break; + case P2_FILT_DISCARDED_PATH: + ret = filtering_discarded_path(cdata, length); + break; case P2_FILT_MESSAGE_INFO: - ret = filtering_message_info(dataBuffer, dataLength); + ret = filtering_message_info(cdata, length); if (ret) { reply_apdu = false; } break; case P2_FILT_DATE_TIME: - ret = filtering_date_time(dataBuffer, dataLength); + ret = filtering_date_time(cdata, length, p1 == 1); break; case P2_FILT_AMOUNT_JOIN_TOKEN: - ret = filtering_amount_join_token(dataBuffer, dataLength); + ret = filtering_amount_join_token(cdata, length, p1 == 1); break; case P2_FILT_AMOUNT_JOIN_VALUE: - ret = filtering_amount_join_value(dataBuffer, dataLength); + ret = filtering_amount_join_value(cdata, length, p1 == 1); break; case P2_FILT_RAW_FIELD: - ret = filtering_raw_field(dataBuffer, dataLength); + ret = filtering_raw_field(cdata, length, p1 == 1); break; default: PRINTF("Unknown P2 0x%x\n", p2); @@ -224,7 +237,7 @@ uint16_t handle_eip712_filtering(uint8_t p2, * @param[in] apdu_buf the APDU payload * @return whether the command was successful or not */ -uint16_t handle_eip712_sign(const uint8_t *dataBuffer, uint8_t dataLength, uint32_t *flags) { +uint16_t handle_eip712_sign(const uint8_t *cdata, uint8_t length, uint32_t *flags) { bool ret = false; if (eip712_context == NULL) { @@ -241,7 +254,7 @@ uint16_t handle_eip712_sign(const uint8_t *dataBuffer, uint8_t dataLength, uint3 (ui_712_remaining_filters() != 0)) { PRINTF("%d EIP712 filters are missing\n", ui_712_remaining_filters()); apdu_response_code = APDU_RESPONSE_REF_DATA_NOT_FOUND; - } else if (parseBip32(dataBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32) == NULL) { + } else if (parseBip32(cdata, &length, &tmpCtx.messageSigningContext.bip32) == NULL) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; } else { if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) { diff --git a/src_features/signMessageEIP712/commands_712.h b/src_features/signMessageEIP712/commands_712.h index a34d264e4..3bc3377f7 100644 --- a/src_features/signMessageEIP712/commands_712.h +++ b/src_features/signMessageEIP712/commands_712.h @@ -8,16 +8,17 @@ #define DOMAIN_STRUCT_NAME "EIP712Domain" -uint16_t handle_eip712_struct_def(uint8_t p2, const uint8_t *dataBuffer, uint8_t dataLength); +uint16_t handle_eip712_struct_def(uint8_t p2, const uint8_t *cdata, uint8_t length); uint16_t handle_eip712_struct_impl(uint8_t p1, uint8_t p2, - const uint8_t *dataBuffer, - uint8_t dataLength, + const uint8_t *cdata, + uint8_t length, uint32_t *flags); -uint16_t handle_eip712_sign(const uint8_t *dataBuffer, uint8_t dataLength, uint32_t *flags); -uint16_t handle_eip712_filtering(uint8_t p2, - const uint8_t *dataBuffer, - uint8_t dataLength, +uint16_t handle_eip712_sign(const uint8_t *cdata, uint8_t length, uint32_t *flags); +uint16_t handle_eip712_filtering(uint8_t p1, + uint8_t p2, + const uint8_t *cdata, + uint8_t length, uint32_t *flags); void handle_eip712_return_code(bool success); diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 276fc3649..d491a10a4 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -28,33 +28,41 @@ * Reconstruct the field path and hash it * * @param[in] hash_ctx the hashing context + * @param[in] discarded if the filter targets a field that does not exist (within an empty array) */ -static void hash_filtering_path(cx_hash_t *hash_ctx) { +static void hash_filtering_path(cx_hash_t *hash_ctx, bool discarded) { const void *field_ptr; const char *key; uint8_t key_len; - for (uint8_t i = 0; i < path_get_depth_count(); ++i) { - if (i > 0) { - hash_byte('.', hash_ctx); - } - if ((field_ptr = path_get_nth_field(i + 1)) != NULL) { - if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { - // field name - hash_nbytes((uint8_t *) key, key_len, hash_ctx); - - // array levels - if (struct_field_is_array(field_ptr)) { - uint8_t lvl_count; - - get_struct_field_array_lvls_array(field_ptr, &lvl_count); - for (int j = 0; j < lvl_count; ++j) { - hash_nbytes((uint8_t *) ".[]", 3, hash_ctx); + if (discarded) { + key = ui_712_get_discarded_path(&key_len); + hash_nbytes((uint8_t *) key, key_len, hash_ctx); + } else { + for (uint8_t i = 0; i < path_get_depth_count(); ++i) { + if (i > 0) { + hash_byte('.', hash_ctx); + } + if ((field_ptr = path_get_nth_field(i + 1)) != NULL) { + if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { + // field name + hash_nbytes((uint8_t *) key, key_len, hash_ctx); + + // array levels + if (struct_field_is_array(field_ptr)) { + uint8_t lvl_count; + + get_struct_field_array_lvls_array(field_ptr, &lvl_count); + for (int j = 0; j < lvl_count; ++j) { + hash_nbytes((uint8_t *) ".[]", 3, hash_ctx); + } } } } } } + // so it is only usable for the following filter + ui_712_set_discarded_path("", 0); } /** @@ -226,14 +234,104 @@ bool filtering_message_info(const uint8_t *payload, uint8_t length) { return true; } +/** + * Check if given path matches the backed-up path + * + * A match is found as long as the given path starts with the backed-up path. + * + * @param[in] path given path + * @param[in] path_len length of the path + * @param[out] offset_ptr offset to where the comparison stopped + * @return whether a match was found or not + */ +static bool matches_backup_path(const char *path, uint8_t path_len, uint8_t *offset_ptr) { + const void *field_ptr; + const char *key; + uint8_t key_len; + uint8_t offset = 0; + uint8_t lvl_count; + + for (uint8_t i = 0; i < path_backup_get_depth_count(); ++i) { + if (i > 0) { + if (((offset + 1) > path_len) || (memcmp(path + offset, ".", 1) != 0)) { + return false; + } + offset += 1; + } + if ((field_ptr = path_backup_get_nth_field(i + 1)) != NULL) { + if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { + // field name + if (((offset + key_len) > path_len) || (memcmp(path + offset, key, key_len) != 0)) { + return false; + } + offset += key_len; + + // array levels + if (struct_field_is_array(field_ptr)) { + get_struct_field_array_lvls_array(field_ptr, &lvl_count); + for (int j = 0; j < lvl_count; ++j) { + if (((offset + 3) > path_len) || (memcmp(path + offset, ".[]", 3) != 0)) { + return false; + } + offset += 3; + } + } + } + } + } + if (offset_ptr != NULL) { + *offset_ptr = offset; + } + return true; +} + +/** + * Command to provide the filter path of a discarded filtered field + * + * Some filtered fields are discarded/never received because they are contained in an array + * that turns out to be empty. + * + * @param[in] payload the payload to parse + * @param[in] length the payload length + * @return whether it was successful or not + */ +bool filtering_discarded_path(const uint8_t *payload, uint8_t length) { + uint8_t path_len; + const char *path; + uint8_t offset = 0; + uint8_t path_offset; + + if ((offset + sizeof(path_len)) > length) { + return false; + } + path_len = payload[offset++]; + if ((offset + path_len) > length) { + return false; + } + path = (char *) &payload[offset]; + offset += path_len; + if (offset < path_len) { + return false; + } + if (!matches_backup_path(path, path_len, &path_offset)) { + return false; + } + if (!path_exists_in_backup(path + path_offset, path_len - path_offset)) { + return false; + } + ui_712_set_discarded_path(path, path_len); + return true; +} + /** * Command to display a field as a date-time * * @param[in] payload the payload to parse * @param[in] length the payload length + * @param[in] discarded if the filter targets a field that is does not exist (within an empty array) * @return whether it was successful or not */ -bool filtering_date_time(const uint8_t *payload, uint8_t length) { +bool filtering_date_time(const uint8_t *payload, uint8_t length, bool discarded) { uint8_t name_len; const char *name; uint8_t sig_len; @@ -269,7 +367,7 @@ bool filtering_date_time(const uint8_t *payload, uint8_t length) { if (!sig_verif_start(&hash_ctx, FILT_MAGIC_DATETIME)) { return false; } - hash_filtering_path((cx_hash_t *) &hash_ctx); + hash_filtering_path((cx_hash_t *) &hash_ctx, discarded); hash_nbytes((uint8_t *) name, sizeof(char) * name_len, (cx_hash_t *) &hash_ctx); if (!sig_verif_end(&hash_ctx, sig, sig_len)) { return false; @@ -291,9 +389,10 @@ bool filtering_date_time(const uint8_t *payload, uint8_t length) { * * @param[in] payload the payload to parse * @param[in] length the payload length + * @param[in] discarded if the filter targets a field that is does not exist (within an empty array) * @return whether it was successful or not */ -bool filtering_amount_join_token(const uint8_t *payload, uint8_t length) { +bool filtering_amount_join_token(const uint8_t *payload, uint8_t length, bool discarded) { uint8_t token_idx; uint8_t sig_len; const uint8_t *sig; @@ -323,7 +422,7 @@ bool filtering_amount_join_token(const uint8_t *payload, uint8_t length) { if (!sig_verif_start(&hash_ctx, FILT_MAGIC_AMOUNT_JOIN_TOKEN)) { return false; } - hash_filtering_path((cx_hash_t *) &hash_ctx); + hash_filtering_path((cx_hash_t *) &hash_ctx, discarded); hash_byte(token_idx, (cx_hash_t *) &hash_ctx); if (!sig_verif_end(&hash_ctx, sig, sig_len)) { return false; @@ -343,9 +442,10 @@ bool filtering_amount_join_token(const uint8_t *payload, uint8_t length) { * * @param[in] payload the payload to parse * @param[in] length the payload length + * @param[in] discarded if the filter targets a field that is does not exist (within an empty array) * @return whether it was successful or not */ -bool filtering_amount_join_value(const uint8_t *payload, uint8_t length) { +bool filtering_amount_join_value(const uint8_t *payload, uint8_t length, bool discarded) { uint8_t name_len; const char *name; uint8_t token_idx; @@ -389,7 +489,7 @@ bool filtering_amount_join_value(const uint8_t *payload, uint8_t length) { if (!sig_verif_start(&hash_ctx, FILT_MAGIC_AMOUNT_JOIN_VALUE)) { return false; } - hash_filtering_path((cx_hash_t *) &hash_ctx); + hash_filtering_path((cx_hash_t *) &hash_ctx, discarded); hash_nbytes((uint8_t *) name, sizeof(char) * name_len, (cx_hash_t *) &hash_ctx); hash_byte(token_idx, (cx_hash_t *) &hash_ctx); if (!sig_verif_end(&hash_ctx, sig, sig_len)) { @@ -423,9 +523,10 @@ bool filtering_amount_join_value(const uint8_t *payload, uint8_t length) { * * @param[in] payload the payload to parse * @param[in] length the payload length + * @param[in] discarded if the filter targets a field that is does not exist (within an empty array) * @return whether it was successful or not */ -bool filtering_raw_field(const uint8_t *payload, uint8_t length) { +bool filtering_raw_field(const uint8_t *payload, uint8_t length, bool discarded) { uint8_t name_len; const char *name; uint8_t sig_len; @@ -461,17 +562,19 @@ bool filtering_raw_field(const uint8_t *payload, uint8_t length) { if (!sig_verif_start(&hash_ctx, FILT_MAGIC_RAW_FIELD)) { return false; } - hash_filtering_path((cx_hash_t *) &hash_ctx); + hash_filtering_path((cx_hash_t *) &hash_ctx, discarded); hash_nbytes((uint8_t *) name, sizeof(char) * name_len, (cx_hash_t *) &hash_ctx); if (!sig_verif_end(&hash_ctx, sig, sig_len)) { return false; } - // Handling - if (name_len > 0) { // don't substitute for an empty name - ui_712_set_title(name, name_len); + if (!discarded) { + // Handling + if (name_len > 0) { // don't substitute for an empty name + ui_712_set_title(name, name_len); + } + ui_712_flag_field(true, name_len > 0, false, false); } - ui_712_flag_field(true, name_len > 0, false, false); return true; } diff --git a/src_features/signMessageEIP712/filtering.h b/src_features/signMessageEIP712/filtering.h index d35963cd9..8143edfb1 100644 --- a/src_features/signMessageEIP712/filtering.h +++ b/src_features/signMessageEIP712/filtering.h @@ -9,10 +9,11 @@ #define MAX_FILTERS 50 bool filtering_message_info(const uint8_t *payload, uint8_t length); -bool filtering_date_time(const uint8_t *payload, uint8_t length); -bool filtering_amount_join_token(const uint8_t *payload, uint8_t length); -bool filtering_amount_join_value(const uint8_t *payload, uint8_t length); -bool filtering_raw_field(const uint8_t *payload, uint8_t length); +bool filtering_date_time(const uint8_t *payload, uint8_t length, bool discarded); +bool filtering_amount_join_token(const uint8_t *payload, uint8_t length, bool discarded); +bool filtering_amount_join_value(const uint8_t *payload, uint8_t length, bool discarded); +bool filtering_raw_field(const uint8_t *payload, uint8_t length, bool discarded); +bool filtering_discarded_path(const uint8_t *payload, uint8_t length); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index c0527d8f8..037550440 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -13,28 +13,30 @@ #include "typed_data.h" static s_path *path_struct = NULL; +static s_path *path_backup = NULL; /** - * Get the field pointer to by the first N depths of the path. + * Get the field pointer to by the first N depths of the given path * + * @param[in] path given path struct * @param[out] fields_count_ptr the number of fields in the last evaluated depth * @param[in] n the number of depths to evaluate * @return the field which the first Nth depths points to */ -static const void *get_nth_field(uint8_t *const fields_count_ptr, uint8_t n) { +static const void *get_nth_field_from(const s_path *path, uint8_t *fields_count_ptr, uint8_t n) { const void *struct_ptr = NULL; const void *field_ptr = NULL; const char *typename; uint8_t length; uint8_t fields_count; - if (path_struct == NULL) { + if (path == NULL) { return NULL; } - struct_ptr = path_struct->root_struct; + struct_ptr = path->root_struct; - if (n > path_struct->depth_count) // sanity check + if (n > path->depth_count) // sanity check { return NULL; } @@ -45,11 +47,11 @@ static const void *get_nth_field(uint8_t *const fields_count_ptr, uint8_t n) { *fields_count_ptr = fields_count; } // check if the index at this depth makes sense - if (path_struct->depths[depth] > fields_count) { + if (path->depths[depth] > fields_count) { return NULL; } - for (uint8_t index = 0; index < path_struct->depths[depth]; ++index) { + for (uint8_t index = 0; index < path->depths[depth]; ++index) { field_ptr = get_next_struct_field(field_ptr); } if (struct_field_type(field_ptr) == TYPE_CUSTOM) { @@ -62,6 +64,10 @@ static const void *get_nth_field(uint8_t *const fields_count_ptr, uint8_t n) { return field_ptr; } +static const void *get_nth_field(uint8_t *fields_count_ptr, uint8_t n) { + return get_nth_field_from(path_struct, fields_count_ptr, n); +} + /** * Get the element the path is pointing to. * @@ -82,6 +88,10 @@ const void *path_get_nth_field(uint8_t n) { return get_nth_field(NULL, n); } +const void *path_backup_get_nth_field(uint8_t n) { + return get_nth_field_from(path_backup, NULL, n); +} + /** * Get Nth to last struct field from path * @@ -452,6 +462,27 @@ static bool check_and_add_array_depth(const void *depth, return true; } +/** + * Back-up the current path + * + * Used for the handling of discarded filtered fields + */ +static void backup_path(void) { + const void *field_ptr; + + memcpy(path_backup, path_struct, sizeof(*path_backup)); + // decrease while it does not point to an array type + while (path_backup->depth_count > 1) { + if ((field_ptr = path_backup_get_nth_field(path_backup->depth_count)) == NULL) { + return; + } + if (struct_field_is_array(field_ptr)) { + break; + } + path_backup->depth_count -= 1; + } +} + /** * Add a new array depth with a given size (number of elements). * @@ -479,6 +510,9 @@ bool path_new_array_depth(const uint8_t *const data, uint8_t length) { } array_size = *data; + if (array_size == 0) { + backup_path(); + } if (!path_update(false, array_size > 0, array_size > 0)) { return false; } @@ -633,15 +667,92 @@ const void *path_get_root(void) { } /** - * Get the current amount of depth + * Get the current amount of depth in a given path struct * + * @param[in] given path struct * @return depth count */ -uint8_t path_get_depth_count(void) { - if (path_struct == NULL) { +static uint8_t get_depth_count(const s_path *path) { + if (path == NULL) { return 0; } - return path_struct->depth_count; + return path->depth_count; +} + +/** + * Get the current amount of depth in the path + * + * @return depth count + */ +uint8_t path_get_depth_count(void) { + return get_depth_count(path_struct); +} + +/** + * Get the current amount of depth in the backup path + * + * @return depth count + */ +uint8_t path_backup_get_depth_count(void) { + return get_depth_count(path_backup); +} + +/** + * Check if the given relative path exists in the backup path + * + * @param[in] path given path + * @param[in] length length of the path + * @return whether it exists or not + */ +bool path_exists_in_backup(const char *path, size_t length) { + size_t offset = 0; + size_t i; + const void *field_ptr; + s_path tmp_path; + const char *typename; + uint8_t typename_len; + const void *struct_ptr; + uint8_t fields_count; + const char *key; + uint8_t key_len; + + memcpy(&tmp_path, path_backup, sizeof(tmp_path)); + field_ptr = get_nth_field_from(&tmp_path, NULL, tmp_path.depth_count); + while (offset < length) { + if (((offset + 1) > length) || (memcmp(path + offset, ".", 1) != 0)) { + return false; + } + offset += 1; + if (((offset + 2) <= length) && (memcmp(path + offset, "[]", 2) == 0)) { + if (!struct_field_is_array(field_ptr)) { + return false; + } + offset += 2; + } else if (offset < length) { + for (i = 0; ((offset + i) < length) && (path[offset + i] != '.'); ++i) + ; + typename = get_struct_field_custom_typename(field_ptr, &typename_len); + if ((struct_ptr = get_structn(typename, typename_len)) == NULL) { + return false; + } + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + while (fields_count > 0) { + key = get_struct_field_keyname(field_ptr, &key_len); + if ((key_len == i) && (memcmp(key, path + offset, i) == 0)) { + break; + } + field_ptr = get_next_struct_field(field_ptr); + fields_count -= 1; + } + if (fields_count == 0) { + return false; + } + offset += i; + } else { + return false; + } + } + return true; } /** @@ -680,13 +791,15 @@ uint32_t get_path_crc(void) { */ bool path_init(void) { if (path_struct == NULL) { - if ((path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct)) == NULL) { + if (((path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct)) == NULL) || + ((path_backup = MEM_ALLOC_AND_ALIGN_TYPE(*path_backup)) == NULL)) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; } else { - path_struct->depth_count = 0; + explicit_bzero(path_struct, sizeof(*path_struct)); + explicit_bzero(path_backup, sizeof(*path_backup)); } } - return path_struct != NULL; + return (path_struct != NULL) && (path_backup != NULL); } /** diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index 336659e85..8285d68f4 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -35,8 +35,11 @@ bool path_new_array_depth(const uint8_t *const data, uint8_t length); e_root_type path_get_root_type(void); const void *path_get_root(void); const void *path_get_nth_field(uint8_t n); +const void *path_backup_get_nth_field(uint8_t n); +bool path_exists_in_backup(const char *path, size_t length); const void *path_get_nth_field_to_last(uint8_t n); uint8_t path_get_depth_count(void); +uint8_t path_backup_get_depth_count(void); uint32_t get_path_crc(void); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 11d08172b..8694cf6e3 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -59,6 +59,8 @@ typedef struct { s_amount_context amount; uint8_t filters_received; uint32_t filters_crc[MAX_FILTERS]; + uint8_t discarded_path_length; + char discarded_path[255]; #ifdef SCREEN_SIZE_WALLET char ui_pairs_buffer[(SHARED_CTX_FIELD_1_SIZE + SHARED_CTX_FIELD_2_SIZE) * 2]; #endif @@ -805,6 +807,28 @@ bool ui_712_push_new_filter_path(void) { return true; } +/** + * Set a discarded filter path + * + * @param[in] path the given filter path + * @param[in] length the path length + */ +void ui_712_set_discarded_path(const char *path, uint8_t length) { + memcpy(ui_ctx->discarded_path, path, length); + ui_ctx->discarded_path_length = length; +} + +/** + * Get the discarded filter path + * + * @param[out] length the path length + * @return filter path + */ +const char *ui_712_get_discarded_path(uint8_t *length) { + *length = ui_ctx->discarded_path_length; + return ui_ctx->discarded_path; +} + #ifdef SCREEN_SIZE_WALLET /* * Get UI pairs buffer diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 9ce4ee152..d04c8db10 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -44,6 +44,8 @@ void ui_712_token_join_prepare_amount(uint8_t index, const char *name, uint8_t n void amount_join_set_token_received(void); bool ui_712_show_raw_key(const void *field_ptr); bool ui_712_push_new_filter_path(void); +void ui_712_set_discarded_path(const char *path, uint8_t length); +const char *ui_712_get_discarded_path(uint8_t *length); #ifdef SCREEN_SIZE_WALLET char *get_ui_pairs_buffer(size_t *size); #endif From 1140bf515e4c7b8eb21cdde293d530ea9e1dda7c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 5 Aug 2024 18:11:45 +0200 Subject: [PATCH 5/6] EIP-712 discarded filter path support in python client --- .../src/ledger_app_clients/ethereum/client.py | 21 ++++-- .../ethereum/command_builder.py | 26 +++++-- .../ethereum/eip712/InputData.py | 75 ++++++++++--------- 3 files changed, 70 insertions(+), 52 deletions(-) diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index d9ddcae88..a6008402b 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -146,25 +146,30 @@ def eip712_sign_legacy(self, def eip712_filtering_activate(self): return self._exchange_async(self._cmd_builder.eip712_filtering_activate()) + def eip712_filtering_discarded_path(self, path: str): + return self._exchange(self._cmd_builder.eip712_filtering_discarded_path(path)) + def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes): return self._exchange_async(self._cmd_builder.eip712_filtering_message_info(name, filters_count, sig)) - def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes): + def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes, discarded: bool): return self._exchange_async(self._cmd_builder.eip712_filtering_amount_join_token(token_idx, - sig)) + sig, + discarded)) - def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes): + def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes, discarded: bool): return self._exchange_async(self._cmd_builder.eip712_filtering_amount_join_value(token_idx, name, - sig)) + sig, + discarded)) - def eip712_filtering_datetime(self, name: str, sig: bytes): - return self._exchange_async(self._cmd_builder.eip712_filtering_datetime(name, sig)) + def eip712_filtering_datetime(self, name: str, sig: bytes, discarded: bool): + return self._exchange_async(self._cmd_builder.eip712_filtering_datetime(name, sig, discarded)) - def eip712_filtering_raw(self, name: str, sig: bytes): - return self._exchange_async(self._cmd_builder.eip712_filtering_raw(name, sig)) + def eip712_filtering_raw(self, name: str, sig: bytes, discarded: bool): + return self._exchange_async(self._cmd_builder.eip712_filtering_raw(name, sig, discarded)) def sign(self, bip32_path: str, diff --git a/client/src/ledger_app_clients/ethereum/command_builder.py b/client/src/ledger_app_clients/ethereum/command_builder.py index bcd505cbe..238a19b66 100644 --- a/client/src/ledger_app_clients/ethereum/command_builder.py +++ b/client/src/ledger_app_clients/ethereum/command_builder.py @@ -41,6 +41,7 @@ class P2Type(IntEnum): LEGACY_IMPLEM = 0x00 NEW_IMPLEM = 0x01 FILTERING_ACTIVATE = 0x00 + FILTERING_DISCARDED_PATH = 0x01 FILTERING_MESSAGE_INFO = 0x0f FILTERING_DATETIME = 0xfc FILTERING_TOKEN_ADDR_CHECK = 0xfd @@ -164,6 +165,15 @@ def _eip712_filtering_send_name(self, name: str, sig: bytes) -> bytes: data += sig return data + def eip712_filtering_discarded_path(self, path: str) -> bytes: + data = bytearray() + data.append(len(path)) + data += path.encode() + return self._serialize(InsType.EIP712_SEND_FILTERING, + P1Type.COMPLETE_SEND, + P2Type.FILTERING_DISCARDED_PATH, + data) + def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes) -> bytes: data = bytearray() data.append(len(name)) @@ -176,17 +186,17 @@ def eip712_filtering_message_info(self, name: str, filters_count: int, sig: byte P2Type.FILTERING_MESSAGE_INFO, data) - def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes) -> bytes: + def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes, discarded: bool) -> bytes: data = bytearray() data.append(token_idx) data.append(len(sig)) data += sig return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.COMPLETE_SEND, + int(discarded), P2Type.FILTERING_TOKEN_ADDR_CHECK, data) - def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes) -> bytes: + def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes, discarded: bool) -> bytes: data = bytearray() data.append(len(name)) data += name.encode() @@ -194,19 +204,19 @@ def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: byt data.append(len(sig)) data += sig return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.COMPLETE_SEND, + int(discarded), P2Type.FILTERING_AMOUNT_FIELD, data) - def eip712_filtering_datetime(self, name: str, sig: bytes) -> bytes: + def eip712_filtering_datetime(self, name: str, sig: bytes, discarded: bool) -> bytes: return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.COMPLETE_SEND, + int(discarded), P2Type.FILTERING_DATETIME, self._eip712_filtering_send_name(name, sig)) - def eip712_filtering_raw(self, name: str, sig: bytes) -> bytes: + def eip712_filtering_raw(self, name: str, sig: bytes, discarded: bool) -> bytes: return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.COMPLETE_SEND, + int(discarded), P2Type.FILTERING_RAW, self._eip712_filtering_send_name(name, sig)) diff --git a/client/src/ledger_app_clients/ethereum/eip712/InputData.py b/client/src/ledger_app_clients/ethereum/eip712/InputData.py index 8683f1443..e2d9f3e8d 100644 --- a/client/src/ledger_app_clients/ethereum/eip712/InputData.py +++ b/client/src/ledger_app_clients/ethereum/eip712/InputData.py @@ -194,6 +194,26 @@ def encode_bytes_dyn(value: str, typesize: int) -> bytes: encoding_functions[EIP712FieldType.DYN_BYTES] = encode_bytes_dyn +def send_filter(path: str, discarded: bool): + assert path in filtering_paths.keys() + + if filtering_paths[path]["type"] == "amount_join_token": + send_filtering_amount_join_token(path, filtering_paths[path]["token"], discarded) + elif filtering_paths[path]["type"] == "amount_join_value": + if "token" in filtering_paths[path].keys(): + token = filtering_paths[path]["token"] + else: + # Permit (ERC-2612) + token = 0xff + send_filtering_amount_join_value(path, token, filtering_paths[path]["name"], discarded) + elif filtering_paths[path]["type"] == "datetime": + send_filtering_datetime(path, filtering_paths[path]["name"], discarded) + elif filtering_paths[path]["type"] == "raw": + send_filtering_raw(path, filtering_paths[path]["name"], discarded) + else: + assert False + + def send_struct_impl_field(value, field): assert not isinstance(value, list) assert field["enum"] != EIP712FieldType.CUSTOM @@ -203,22 +223,7 @@ def send_struct_impl_field(value, field): if filtering_paths: path = ".".join(current_path) if path in filtering_paths.keys(): - if filtering_paths[path]["type"] == "amount_join_token": - send_filtering_amount_join_token(filtering_paths[path]["token"]) - elif filtering_paths[path]["type"] == "amount_join_value": - if "token" in filtering_paths[path].keys(): - token = filtering_paths[path]["token"] - else: - # Permit (ERC-2612) - token = 0xff - send_filtering_amount_join_value(token, - filtering_paths[path]["name"]) - elif filtering_paths[path]["type"] == "datetime": - send_filtering_datetime(filtering_paths[path]["name"]) - elif filtering_paths[path]["type"] == "raw": - send_filtering_raw(filtering_paths[path]["name"]) - else: - assert False + send_filter(path, False) with app_client.eip712_send_struct_impl_struct_field(data): enable_autonext() @@ -233,6 +238,12 @@ def evaluate_field(structs, data, field, lvls_left, new_level=True): if len(array_lvls) > 0 and lvls_left > 0: with app_client.eip712_send_struct_impl_array(len(data)): pass + if len(data) == 0: + for path in filtering_paths.keys(): + dpath = ".".join(current_path) + ".[]" + if path.startswith(dpath): + app_client.eip712_filtering_discarded_path(path) + send_filter(path, True) idx = 0 for subdata in data: current_path.append("[]") @@ -294,57 +305,49 @@ def send_filtering_message_info(display_name: str, filters_count: int): disable_autonext() -def send_filtering_amount_join_token(token_idx: int): +def send_filtering_amount_join_token(path: str, token_idx: int, discarded: bool): global sig_ctx - path_str = ".".join(current_path) - to_sign = start_signature_payload(sig_ctx, 11) - to_sign += path_str.encode() + to_sign += path.encode() to_sign.append(token_idx) sig = keychain.sign_data(keychain.Key.CAL, to_sign) - with app_client.eip712_filtering_amount_join_token(token_idx, sig): + with app_client.eip712_filtering_amount_join_token(token_idx, sig, discarded): pass -def send_filtering_amount_join_value(token_idx: int, display_name: str): +def send_filtering_amount_join_value(path: str, token_idx: int, display_name: str, discarded: bool): global sig_ctx - path_str = ".".join(current_path) - to_sign = start_signature_payload(sig_ctx, 22) - to_sign += path_str.encode() + to_sign += path.encode() to_sign += display_name.encode() to_sign.append(token_idx) sig = keychain.sign_data(keychain.Key.CAL, to_sign) - with app_client.eip712_filtering_amount_join_value(token_idx, display_name, sig): + with app_client.eip712_filtering_amount_join_value(token_idx, display_name, sig, discarded): pass -def send_filtering_datetime(display_name: str): +def send_filtering_datetime(path: str, display_name: str, discarded: bool): global sig_ctx - path_str = ".".join(current_path) - to_sign = start_signature_payload(sig_ctx, 33) - to_sign += path_str.encode() + to_sign += path.encode() to_sign += display_name.encode() sig = keychain.sign_data(keychain.Key.CAL, to_sign) - with app_client.eip712_filtering_datetime(display_name, sig): + with app_client.eip712_filtering_datetime(display_name, sig, discarded): pass # ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures -def send_filtering_raw(display_name): +def send_filtering_raw(path: str, display_name: str, discarded: bool): global sig_ctx - path_str = ".".join(current_path) - to_sign = start_signature_payload(sig_ctx, 72) - to_sign += path_str.encode() + to_sign += path.encode() to_sign += display_name.encode() sig = keychain.sign_data(keychain.Key.CAL, to_sign) - with app_client.eip712_filtering_raw(display_name, sig): + with app_client.eip712_filtering_raw(display_name, sig, discarded): pass From ab94ac864a9d2f970213aaf7f1bdc42b08020901 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 5 Aug 2024 18:11:59 +0200 Subject: [PATCH 6/6] EIP-712 discarded filter path Ragger test --- .../00000.png | Bin 0 -> 8943 bytes .../00001.png | Bin 0 -> 9639 bytes .../00002.png | Bin 0 -> 9417 bytes .../00003.png | Bin 0 -> 6132 bytes .../00000.png | Bin 0 -> 472 bytes .../00001.png | Bin 0 -> 485 bytes .../00002.png | Bin 0 -> 390 bytes .../00003.png | Bin 0 -> 364 bytes .../00004.png | Bin 0 -> 381 bytes .../00000.png | Bin 0 -> 472 bytes .../00001.png | Bin 0 -> 485 bytes .../00002.png | Bin 0 -> 390 bytes .../00003.png | Bin 0 -> 364 bytes .../00004.png | Bin 0 -> 381 bytes .../00000.png | Bin 0 -> 8642 bytes .../00001.png | Bin 0 -> 8956 bytes .../00002.png | Bin 0 -> 8645 bytes .../00003.png | Bin 0 -> 5988 bytes tests/ragger/test_eip712.py | 91 ++++++++++++++++++ 19 files changed, 91 insertions(+) create mode 100644 tests/ragger/snapshots/flex/test_eip712_filtering_empty_array/00000.png create mode 100644 tests/ragger/snapshots/flex/test_eip712_filtering_empty_array/00001.png create mode 100644 tests/ragger/snapshots/flex/test_eip712_filtering_empty_array/00002.png create mode 100644 tests/ragger/snapshots/flex/test_eip712_filtering_empty_array/00003.png create mode 100644 tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00000.png create mode 100644 tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00001.png create mode 100644 tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00002.png create mode 100644 tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00003.png create mode 100644 tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00004.png create mode 100644 tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00000.png create mode 100644 tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00001.png create mode 100644 tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00002.png create mode 100644 tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00003.png create mode 100644 tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00004.png create mode 100644 tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00000.png create mode 100644 tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00001.png create mode 100644 tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00002.png create mode 100644 tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00003.png diff --git a/tests/ragger/snapshots/flex/test_eip712_filtering_empty_array/00000.png b/tests/ragger/snapshots/flex/test_eip712_filtering_empty_array/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..14713d83b6d15156ce76fb2abc1239496eb5b63c GIT binary patch literal 8943 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00000.png b/tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..b546f65af2e624cf54d7462335867daf18c90153 GIT binary patch literal 472 zcmV;}0Vn>6P)Nkl!1@zUYUn@802huw) z?fRqHv&kH0`to?FOand~0R4Elz%gFRzu3Y-*zSnjG9Fd{00000z+JzPLVxDA#@Ge` O000041i&FGB@G=N8&E~!=z~_MMS=md7oddI)GGA>V^OS001oax3~I~l5EEqH@4&# zrId647XN%;A#dy)0oRq=8((@v4yBZ0jCz08Yu6sxpPitqsC4(1A^2HeWu1ATx<1{mP zX)CS#uzu!kChlpQJFcj4NVSfDWy|#TsbK1)v1~>e7(g9UCnnHWq(_W}R_000000000000000 b0Mq;fEy|lAlNgKm00000NkvXXu0mjf#LnKr literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00002.png b/tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..f070aac76a3152f761900266742d9af3cf259e2e GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|{t1ba4!+nDh2#F5e*qo`yt| zdo91^SC)Tl=RC-i8tukdJ3rn^GT~>`E&-q(hVuTKvKu-&b_pfVxiopdn$lAbp1l_@ zXP2?BJ8@Vl*(d(<`Bfh_c|W(@&AfbRLtT=VQfk5F^{+lRnMpU89K8B%5}UG)&CS)@ z?lc|B3pC5w#uQa}{`tRO?D=_{895@ST-HmG-xF=a&B(y;pzf1y?3!tB!b;T(D+aRqWLZUMv6koI4!F_;lZuSq*oY);>G8 z?Zq~xYrd{A*8EpIlkeNrhVz_GbMB3)-+tiWlsUUL-hQO;dR;?7!vDRt5m(EzoL`DQ zuxDKrmHuWTlN-zD?U~0E*Ko!EVn53lA^K^`+Xa#-i{392%3Ack$8bhxY{oLb36jkV gIrKpxf&>iCNNdby-x(^%_Xs5F>FVdQ&MBb@0I$BM#Q*>R literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00003.png b/tests/ragger/snapshots/nanosp/test_eip712_filtering_empty_array/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..53ae65195fbea5b281a1bc1a80351dbf1cd79ff8 GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|=-%ba4!+nDh2#AYZeBfa}G~ z%X2sUSKTFSd4J{-YHcY(QO5;J{<~e9q=7F?O4)!WO}6UUdH! zS+Hz=(4})vCM2-VD4nt3W%LdvkyzeG%km#`1t_mu*zR}#jNB{EKZnW}g5-=Bym-_% z`=7%C>zxJL|9msNSMXnbQRayy^)nr>EU7olTHqoc^vG(B%bo(Id*|kR{84!`z2x6n zZ`;PkbrPvd6U}A>Y0chs_tyHmLi6kw6c{>JvYiQ=+MTFTx8d*9^V|R0+_75oZt9or zLetinU!`YMvFyLCb8XxElhO0{v2rTkNR$jMW7~4}*8a52+MC^J%KMbxPN-JRb2RNP zEsp0ebAPS3=e2G53#Z$XZAI6Yd=YU4`s1;0 z?{f>9Zfbf|_00eLCwPudd#p2Mzfg8-=Z~Y&(Z3(6-dSPln_e}2 z&8bJe=5=w3_V0Wxpw!uqXV@ zTyg#0l&4R7|Ayyoo}1J3WZRs6lez;p_x-s3`O4ERHjEo5Zk@S4@1z`Oka)v{pj(C~ z>TB;CrkB0D2@>;k^>bP0l+XkKqsFS~ literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00000.png b/tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..b546f65af2e624cf54d7462335867daf18c90153 GIT binary patch literal 472 zcmV;}0Vn>6P)Nkl!1@zUYUn@802huw) z?fRqHv&kH0`to?FOand~0R4Elz%gFRzu3Y-*zSnjG9Fd{00000z+JzPLVxDA#@Ge` O000041i&FGB@G=N8&E~!=z~_MMS=md7oddI)GGA>V^OS001oax3~I~l5EEqH@4&# zrId647XN%;A#dy)0oRq=8((@v4yBZ0jCz08Yu6sxpPitqsC4(1A^2HeWu1ATx<1{mP zX)CS#uzu!kChlpQJFcj4NVSfDWy|#TsbK1)v1~>e7(g9UCnnHWq(_W}R_000000000000000 b0Mq;fEy|lAlNgKm00000NkvXXu0mjf#LnKr literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00002.png b/tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..f070aac76a3152f761900266742d9af3cf259e2e GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|{t1ba4!+nDh2#F5e*qo`yt| zdo91^SC)Tl=RC-i8tukdJ3rn^GT~>`E&-q(hVuTKvKu-&b_pfVxiopdn$lAbp1l_@ zXP2?BJ8@Vl*(d(<`Bfh_c|W(@&AfbRLtT=VQfk5F^{+lRnMpU89K8B%5}UG)&CS)@ z?lc|B3pC5w#uQa}{`tRO?D=_{895@ST-HmG-xF=a&B(y;pzf1y?3!tB!b;T(D+aRqWLZUMv6koI4!F_;lZuSq*oY);>G8 z?Zq~xYrd{A*8EpIlkeNrhVz_GbMB3)-+tiWlsUUL-hQO;dR;?7!vDRt5m(EzoL`DQ zuxDKrmHuWTlN-zD?U~0E*Ko!EVn53lA^K^`+Xa#-i{392%3Ack$8bhxY{oLb36jkV gIrKpxf&>iCNNdby-x(^%_Xs5F>FVdQ&MBb@0I$BM#Q*>R literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00003.png b/tests/ragger/snapshots/nanox/test_eip712_filtering_empty_array/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..53ae65195fbea5b281a1bc1a80351dbf1cd79ff8 GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|=-%ba4!+nDh2#AYZeBfa}G~ z%X2sUSKTFSd4J{-YHcY(QO5;J{<~e9q=7F?O4)!WO}6UUdH! zS+Hz=(4})vCM2-VD4nt3W%LdvkyzeG%km#`1t_mu*zR}#jNB{EKZnW}g5-=Bym-_% z`=7%C>zxJL|9msNSMXnbQRayy^)nr>EU7olTHqoc^vG(B%bo(Id*|kR{84!`z2x6n zZ`;PkbrPvd6U}A>Y0chs_tyHmLi6kw6c{>JvYiQ=+MTFTx8d*9^V|R0+_75oZt9or zLetinU!`YMvFyLCb8XxElhO0{v2rTkNR$jMW7~4}*8a52+MC^J%KMbxPN-JRb2RNP zEsp0ebAPS3=e2G53#Z$XZAI6Yd=YU4`s1;0 z?{f>9Zfbf|_00eLCwPudd#p2Mzfg8-=Z~Y&(Z3(6-dSPln_e}2 z&8bJe=5=w3_V0Wxpw!uqXV@ zTyg#0l&4R7|Ayyoo}1J3WZRs6lez;p_x-s3`O4ERHjEo5Zk@S4@1z`Oka)v{pj(C~ z>TB;CrkB0D2@>;k^>bP0l+XkKqsFS~ literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00000.png b/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..eb8072d46ba3724406e0b827908ff8136c55d479 GIT binary patch literal 8642 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 T+zt3|hLEGZ%Q>uFVA{U{__8iD literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00001.png b/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..efce2dfe667940b4206fb84b1638b50b163776ed GIT binary patch literal 8956 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00002.png b/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..fbea72d8f0418646c42bfbfdbf513b79ac710de0 GIT binary patch literal 8645 zcmeHtc{r5c-@i3PB->=I5HW;oS=*2zAIcy*Nn*%0V;fsVS&Qr;Dw1Uw%V02;P$r)V z*=HydikY#+V3>LC>Upl`^XGFtzvp*-|M~uLt~uwv&VA0g-|utY=k)GhB^wqN)+a11Y%jRkfG2uZZIvu6$5YHNoxc(GoI<1^P;EVZYd;y!c|Qq$ z=hkJDVm13!(Th)ul?B@IDaT}gMn7Htm}hKt!l<(ZyHW{>JIS4B&izvshmterW3iZF zraH5*d{6FVJ9huy<=-~=7w-WQeuA@edjLi7M|9-tP-bCo=QrTP70y#we*EcqTXAd2*|9&p# za4a7i@1N3FdoOeJt`&giL5~03{FDoE2dRoC z*Cla)*myfAFvn~!$D8xv(i~l9a2Jb(MGORZnZhRO1%qS(#Lt!hcglI;rYX_eDq+aVj&?stnZK2viwWS z6TT`G7>AO?2(uJXfX%FCVlBy^SgJ@yx`X-xB@^7I*Nu&hmp7+CnPorjHb&T-3jDAZ z)?oa=9mLVq{IIM2nODm+h4ABOf1qT)r>G(hHT#rIqbnwfUlmFC8jVFFgN%yzd;ebC z*ho$_Rec4i9G&p$l6sC`Z1S7()Ar1|+o1XtkH^z!X!^|>#iThpoxZqOz8-OygZgeY zrsze_*IFml9Gq9XXy-eTUvA=0yhy^}=GTFaA53=fiYir_miroV=BPGw8)4Sq|gNI4>!)P5GQlWeCD0u{%JvO>7V3*6HZ;+k9L)DO)pi%Y0#Ga(UCSE zSU{wRcz4hk(zhjGu2hlWwl|V;FYA-7Mp9{0Wt_FJy!o(h_q^(NO;M)y%9-n_Izc8bZSk&XOgXcJB11lRyBIeb zpB7DSS`ssk!L(GM%MA96X*csd`DupT0`+emGu43 ze&I^EX?Uu-dB%;Ic27A)RP4pRu@er>yiBtqkN=XB@T_YOcR~);Vp7}^NgW9fm$-PJ0 zgQo_x$(u6fNY^`2d-p>zJMRARjeyY(+ z{;b%BXg960ERh6vQe2o(@pMNVU^{WUw&s8Uo~kBGPQRF6RzC70M9;RPRf05X3=>N>DOz0I z*a^|wQLrJMy*{}fjYZSPvioCbQ=O>gR(xefcNV-JyupMZE{EB3?mx|!GbixVm`+}R zGqz>xx8BtDUo2XTu3Iku_JuuimEVUHqqkYe z?J6E9X*WihS?|?r+Kj?PK7_8g@ck7NcZGdSpetf}jCrCWDtWuu4k56Oc)-BCpIyvK zAI7E;!aN$B--tV%M|VXGV7fWZe_5fhRyLErGp>BU+~+Gm?$Uo%qmdp`%qR{_!hUfl4@wl ziTm)&CSDxXOow}L&zbySy+S=x_fo~Xd+#OKam<=Kl~1u2W;wNiL{_nbyB~Y*`hLJ@ z3dFytmP_>xnzy3kw$G1g-~OrtB_uPVeI*%tR#m8kazPULcNq+$V!T+o4|C(Yy)Iu5wZh-39BO)*|NYS+Xc9{a1}yVDQOQF&iMvNb8+H zoDvrGSzwlmsBB!ctd0*aLP9F9s3et6P+%K7QQ8HgLSLtB2T;!kZv*#ybV2WW`U6v| ziFRQBCPhH$Yl0_-y5Bm2MYodQgX*U#sz$Vd##61>ZwlKJ#yTZ27~<5{vt{uRzSX=f z(`cF_WWLqqk` zH;+Q?M@mifz#h#TS5oIpr74qfqhT0xSu}jN;raebanILJ6KbabxTTOaPdi9a_GgB~ zlr$RV_3y1nZ+Npbiy`ke@AR*&h4~p&m9O$mbcs|2^kbd?!PDcqdp0< zj|0`;tZv1oK6m6NM^X-p!Cun11~)Jdyc|@ScO$3LLTzj47!))lDqqK%j)zS+DaU7ed#ik^O}UM+rcF`J$4 zqA$nrT@|H%pNyMdc^APx0qjx+@fD(p40~fpS(a$K3nu3|T@A(%ThCzeVdqINSt;`4 zyQcBS?qQn@udm+I{dl{6KIjM6lXmN|tlG7<{0nZt&V&jL-mXU?k$sIzvy$wPmN!rt zexq8hl_!0@rUB=f-h@b=mx^!kg4G{SRf9ii2R3MQ)ZVNHZp?PxuC$ch>gA?zdnlZ@ z$#QA=li`QvQbx=QGZe3| zCWAfW@#|Ze{=z1eqe?i`=-qj&%c&2swo05h)W@1RqyDBVhkk$cE|)d(Z^b^Is$}>R z$5gl-%-E$YD7ebg_`Uamv49bkOz5n2R?t5@`)UNMX@cw&7okhj3R55@rnZLrgoxGVBiq=8@<){ zFOsy!HYUo18mzu&E{aP@RNcTbs@~Mj%kb|^Fb^%0Uj~p3&v|Oxl@Xq*E|HK_ zDc8dsQd+IBtbzMS_g>C20No`Rh?b?jj93 z0~5ukT`HxTt~g{h4rS~4AiH&FC1s(12I+Hi)>@LJvPlR}Gm`ek%a((U+W5|79Fac3ib;+&$cr`n{#HIY| zos_lWSN;rs6T2eC&-(^U@3$XsHi?<`EAf?CEO$n~F?W)0L!!VGOFl;9*+}^?8^b8L zPL&jTDO{v%{WQ0X<0Xs719AI*od(&nxQt#6CJ;35*`cKmR|LB<%V^1-b&X-Y9QBF<^G#ZUZzD10UH%tm~x0@OZJyRjk2^b{zL(_;MA$UsE& z8aLRj^kflaER)yVjrF?RQ^?qvg-+x{#)J-9 z{ZR+5A1AheSbzJO0SS=6DwyMEV)Slk&bmd{PSlq1+QyCn?C~mHKQ89Vt|pw| zvsZw0l;Te$`g^DF#JlCE<{aY&ZuCQVySr!Sh-wL;l>j*=`lF|-Q>%RjPypjqOaj6_ z2Ar$-*i=TP3VpaguxQaUhi$}bWQj(D&&m1%ju5m^65DGUm7Q{cS5=+ktpwwKYa-Ud zeYpVr)=ew@o(+}e2UMpo+RgUh11V%bv?IycJ_HH!oYVDhGYzM;0tk3qB(+qL?;?D% zYf(u8Pzg94^g(vQCZGg&P%Fa2wIORgIYL*`j|f#E{ARTWoZj?in*Zcr=55<{_l@8| zLj8dxB{AfYh47g$(MeqVKJo}ewcQrr@YBn~)lgr6RHMh*9jCS)8)_3Avk-If_>P&} z`#m+I9rx4jFXk0NY9lG=A21#qYHxS-^O5O2o7 zkvKnX-|3sy!a#@AP`}T0Rtp>9cpF4ajE*XxUi@lgd{Nz9yoRVV54|B_sZ+bK(W0N6 zAvd`~D<3(9-_W!qY5-W)Zg8duzvv&18T?Hap|Ri`9K(?}-VHkos0sjNcID~0$FzIB z6Fl2mHSRkuV43l*u^a^st}bDye_hGcO{)jj)8CC%;*9{Dn%HxkuV?hGEwW?TdXC5G zfb&OiZ-d6dQ|Imx`K*Wglx?B>rS>z-c)QmlLqoK+k&+HeY|K>iISKTn8lQw1$SFr# zHERi1d`*E4In2ROg-)vwy(&M(-B~^&4!El0?bsbXv6UB#uHiSs^%!%V`&-SOtyko0 z31=OQg}}_H)pCD0N47+9Qt6w(f@sW_v^cG1RcI@(50+2qbQ47sZ8bHpFqOV(yvPM1 zfe5`x*n0Q!4<-o4r;&pr`(}xG9yzs&2ge_;T7in0tr+d`uVDtQHI~y+Ay^*R>GU_r zyzzbR;K)>@)#M&xxZF=_-e)hFnsLIpySxIvtQ3ADT_LsTi>m-%)oMml9tY!6T$ArA zqkRyO5WVL$RVGN#wJjZzU7EY_?KgD-BqaAt^1h^xvSTq~I6FOkqPY2uZ2jkv0fP|f zYzZr$U|q=$EOr@5SFq{-`34YFG7ue|`(M6KlwlUBF4ERHnwEe9Qf=*!?XLs@CBU|> zPp2fhd-3+vVr~$g>uPOs$!TuUzYPyq>?pa%*qK<+Cbf23#_Lu85FDT6(%KR3+x3&S z?X^R`sTd5%G|9*76+5nQ+O9Bw_*iXBmU`f`yyXbMf%FO?m9`mjj3n6I(O#9G!+hf+M0YcOSY-yf`ac^kL^w>vp%A#fTwI8R;Z^8yK(i%9Q_u8Ca>U`-BI$vMJTs2$7Pxa#Y3<7! zzfK0P)2QhN4ia!nD!o5>P}5>I(VL7S+%1ipFS{aQVl9&fpt-==J!t0JB?>PZ&`3Aw zZhIK-nmINg@bsmPQbH?gbGRk+5!9uUA2}q(rP-C0l}&*1#llF41KqLZ`Q#CCr4ijs zIfrV9PyVjTBdY!dvKOOI{{}&OL^D)&j6wQq7axNBI8%H5qtfV6NRD>2N~z+_YKTMj zWCczELfhPoL(r>hoa&(9rf|b;`r6w>#j=W z4%5f1K?#c+;pg16vg;XPd90y7J{f+twfacy$8A^Km<{C^BZWjgI!)?`^ec6~fScKO zEq*|e=KIUFcjo7Gpyekc*%FP&W9CO zN>dbBR^~UH2Sm_7Kb`eQky){hfQmd8?D6x_0IET!V-jN@QIrbXX@V0AL#%~~z6-aC zCwY37hS$WLb*B{4W>Af$)E_sSBqC`PY`qwW6Z^g6ka=on_alONZ^=40vd{s(vURQX z>LW8i+ir0JLK~op_Hpjoe zHoFY|k>U7fw&VYz&H#DBFvN*omRKB6&5%2a0kPaW`p;y@|HUl-yDn`DSGme>Tw33&0sq^66CMtmudeP)@+BjF#03fivXEn z(y!gc_@{t-KCt6ns__9@>wXIuM_OxkK|*m}M#aU~%6)Y}CPT4}vA)1=9$48m0hYN2 z@K*IQMF81|>nE64Kl3_Ub$TqudHS8N6OgvpMTYRrUd{Hxh6#ihO$!+E_3UC)UnwQN z+ljiC@y_@*`A!ZN5I*Vx3*N;6^2k({FRoDpkE-re{ zPtC1`0ci;!+d-5dW2r!6@OG9wMLvEfD%x3Vb(k9r+JMX97-SlkvkPFG#ho7^K`Mot z5foK^?IkQV$_PHr02Z|o)p&Cf4Z{>30y5TH^mgBeldd$np>sFwLe46pA(UssAieYr z1LA0ghbjPR0(q_~$5UC6hH0L(Qp~};vLDqL_IOZ8jG2iw0LnEx=JU1t3Y_P$cTye$ zVYZaCHlfZ{%51Gsu+KYEXAdR|G?aWfY`<*HloHi$4`Q{a(4N;L#$))%t7Z1fU_c=?N7$s&PMQE++T9fFAFb% zH0UZc6i6&bQv_Z@))XQ9QK2)LS`m$G?~nZ{2=3loqP2S2Xq| z*vjAS<`2iOO2>DX-se*b4r0cuzlWdAcphOD!V_4z6Gc`1)*EQx@4h+(WIYF7+kz4- za=R}I+BX9=SnTt1Inof2`mBC`%HWX7|Hqi~KZynmnbwKdV~@_9IS>3jgT>tB+NGKc HZt?#CCc$_0 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00003.png b/tests/ragger/snapshots/stax/test_eip712_filtering_empty_array/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..cfee3aec2b96566ef135772ac1ad4ce169166f31 GIT binary patch literal 5988 zcmeHL`#;-f`?pP5TZg_{t5%I}J*nfS>X0Jqv!SIJt;2#aMJvuq5JaD9n`cy`2dtEI zl#rBYiD(Y8)YQ~aB{9+@3)57xh$KQI65m^&|Ka=n{?Pk}HWU0>!tlVJj{@TSf8F^&-~OZbUp624SMFR#Cs+p|_lF#~>HUO5nzpG-=ucB94zI)7 z!{_9^hD%bdAi_!S*Ql+#cYNDm`dB)J#$;}&BvYxbDI~T} z-N+lEP`D}+tgf!oc#FrXE3MCT<`bR6`h}g};z!e5>x7y4d3kwNesjK`?k1hE!bY44 zV+%8Tnaq;>e0MACRB;0OAZzxs;rS=U&{mtWp@b)q_A45#QmG6W03S$NjQ#FALl14r zs_}bFF!(ITi`}qx_yblBBn>qW6j2w!?at9eBGEwncxcB$&RT)wnd$yW7ewtfpkWVJ z>St9zh8<5#`Dzc{?#gR3%+=^jPb=Bt;wW^$t}LDbCrwFAM_VnhgFwyr9wd#I>F)Ay zz~_n&Po8gSf?=z1%?mkHfy}RIoZN8H(<+0)9ZCqLX*1o8F#RcB+o3qH9+3v&5mQ~-D_o#KF#!~}$xaiu++M}vcd^V#a@;J_WMS?j?$c@g%FY?5%! zm57{6bv>px_EVf3JDnQ7kBYz@Q|&ElY^}Hed<`wpt&b%Bbz+x2iUH5)nw~W2JA7vd ze>3X%y)%c&(}cQ_=gF_`>X!rc3-j|^br(X+aPoV9?Hds(XBz6q^Zc*)07cm*1|p?; z3Qi&*<1QX4za1AMC^=dd?`c)Seswi4NOPs@3W)40jk--%{E`{}?+sClfG<6gr(=Bz__vneL}1ld=34q`%iD zFFaxsrX*Um5}!QPCXh|Nar<+Knsp_20wLS5gG1glDq$)VLt(eQZZT9du1-AIdvx<* zVI*$N2Omg=KubCkZ~gK7{gzRs{B-bSdpM)Z%bxXLA%2k=9&EK-E(c*QgSbediEvWH zzO$F+ukSk&>DJDvkMMhKZEZa)VMNofXHT5X>0qnB+}jT}4@6awZ8x;flZrdTzdsO3 z4@DbR-q0e`9L=tC8*XQjXD0rW80wqg( zUlT{Di+y33v9Hyv%@9@Qby2sy3gHqMPGo+Cr7u;j1p3nSfvI@4p?e~@Wg=3A0t z5)HmhdsD-+)0c4Awl9UwzllbuoFd}Pv))$+JKX)XjZIQRR3JfcG*)xFBfX)a0U=PY zn|EA);kJq+!S(w17hMQ9)5emzR46Uq@abBfbh2+M#l@oSGn1`Pm#)EL(T8U7c)U`E z)Do@M#T13QNvpK^ZAdmx2 z{D8$`W%aX}n|QH8zccjPRRe$TLw|4P#o}UzYgP_ZurZ>nFvaxqcrmK#Mw=dPyAbKW z()rGhDS%r4iPkh%LOVROsK~h@?+Cjf{ql{?e8|9*OwDtlv$Kg-Efa0L5`unt$-Oa= z2=5}`@ruRyob0EbP>auv3|gFlA1Bi&TxAv!^L~xJt8Sl8MS7Pv+LW2xiBQY+%eOna zraX-{!Sv5;9kPBrow^9XW(@A~GH4fT_6nL=BERtrs3#i3(P%W&q!bF5C)iv#>`h~X zQZ1Sk9(^z}I^31$Y7h%a1d{qB`+;?;V=cWbO`ICE$LfQM+f5w)=%4*8I5=2zc8plo zxTnIm6hQhO0PZ{wThd7?xDt(*)RkONnH@Ti;QUnaO4$Vf*e-ZcLpR+9una-Y-t#-r za;JidW-=q~IT9870t4&YBt_ETBz75Phdm$RVj*4CNRfqA$2380*?SH6AZ~H2xK20; zhr^kd`(Uywe4EroI)&oPu$(xfa;&Z4WF9R>eaMY{OO&ZZE0~IH(Ls#P*?% z_HH%V`tSeUdU3!s2Qb*+T!hn>sm&0*%={Zrnh9p2#KfSKBFLj~b7==+<6Y~#yuDXY zjD;KsZj6b_@*ZEP`oYWxMAOVQbwD_gJ!}jRolQog(Tm7IEqbGEc*xKT3eD44*S|Gu z12$j}D{5Z9{o+Tp`8z6eDDS0gH8^8V%vA49cZmdU%21Pe=@DL)2cIFx7Dj-0H`wzH_>uM)4d@a7 zYRdg|OYEQnsX0_+3?v2F9p7UV6i`^6Eue6j^l@jzjf)6WywT5p`V=VZr|UxJvxHzYC~KXs-+GhhNQ zqMD{xwlq7K&lx}u(Ffwf4Bczd%(WBj5**X30f*xrxd1Yxob-DHKBpcUfuYeML2i4D z96Qj%f+ZD-xAW2HzK27FQ`AF|^;1NPM=gw=^09Zfj1}Tn+c?-TV@|-l=|cSD-gg={ zqf?6(Ec5f#tK4Mgd#MyIs35fHScicEGr}$l04EDSMiAM_(}-1ZW{r3-!6)R=ltfYS zn3qm0FVcXrme2HuXjn^?H$!;c0n2iZ+d9J7Kn51l8Lz0W~x{LkZY@odTE;dXNi zY^1IQ;uXUbki26swEKKpsSH!?+`!z0xU>bqd|6QHve3y1WUc*<3R@)rK9=@5U|$!M z-iJ4u*B}{iioMhn*~B6oYFim5-zV!!&RuO`J(!3chnda9B0#;fZkmgs-A$pw+8wR3 z?(>t#2;7SEsNf#C3KIJ>u;pnCIei<)j{|I3&Q+UnW=iw;`|-_bGvBklPO#RJ5iFE? z^<=EHCdKeZAd3)AO8&kDB`6P;uaq{K9h2oQbRN#K%7& zbb-(vN_Dlf{u|J7(>N%|D8YemGi%C+qZiMQDkI~0DvAH+wwvh$KK zcK5HPtiOtqVnxQGjK|4s=}OOau5q}>Equ}5unUZu3@kr|EY@lcMfQ+lRAKJ*LBmHq z7#l%jM`IU#Lhepn8Wr66d@8 zFlzOMe^xaVsUj!uF7~<-K^byc9-B?lBDw&Sdr*xjcDJ);$)_hT6CMs>ldp~ zAmN3=U#28gm{UN(oVzX-6>u+D8=)9+X}n;6%LG6)2SFid0lKEoZ9L}-1QCYrkk|7% zT_0-bAt+>n{&B2Hcp6}Kb;e3yFdtY1gH+bPve5iY)laA1vc>@9i%bCvPp8+KCHy^*90%XBC9=yBQ$#ob-< zVA>k{@r_k}Gc_YuzO*tc5Q#76OsIu{l#=HL$qK%)>K3@5(x8q1-kpD}N129uEW>pg zm)ol6<3zx-Yv<7x1QKX!fR%xb))Wv+lli}fc|6`gVZC()vUiwR3zy?pR{*=0)B)ma zDYroK_Xl>+H~>DnfG0hVuXT5uZ*;#->P**GF3tb*_{J=)$s3&HW0v&;zu5;aydC&PhY?&hD0u^_^U`K>p%BT zEOfgH#5r5r_Ze|OPrw#_XXt1yzDJN zTc)tDo`|BZ-|nuKeR*T)?383Tn3RDn9|mb3W``dpssA5_$2#kv(i4{xTONQJx}M%? NpP=tL{uOuSe*iQ6;hz8i literal 0 HcmV?d00001 diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index 1f64fb159..830d2a5d3 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -437,3 +437,94 @@ def test_eip712_advanced_filtering(firmware: Firmware, # verify signature addr = recover_message(data_set.data, vrs) assert addr == get_wallet_addr(app_client) + + +def test_eip712_filtering_empty_array(firmware: Firmware, + backend: BackendInterface, + navigator: Navigator, + default_screenshot_path: Path, + test_name: str, + golden_run: bool): + global SNAPS_CONFIG + + app_client = EthAppClient(backend) + if firmware == Firmware.NANOS: + pytest.skip("Not supported on LNS") + + SNAPS_CONFIG = SnapshotsConfig(test_name) + + data = { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"}, + ], + "Person": [ + {"name": "name", "type": "string"}, + {"name": "addr", "type": "address"}, + ], + "Message": [ + {"name": "title", "type": "string"}, + {"name": "to", "type": "Person[]"}, + ], + "Root": [ + {"name": "text", "type": "string"}, + {"name": "subtext", "type": "string[]"}, + {"name": "msg_list1", "type": "Message[]"}, + {"name": "msg_list2", "type": "Message[]"}, + ], + }, + "primaryType": "Root", + "domain": { + "name": "test", + "version": "1", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "chainId": 1, + }, + "message": { + "text": "This is a test", + "subtext": [], + "msg_list1": [ + { + "title": "This is a test", + "to": [], + } + ], + "msg_list2": [], + } + } + filters = { + "name": "Empty array filtering", + "fields": { + "text": { + "type": "raw", + "name": "Text", + }, + "subtext.[]": { + "type": "raw", + "name": "Sub-Text", + }, + "msg_list1.[].to.[].addr": { + "type": "raw", + "name": "(1) Recipient addr", + }, + "msg_list2.[].to.[].addr": { + "type": "raw", + "name": "(2) Recipient addr", + }, + } + } + vrs = eip712_new_common(firmware, + navigator, + default_screenshot_path, + app_client, + data, + filters, + False, + golden_run) + + # verify signature + addr = recover_message(data, vrs) + assert addr == get_wallet_addr(app_client)