From b6818327e705f6b2be2440c19d0fc2cbea66b5ca Mon Sep 17 00:00:00 2001 From: 0xPxt Date: Thu, 8 Aug 2024 07:57:21 +0200 Subject: [PATCH] Parsing simple transaction --- CMakeLists.txt | 25 ++- app/Makefile | 4 +- app/src/coin.h | 5 + app/src/common/parser_common.h | 26 ++- app/src/jsmn/jsmn.c | 390 ++++++++++++++++++++++++++++++++ app/src/jsmn/jsmn.h | 105 +++++++++ app/src/json/json_parser.c | 294 ++++++++++++++++++++++++ app/src/json/json_parser.h | 114 ++++++++++ app/src/parser.c | 108 ++++++++- app/src/parser_impl.c | 65 +++++- app/src/parser_impl.h | 13 +- app/src/parser_txdef.h | 64 +++++- tests/json_parser.cpp | 398 +++++++++++++++++++++++++++++++++ tests/testcases.json | 85 ++----- tests/ui_tests.cpp | 7 +- tests/utils/common.h | 16 ++ 16 files changed, 1612 insertions(+), 107 deletions(-) create mode 100644 app/src/jsmn/jsmn.c create mode 100644 app/src/jsmn/jsmn.h create mode 100644 app/src/json/json_parser.c create mode 100644 app/src/json/json_parser.h create mode 100644 tests/json_parser.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cbb8b2..e719faf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,9 +128,14 @@ add_definitions( ##{TODO} ############################################################## # Static Libraries -file(GLOB_RECURSE LIB_SRC + +file(GLOB_RECURSE BLAKE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/deps/BLAKE2/ref/blake2b-ref.c + ) + +file(GLOB_RECURSE LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/src/app_mode.c + ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/src/base64.c ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/src/base58.c ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/src/bech32.c ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/src/bignum.c @@ -141,18 +146,28 @@ file(GLOB_RECURSE LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto_helper.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/json/json_parser.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/jsmn/jsmn.c ) -add_library(app_lib STATIC ${LIB_SRC}) +add_library(app_lib STATIC + ${LIB_SRC} + ${JSMN_SRC} + ${BLAKE_SRC} + ) target_include_directories(app_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/BLAKE2/ref ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/include ${CMAKE_CURRENT_SOURCE_DIR}/app/src - ${CMAKE_CURRENT_SOURCE_DIR}/app/src/lib ${CMAKE_CURRENT_SOURCE_DIR}/app/src/common + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/json + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/jsmn + ${CMAKE_CURRENT_SOURCE_DIR}/deps/ledger-zxlib/app/common ) +target_link_libraries(app_lib PUBLIC) + ############################################################## # Fuzz Targets if(ENABLE_FUZZING) @@ -177,6 +192,8 @@ else() ${gmock_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/app/src ${CMAKE_CURRENT_SOURCE_DIR}/app/src/lib + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/json + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/jsmn ) target_link_libraries(unittests PRIVATE @@ -188,4 +205,4 @@ else() add_compile_definitions(TESTVECTORS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/") add_test(NAME unittests COMMAND unittests) set_tests_properties(unittests PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) -endif() +endif() \ No newline at end of file diff --git a/app/Makefile b/app/Makefile index 5a8bc11..b9e6100 100755 --- a/app/Makefile +++ b/app/Makefile @@ -61,8 +61,8 @@ $(info COIN = [$(COIN)]) ifeq ($(COIN),KDA) # Main app configuration -APPNAME = "Kadena" -APPPATH = "44'/626'" +APPNAME = "Kadena-Led" +APPPATH = "44'/626'" --path "44'/1'" else define error_message diff --git a/app/src/coin.h b/app/src/coin.h index 19d2147..07ad602 100644 --- a/app/src/coin.h +++ b/app/src/coin.h @@ -52,6 +52,11 @@ extern "C" { #define APPVERSION_LINE1 "Kadena" #define APPVERSION_LINE2 "v" APPVERSION +typedef enum { + tx_json = 0, + tx_textual +} tx_type_e; + #ifdef __cplusplus } #endif diff --git a/app/src/common/parser_common.h b/app/src/common/parser_common.h index 6afacf3..8fcc3d5 100644 --- a/app/src/common/parser_common.h +++ b/app/src/common/parser_common.h @@ -22,8 +22,6 @@ extern "C" { #include #include -#include "parser_txdef.h" - #define CHECK_ERROR(__CALL) \ { \ parser_error_t __err = __CALL; \ @@ -56,15 +54,23 @@ typedef enum { parser_missing_field, paser_unknown_transaction, parser_tx_obj_empty, -} parser_error_t; -typedef struct { - const uint8_t *buffer; - uint16_t bufferLen; - uint16_t offset; - parser_tx_t *tx_obj; -} parser_context_t; + // Coin Specific + parser_json_zero_tokens, + parser_json_too_many_tokens, // "NOMEM: JSON string contains too many tokens" + parser_json_incomplete_json, // "JSON string is not complete"; + // TODO : Clean these if never used + //parser_json_contains_whitespace, + //parser_json_is_not_sorted, + //parser_json_missing_chain_id, + //parser_json_missing_sequence, + //parser_json_missing_fee, + //parser_json_missing_msgs, + //parser_json_missing_account_number, + //parser_json_missing_memo, + parser_json_unexpected_error, +} parser_error_t; #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/app/src/jsmn/jsmn.c b/app/src/jsmn/jsmn.c new file mode 100644 index 0000000..422139e --- /dev/null +++ b/app/src/jsmn/jsmn.c @@ -0,0 +1,390 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "jsmn.h" +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#ifdef __cplusplus +} +#endif diff --git a/app/src/jsmn/jsmn.h b/app/src/jsmn/jsmn.h new file mode 100644 index 0000000..44dd8a5 --- /dev/null +++ b/app/src/jsmn/jsmn.h @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/app/src/json/json_parser.c b/app/src/json/json_parser.c new file mode 100644 index 0000000..3c4d890 --- /dev/null +++ b/app/src/json/json_parser.c @@ -0,0 +1,294 @@ +#include "json_parser.h" +#include +#include "../common/parser_common.h" +#include "jsmn.h" + +#define EQUALS(_P, _Q, _LEN) (MEMCMP( (const void*) PIC(_P), (const void*) PIC(_Q), (_LEN))==0) + +/** + * Create JSON parser over an array of tokens + */ +extern void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +extern int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +parser_error_t array_get_element_count(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t *number_elements) { + *number_elements = 0; + if (array_token_index < 0 || array_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t array_token = json->tokens[array_token_index]; + uint16_t token_index = array_token_index; + uint16_t prev_element_end = array_token.start; + while (true) { + token_index++; + if (token_index >= json->numberOfTokens) { + break; + } + jsmntok_t current_token = json->tokens[token_index]; + if (current_token.start > array_token.end) { + break; + } + if (current_token.start <= prev_element_end) { + continue; + } + prev_element_end = current_token.end; + (*number_elements)++; + } + + return parser_ok; +} + +parser_error_t array_get_nth_element(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t element_index, + uint16_t *token_index) { + if (array_token_index < 0 || array_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t array_token = json->tokens[array_token_index]; + *token_index = array_token_index; + + uint16_t element_count = 0; + uint16_t prev_element_end = array_token.start; + while (true) { + (*token_index)++; + if (*token_index >= json->numberOfTokens) { + break; + } + jsmntok_t current_token = json->tokens[*token_index]; + if (current_token.start > array_token.end) { + break; + } + if (current_token.start <= prev_element_end) { + continue; + } + prev_element_end = current_token.end; + if (element_count == element_index) { + return parser_ok; + } + element_count++; + } + + return parser_no_data; +} + +parser_error_t object_get_element_count(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t *element_count) { + *element_count = 0; + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t object_token = json->tokens[object_token_index]; + uint16_t token_index = object_token_index; + uint16_t prev_element_end = object_token.start; + token_index++; + while (true) { + if (token_index >= json->numberOfTokens) { + break; + } + jsmntok_t key_token = json->tokens[token_index++]; + jsmntok_t value_token = json->tokens[token_index]; + if (key_token.start > object_token.end) { + break; + } + if (key_token.start <= prev_element_end) { + continue; + } + prev_element_end = value_token.end; + (*element_count)++; + } + + return parser_ok; +} + +parser_error_t object_get_nth_key(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *token_index) { + *token_index = object_token_index; + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + jsmntok_t object_token = json->tokens[object_token_index]; + uint16_t element_count = 0; + uint16_t prev_element_end = object_token.start; + (*token_index)++; + while (true) { + if (*token_index >= json->numberOfTokens) { + break; + } + jsmntok_t key_token = json->tokens[(*token_index)++]; + jsmntok_t value_token = json->tokens[*token_index]; + if (key_token.start > object_token.end) { + break; + } + if (key_token.start <= prev_element_end) { + continue; + } + prev_element_end = value_token.end; + if (element_count == object_element_index) { + (*token_index)--; + return parser_ok; + } + element_count++; + } + + return parser_no_data; +} + +parser_error_t object_get_nth_value(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *key_index) { + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + CHECK_ERROR(object_get_nth_key(json, object_token_index, object_element_index, key_index)) + (*key_index)++; + + return parser_ok; +} + +parser_error_t object_get_value(const parsed_json_t *json, + uint16_t object_token_index, + const char *key_name, + uint16_t *token_index) { + if (object_token_index < 0 || object_token_index > json->numberOfTokens) { + return parser_no_data; + } + + const jsmntok_t object_token = json->tokens[object_token_index]; + + *token_index = object_token_index; + int prev_element_end = object_token.start; + (*token_index)++; + + while (*token_index < json->numberOfTokens) { + const jsmntok_t key_token = json->tokens[*token_index]; + (*token_index)++; + const jsmntok_t value_token = json->tokens[*token_index]; + + if (key_token.start > object_token.end) { + break; + } + if (key_token.start <= prev_element_end) { + continue; + } + + if (value_token.type == JSMN_OBJECT) { + // An object was found, look inside it + parsed_json_t json_obj; + uint16_t token_index_before_recursion = *token_index; + + json_parse(&json_obj, json->buffer + value_token.start, value_token.end - value_token.start); + + if (object_get_value(&json_obj, 0, key_name, token_index) == parser_ok) { + *token_index = *token_index + token_index_before_recursion; + return parser_ok; + } + } else if (value_token.type == JSMN_ARRAY) { + // An array was found, look inside it + parsed_json_t json_array; + parsed_json_t json_array_element; + uint16_t token_index_before_object_recursion = 0; + uint16_t token_index_before_array_iteration = 0; + uint16_t element_count = 0; + + json_parse(&json_array, json->buffer + value_token.start, value_token.end - value_token.start); + + CHECK_ERROR(array_get_element_count(&json_array, 0, &element_count)) + + for (int i = 0; i < element_count; i++) { + CHECK_ERROR(array_get_nth_element(&json_array, 0, i, &token_index_before_array_iteration)) + + json_parse(&json_array_element, json_array.buffer + json_array.tokens[token_index_before_array_iteration].start, json_array.tokens[token_index_before_array_iteration].end - json_array.tokens[token_index_before_array_iteration].start); + + if (object_get_value(&json_array_element, 0, key_name, &token_index_before_object_recursion) == parser_ok) { + *token_index = *token_index + token_index_before_object_recursion + token_index_before_array_iteration; + return parser_ok; + } + } + } + + prev_element_end = value_token.end; + + if (((uint16_t) strlen(key_name)) == (key_token.end - key_token.start)) { + if (EQUALS(key_name, + json->buffer + key_token.start, + key_token.end - key_token.start)) { + return parser_ok; + } + } + } + + return parser_no_data; +} + +parser_error_t json_parse(parsed_json_t *parsed_json, const char *buffer, uint16_t bufferLen) { + jsmn_parser parser; + + jsmn_init(&parser); + + MEMZERO(parsed_json, sizeof(parsed_json_t)); + parsed_json->buffer = buffer; + parsed_json->bufferLen = bufferLen; + + int32_t num_tokens = jsmn_parse( + &parser, + parsed_json->buffer, + parsed_json->bufferLen, + parsed_json->tokens, + MAX_NUMBER_OF_TOKENS); + +#ifdef APP_TESTING + char tmpBuffer[100]; + snprintf(tmpBuffer, sizeof(tmpBuffer), "tokens: %d\n", num_tokens); + zemu_log(tmpBuffer); +#endif + + if (num_tokens < 0) { + switch (num_tokens) { + case JSMN_ERROR_NOMEM: + return parser_json_too_many_tokens; + case JSMN_ERROR_INVAL: + return parser_unexpected_characters; + case JSMN_ERROR_PART: + return parser_json_incomplete_json; + default: + return parser_json_unexpected_error; + } + } + + parsed_json->numberOfTokens = 0; + parsed_json->isValid = 0; + + // Parsing error + if (num_tokens <= 0) { + return parser_json_zero_tokens; + } + + // We cannot support if number of tokens exceeds the limit + if (num_tokens > MAX_NUMBER_OF_TOKENS) { + return parser_json_too_many_tokens; + } + + parsed_json->numberOfTokens = num_tokens; + parsed_json->isValid = true; + + return parser_ok; +} \ No newline at end of file diff --git a/app/src/json/json_parser.h b/app/src/json/json_parser.h new file mode 100644 index 0000000..2b93e48 --- /dev/null +++ b/app/src/json/json_parser.h @@ -0,0 +1,114 @@ +/******************************************************************************* + * (c) 2018 - 2024 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ +#pragma once + +#include "jsmn.h" +#include +#include +#include +#include "common/parser_common.h" + +/// Max number of accepted tokens in the JSON input +#define MAX_NUMBER_OF_TOKENS 768 + +// Limit depending on target +#if defined(TARGET_NANOS) || defined(TARGET_NANOX) +#undef MAX_NUMBER_OF_TOKENS +#define MAX_NUMBER_OF_TOKENS 10 // TODO : Check how many are actually needed, currently made to fit in memory. +#endif + +#if defined(TARGET_STAX) || defined(TARGET_FLEX) +#undef MAX_NUMBER_OF_TOKENS +#define MAX_NUMBER_OF_TOKENS 600 +#endif +// Context that keeps all the parsed data together. That includes: +// - parsed json tokens +// - re-created SendMsg struct with indices pointing to tokens in parsed json +typedef struct { + uint8_t isValid; + uint32_t numberOfTokens; + jsmntok_t tokens[MAX_NUMBER_OF_TOKENS]; + const char *buffer; + uint16_t bufferLen; +} parsed_json_t; + +/// Parse json to create a token representation +/// \param parsed_json +/// \param transaction +/// \param transaction_length +/// \return Error message +parser_error_t json_parse(parsed_json_t *parsed_json, const char *buffer, uint16_t bufferLen); + +/// Get the number of elements in the array +/// \param json +/// \param array_token_index +/// \param number of elements (out) +/// \return Error message +parser_error_t array_get_element_count(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t *number_elements); + +/// Get the token index of the nth array's element +/// \param json +/// \param array_token_index +/// \param element_index +/// \param token index +/// \return Error message +parser_error_t array_get_nth_element(const parsed_json_t *json, + uint16_t array_token_index, + uint16_t element_index, + uint16_t *token_index); + +/// Get the number of dictionary elements (key/value pairs) under given object +/// \param json +/// \param object_token_index: token index of the parent object +/// \param number of elements (out) +/// \return Error message +parser_error_t object_get_element_count(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t *number_elements); + +/// Get the token index for the nth dictionary key +/// \param json +/// \param object_token_index: token index of the parent object +/// \param object_element_index +/// \return token index (out) +/// \return Error message +parser_error_t object_get_nth_key(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *token_index); + +/// Get the token index for the nth dictionary value +/// \param json +/// \param object_token_index: token index of the parent object +/// \param object_element_index +/// \return token index (out)) +/// \return Error message +parser_error_t object_get_nth_value(const parsed_json_t *json, + uint16_t object_token_index, + uint16_t object_element_index, + uint16_t *token_index); + +/// Get the token index of the value that matches the given key +/// \param json +/// \param object_token_index: token index of the parent object +/// \param key_name: key name of the wanted value +/// \return Error message +parser_error_t object_get_value(const parsed_json_t *json, + uint16_t object_token_index, + const char *key_name, + uint16_t *token_index); \ No newline at end of file diff --git a/app/src/parser.c b/app/src/parser.c index b5feefd..8f48d93 100644 --- a/app/src/parser.c +++ b/app/src/parser.c @@ -20,20 +20,20 @@ #include #include #include +#include #include "coin.h" #include "crypto.h" #include "crypto_helper.h" -#include "parser_common.h" #include "parser_impl.h" parser_error_t parser_init_context(parser_context_t *ctx, const uint8_t *buffer, uint16_t bufferSize) { ctx->offset = 0; - ctx->buffer = NULL; - ctx->bufferLen = 0; if (bufferSize == 0 || buffer == NULL) { // Not available, use defaults + ctx->buffer = NULL; + ctx->bufferLen = 0; return parser_init_context_empty; } @@ -45,10 +45,16 @@ parser_error_t parser_init_context(parser_context_t *ctx, const uint8_t *buffer, parser_error_t parser_parse(parser_context_t *ctx, const uint8_t *data, size_t dataLen, parser_tx_t *tx_obj) { CHECK_ERROR(parser_init_context(ctx, data, dataLen)) ctx->tx_obj = tx_obj; - return _read(ctx, tx_obj); + + CHECK_ERROR(_read_json_tx(ctx, tx_obj)); + + return parser_ok; } parser_error_t parser_validate(parser_context_t *ctx) { + + // TODO: validate the tx (JSON validation) + // Iterate through all items to check that all can be shown and are valid uint8_t numItems = 0; CHECK_ERROR(parser_getNumItems(ctx, &numItems)) @@ -68,7 +74,7 @@ parser_error_t parser_getNumItems(const parser_context_t *ctx, uint8_t *num_item return parser_tx_obj_empty; } - *num_items = 3; + *num_items = 10; return parser_ok; } @@ -99,20 +105,98 @@ parser_error_t parser_getItem(const parser_context_t *ctx, uint8_t displayIdx, c switch (displayIdx) { case 0: - snprintf(outKey, outKeyLen, "Blind sign"); - snprintf(outVal, outValLen, "Plain text"); + snprintf(outKey, outKeyLen, "Signing"); + snprintf(outVal, outValLen, "Transaction"); return parser_ok; case 1: - snprintf(outKey, outKeyLen, "Blob"); - pageString(outVal, outValLen, (char *)ctx->buffer, pageIdx, pageCount); + snprintf(outKey, outKeyLen, "On Network"); + char net[9]; + uint16_t net_size; + CHECK_ERROR(parser_getJsonValueAsString("networkId", net, &net_size)); + snprintf(outVal, net_size + 1, "%s", net); return parser_ok; case 2: - snprintf(outKey, outKeyLen, "Hash"); - uint8_t hash[BLAKE2B_HASH_SIZE] = {0}; + snprintf(outKey, outKeyLen, "Requiring"); + snprintf(outVal, outValLen, "Capabilities"); + return parser_ok; + case 3: + snprintf(outKey, outKeyLen, "Of Key"); + char pubKey[64]; + uint16_t pubKey_size; + CHECK_ERROR(parser_getJsonValueAsString("sender", pubKey, &pubKey_size)); + snprintf(outVal, pubKey_size + 1, "%s", pubKey); + return parser_ok; + case 4: + snprintf(outKey, outKeyLen, "Paying Gas"); + snprintf(outVal, outValLen, ""); + return parser_ok; + case 5: + // TODO : Iterate over all the transfers + snprintf(outKey, outKeyLen, "Transfer 1"); + char to[65]; + char from[65]; + char amount[10]; + uint16_t to_size; + uint16_t from_size; + uint16_t amount_size; + CHECK_ERROR(parser_getTransactionParams(1, amount, &amount_size, from, &from_size, to, &to_size)); + + snprintf(outVal, amount_size + from_size + to_size + 15, "%s from \"%s\" to \"%s\"", amount, from, to); + + return parser_ok; + case 6: + snprintf(outKey, outKeyLen, "On Chain"); + char chain[2]; + uint16_t chain_size; + CHECK_ERROR(parser_getJsonValueAsString("chainId", chain, &chain_size)); + snprintf(outVal, chain_size + 1, "%s", chain); + return parser_ok; + case 7: + snprintf(outKey, outKeyLen, "Using Gas"); + char gasLimit[10]; + char gasPrice[10]; + uint16_t gasLimit_size; + uint16_t gasPrice_size; + + CHECK_ERROR(parser_getJsonValueAsString("gasLimit", gasLimit, &gasLimit_size)); + CHECK_ERROR(parser_getJsonValueAsString("gasPrice", gasPrice, &gasPrice_size)); + + snprintf(outVal, 8, "at most"); + snprintf(outVal + strlen(outVal), gasLimit_size + 2, " %s", gasLimit); + snprintf(outVal + strlen(outVal), 10, " at price"); + snprintf(outVal + strlen(outVal), gasPrice_size + 2, " %s", gasPrice); + return parser_ok; + case 8: + snprintf(outKey, outKeyLen, "Transaction hash"); + uint8_t hash[BLAKE2B_OUTPUT_LEN] = {0}; if (blake2b_hash((uint8_t *)ctx->buffer, ctx->bufferLen, hash) != zxerr_ok) { return parser_unexpected_error; } - pageStringHex(outVal, outValLen, (char *)hash, BLAKE2B_HASH_SIZE, pageIdx, pageCount); + + uint8_t base64_hash[44]; + base64_encode(base64_hash, 44, hash, sizeof(hash)); + + // Make it base64 URL safe + for (int i = 0; base64_hash[i] != '\0'; i++) { + if (base64_hash[i] == '+') { + base64_hash[i] = '-'; + } else if (base64_hash[i] == '/') { + base64_hash[i] = '_'; + } + } + + snprintf(outVal, sizeof(base64_hash), "%s", base64_hash); + return parser_ok; + case 9: + snprintf(outKey, outKeyLen, "Sign for Address"); + /* + Currently launching cpp tests, so this is not available + uint8_t address[32]; + uint16_t address_size; + CHECK_ERROR(crypto_fillAddress(address, sizeof(address), &address_size)); + snprintf(outVal, address_size + 1, "%s", address); + */ + return parser_ok; default: break; diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index 8f275e6..f5b2413 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -16,14 +16,65 @@ #include "parser_impl.h" -parser_error_t _read(parser_context_t *c, parser_tx_t *v) { - if (c == NULL || v == NULL) { - return parser_unexpected_error; - } +parser_tx_t parser_tx_obj; + +parser_error_t _read_json_tx(parser_context_t *c, __Z_UNUSED parser_tx_t *v) { + CHECK_ERROR(json_parse(&parser_tx_obj.tx_json.json, (const char *) c->buffer, + c->bufferLen)); + + parser_tx_obj.tx_json.tx = (const char *) c->buffer; + parser_tx_obj.tx_json.flags.cache_valid = 0; + parser_tx_obj.tx_json.filter_msg_type_count = 0; + parser_tx_obj.tx_json.filter_msg_from_count = 0; + + return parser_ok; +} + + +parser_error_t parser_getJsonValueAsString(const char *key_name, char *outVal, uint16_t *outValLen) { + uint16_t token_index = 0; + + // Search token_index to access the parsed JSON object + object_get_value(&parser_tx_obj.tx_json.json, 0, key_name, &token_index); + + *outValLen = (parser_tx_obj.tx_json.json.tokens[token_index].end - parser_tx_obj.tx_json.json.tokens[token_index].start); + strncpy(outVal, parser_tx_obj.tx_json.json.buffer + parser_tx_obj.tx_json.json.tokens[token_index].start, *outValLen); + + return parser_ok; +} + +parser_error_t parser_getTransactionParams(uint8_t tx_index, char *amount, uint16_t *amount_size, char *from, uint16_t *from_size, char *to, uint16_t *to_size) { + parsed_json_t json_clist; + parsed_json_t json_tx; + parsed_json_t json_args; + uint16_t token_index = 0; + + object_get_value(&parser_tx_obj.tx_json.json, 0, "clist", &token_index); + + json_parse(&json_clist, parser_tx_obj.tx_json.json.buffer + parser_tx_obj.tx_json.json.tokens[token_index].start, parser_tx_obj.tx_json.json.tokens[token_index].end - parser_tx_obj.tx_json.json.tokens[token_index].start); + + array_get_nth_element(&json_clist, 0, 0, &token_index); + + json_parse(&json_tx, json_clist.buffer + json_clist.tokens[token_index].start, json_clist.tokens[token_index].end - json_clist.tokens[token_index].start); + + object_get_value(&json_tx, 0, "args", &token_index); + + json_parse(&json_args, json_tx.buffer + json_tx.tokens[token_index].start, json_tx.tokens[token_index].end - json_tx.tokens[token_index].start); + + array_get_nth_element(&json_args, 0, 0, &token_index); + strncpy(from, json_args.buffer + json_args.tokens[token_index].start, json_args.tokens[token_index].end - json_args.tokens[token_index].start); + *from_size = json_args.tokens[token_index].end - json_args.tokens[token_index].start; + from[*from_size] = '\0'; + + array_get_nth_element(&json_args, 0, 1, &token_index); + strncpy(to, json_args.buffer + json_args.tokens[token_index].start, json_args.tokens[token_index].end - json_args.tokens[token_index].start); + *to_size = json_args.tokens[token_index].end - json_args.tokens[token_index].start; + to[*to_size] = '\0'; - // TODO: implement parser - v->generic_tx.len = c->bufferLen; - v->generic_tx.ptr = c->buffer; + array_get_nth_element(&json_args, 0, 2, &token_index); + strncpy(amount, json_args.buffer + json_args.tokens[token_index].start, json_args.tokens[token_index].end - json_args.tokens[token_index].start); + *amount_size = json_args.tokens[token_index].end - json_args.tokens[token_index].start; + amount[*amount_size] = '\0'; return parser_ok; } diff --git a/app/src/parser_impl.h b/app/src/parser_impl.h index 9c31332..62b7a40 100644 --- a/app/src/parser_impl.h +++ b/app/src/parser_impl.h @@ -25,8 +25,17 @@ extern "C" { #endif -// #{TODO} --> functions to parse, get, process transaction fields -parser_error_t _read(parser_context_t *c, parser_tx_t *v); + +typedef struct { + const uint8_t *buffer; + uint16_t bufferLen; + uint16_t offset; + parser_tx_t *tx_obj; +} parser_context_t; + +parser_error_t _read_json_tx(parser_context_t *c, parser_tx_t *v); +parser_error_t parser_getJsonValueAsString(const char *key_name, char *outVal, uint16_t *outValLen); +parser_error_t parser_getTransactionParams(uint8_t index, char *amount, uint16_t *amount_size, char *from, uint16_t *from_size, char *to, uint16_t *to_size); #ifdef __cplusplus } diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h index 94128e4..0bebeb7 100644 --- a/app/src/parser_txdef.h +++ b/app/src/parser_txdef.h @@ -19,16 +19,76 @@ extern "C" { #endif -#include #include +#include +#include +#include "coin.h" typedef struct { uint16_t len; const uint8_t *ptr; } Bytes_t; +typedef struct tx_textual_t{ + size_t n_containers; + uint8_t n_expert; + uint8_t tmpBuffer[625]; +} tx_textual_t; + +typedef struct { + // These are internal values used for tracking the state of the query/search + uint16_t _item_index_current; + + // maximum json tree level. Beyond this tree depth, key/values are flattened + uint8_t max_level; + + // maximum tree traversal depth. This limits possible stack overflow issues + uint8_t max_depth; + + // Index of the item to retrieve + int16_t item_index; + // Chunk of the item to retrieve (assuming partitioning based on out_val_len chunks) + int16_t page_index; + + // These fields (out_*) are where query results are placed + char *out_key; + uint16_t out_key_len; + char *out_val; + int16_t out_val_len; +} tx_query_t; + +typedef struct +{ + // Buffer to the original tx blob + const char *tx; + + // parsed data (tokens, etc.) + parsed_json_t json; + + // internal flags + struct { + bool cache_valid:1; + bool msg_type_grouping:1; // indicates if msg type grouping is enabled + bool msg_from_grouping:1; // indicates if msg from grouping is enabled + bool msg_from_grouping_hide_all:1; // indicates if msg from grouping should hide all + } flags; + + // indicates that N identical msg_type fields have been detected + uint8_t filter_msg_type_count; + int32_t filter_msg_type_valid_idx; + + // indicates that N identical msg_from fields have been detected + uint8_t filter_msg_from_count; + int32_t filter_msg_from_valid_idx; + const char *own_addr; + + // current tx query + tx_query_t query; +}tx_json_t; + typedef struct { - Bytes_t generic_tx; + // TODO : Remove this abstraction if no more fields are needed + tx_json_t tx_json; } parser_tx_t; #ifdef __cplusplus diff --git a/tests/json_parser.cpp b/tests/json_parser.cpp new file mode 100644 index 0000000..49bf87b --- /dev/null +++ b/tests/json_parser.cpp @@ -0,0 +1,398 @@ +/******************************************************************************* +* (c) 2024 Zondax GmbH +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "parser_impl.h" + +#include + +#include +#include + +#include "gmock/gmock.h" +#include "parser.h" +#include "parser_txdef.h" +#include "utils/common.h" + +namespace { + TEST(JsonParserTest, Empty) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, ""); + + EXPECT_FALSE(parserData.isValid); + EXPECT_EQ(0, parserData.numberOfTokens); + } + + TEST(JsonParserTest, SinglePrimitive) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "EMPTY"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(1, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, KeyValuePrimitives) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "KEY : VALUE"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(2, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, SingleString) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "\"EMPTY\""); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(1, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_STRING); + } + + TEST(JsonParserTest, KeyValueStrings) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, R"("KEY" : "VALUE")"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(2, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_STRING); + } + + TEST(JsonParserTest, SimpleArray) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "LIST : [1, 2, 3, 4]"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(6, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_ARRAY); + EXPECT_TRUE(parserData.tokens[2].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[3].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[4].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[5].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, MixedArray) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, R"(LIST : [1, "Text", 3, "Another text"])"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(6, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_ARRAY); + EXPECT_TRUE(parserData.tokens[2].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[3].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[4].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[5].type == jsmntype_t::JSMN_STRING); + } + + TEST(JsonParserTest, SimpleObject) { + parsed_json_t parserData = {false}; + JSON_PARSE(&parserData, "vote : " + "{ " + "\"key\" : \"value\", " + "\"another key\" : { " + "\"inner key\" : \"inner value\", " + "\"total\":123 }" + "}"); + + EXPECT_TRUE(parserData.isValid); + EXPECT_EQ(10, parserData.numberOfTokens); + EXPECT_TRUE(parserData.tokens[0].type == jsmntype_t::JSMN_PRIMITIVE); + EXPECT_TRUE(parserData.tokens[1].type == jsmntype_t::JSMN_OBJECT); + EXPECT_TRUE(parserData.tokens[2].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[3].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[4].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[5].type == jsmntype_t::JSMN_OBJECT); + EXPECT_TRUE(parserData.tokens[6].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[7].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[8].type == jsmntype_t::JSMN_STRING); + EXPECT_TRUE(parserData.tokens[9].type == jsmntype_t::JSMN_PRIMITIVE); + } + + TEST(JsonParserTest, ArrayElementCount_objects) { + auto transaction = + R"({"array":[{"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_ok); + EXPECT_EQ(token, 3) << "Wrong number of array elements"; + } + + TEST(JsonParserTest, ArrayElementCount_primitives) { + auto transaction = R"({"array":[1, 2, 3, 4, 5, 6, 7]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_ok); + EXPECT_EQ(token, 7) << "Wrong number of array elements"; + } + + TEST(JsonParserTest, ArrayElementCount_strings) { + auto transaction = R"({"array":["hello", "there"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_ok); + EXPECT_EQ(token, 2) << "Wrong number of array elements"; + } + + TEST(JsonParserTest, ArrayElementCount_empty) { + auto transaction = R"({"array":[])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token; + EXPECT_EQ(array_get_element_count(&parsed_json, 2, &token), parser_no_data); + } + + TEST(JsonParserTest, ArrayElementGet_objects) { + auto transaction = + R"({"array":[{"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}, {"amount":5,"denom":"photon"}]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 1, &token_index), parser_ok); + EXPECT_EQ(token_index, 8) << "Wrong token index returned"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_OBJECT) << "Wrong token type returned"; + } + + TEST(JsonParserTest, ArrayElementGet_primitives) { + auto transaction = R"({"array":[1, 2, 3, 4, 5, 6, 7]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 5, &token_index), parser_ok); + EXPECT_EQ(token_index, 8) << "Wrong token index returned"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_PRIMITIVE) << "Wrong token type returned"; + } + + TEST(TxValidationTest, ArrayElementGet_strings) { + auto transaction = R"({"array":["hello", "there"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 0, &token_index), parser_ok); + EXPECT_EQ(token_index, 3) << "Wrong token index returned"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_STRING) << "Wrong token type returned"; + } + + TEST(TxValidationTest, ArrayElementGet_empty) { + auto transaction = R"({"array":[])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 0, &token_index), parser_no_data) + << "Token index should be invalid (not found)."; + } + + TEST(TxValidationTest, ArrayElementGet_out_of_bounds_negative) { + auto transaction = R"({"array":["hello", "there"])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, -1, &token_index), parser_no_data) + << "Token index should be invalid (not found)."; + } + + TEST(TxValidationTest, ArrayElementGet_out_of_bounds) { + auto transaction = R"({"array":["hello", "there"])"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(array_get_nth_element(&parsed_json, 2, 3, &token_index), parser_no_data) + << "Token index should be invalid (not found)."; + } + + TEST(TxValidationTest, ObjectElementCount_primitives) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 3) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_string) { + auto transaction = R"({"age":"36", "height":"185", "year":"1981", "month":"july"})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 4) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_array) { + auto transaction = R"({ "ages":[36, 31, 10, 2], + "heights":[185, 164, 154, 132], + "years":[1981, 1985, 2008, 2016], + "months":["july", "august", "february", "july"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 4) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_object) { + auto transaction = R"({"person1":{"age":36, "height":185, "year":1981}, + "person2":{"age":36, "height":185, "year":1981}, + "person3":{"age":36, "height":185, "year":1981}})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 3) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementCount_deep) { + auto transaction = R"({"person1":{"age":{"age":36, "height":185, "year":1981}, "height":{"age":36, "height":185, "year":1981}, "year":1981}, + "person2":{"age":{"age":36, "height":185, "year":1981}, "height":{"age":36, "height":185, "year":1981}, "year":1981}, + "person3":{"age":{"age":36, "height":185, "year":1981}, "height":{"age":36, "height":185, "year":1981}, "year":1981}})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t count; + EXPECT_EQ(object_get_element_count(&parsed_json, 0, &count), parser_ok); + EXPECT_EQ(count, 3) << "Wrong number of object elements"; + } + + TEST(TxValidationTest, ObjectElementGet_primitives) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_key(&parsed_json, 0, 0, &token_index), parser_ok); + EXPECT_EQ(token_index, 1) << "Wrong token index"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_STRING) << "Wrong token type returned"; + EXPECT_EQ(memcmp(transaction + parsed_json.tokens[token_index].start, "age", strlen("age")), 0) + << "Wrong key returned"; + } + + TEST(TxValidationTest, ObjectElementGet_string) { + auto transaction = R"({"age":"36", "height":"185", "year":"1981", "month":"july"})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_value(&parsed_json, 0, 3, &token_index), parser_ok); + EXPECT_EQ(token_index, 8) << "Wrong token index"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_STRING) << "Wrong token type returned"; + EXPECT_EQ(memcmp(transaction + parsed_json.tokens[token_index].start, "july", strlen("july")), 0) + << "Wrong key returned"; + } + + TEST(TxValidationTest, ObjectElementGet_out_of_bounds_negative) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_key(&parsed_json, 0, -1, &token_index), parser_no_data) + << "Wrong token index, should be invalid"; + } + + TEST(TxValidationTest, ObjectElementGet_out_of_bounds) { + auto transaction = R"({"age":36, "height":185, "year":1981})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_nth_key(&parsed_json, 0, 5, &token_index), parser_no_data) + << "Wrong token index, should be invalid"; + } + + TEST(TxValidationTest, ObjectElementGet_array) { + auto transaction = R"({ "ages":[36, 31, 10, 2], + "heights":[185, 164, 154, 132], + "years":[1981, 1985, 2008, 2016, 2022], + "months":["july", "august", "february", "july"]})"; + + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_value(&parsed_json, 0, "years", &token_index), parser_ok); + + EXPECT_EQ(token_index, 14) << "Wrong token index"; + EXPECT_EQ(parsed_json.tokens[token_index].type, JSMN_ARRAY) << "Wrong token type returned"; + uint16_t number_elements; + EXPECT_EQ(array_get_element_count(&parsed_json, token_index, &number_elements), parser_ok); + EXPECT_EQ(number_elements, 5) << "Wrong number of array elements"; + } + + TEST(TxValidationTest, ObjectGetValueCorrectFormat) { + auto transaction = + R"({"account_number":"0","chain_id":"test-chain-1","fee":{"amount":[{"amount":"5","denom":"photon"}],"gas":"10000"},"memo":"testmemo","msgs":[{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"amount":"10","denom":"atom"}]}]}],"sequence":"1"})"; + parsed_json_t parsed_json; + JSON_PARSE(&parsed_json, transaction); + + uint16_t token_index; + EXPECT_EQ(object_get_value(&parsed_json, 0, "alt_bytes", &token_index), parser_no_data) + << "Wrong token index"; // alt_bytes should not be found + + EXPECT_EQ(object_get_value(&parsed_json, 0, "account_number", &token_index), parser_ok); + EXPECT_EQ(token_index, 2) << "Wrong token index"; // alt_bytes should not be found + + EXPECT_EQ(object_get_value(&parsed_json, 0, "chain_id", &token_index), parser_ok); + EXPECT_EQ(token_index, 4) << "Wrong token index"; + + EXPECT_EQ(object_get_value(&parsed_json, 0, "fee", &token_index), parser_ok); + EXPECT_EQ(token_index, 6) << "Wrong token index"; + + EXPECT_EQ(object_get_value(&parsed_json, 0, "msgs", &token_index), parser_ok); + EXPECT_EQ(token_index, 19) << "Wrong token index"; + + EXPECT_EQ(object_get_value(&parsed_json, 0, "sequence", &token_index), parser_ok); + EXPECT_EQ(token_index, 46) << "Wrong token index"; + } +} \ No newline at end of file diff --git a/tests/testcases.json b/tests/testcases.json index bc16c98..dfea211 100644 --- a/tests/testcases.json +++ b/tests/testcases.json @@ -2,73 +2,30 @@ { "index": 0, "name": "Asset_Freeze", - "blob": "8ba466616464c4204b2a4ad9d4d900ea16f9dcee534b9c0189daa1acbccace73d794bf168b8a73e3a466616964ce000b6717a3666565ce001e163fa26676ce000153c8a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce00018b66a46e6f7465c41c48656c6c6f2074686572652c20746869732069732061206e6f746521a572656b6579c4206a08f935de3bb9eb65b9d206598f5d34419257491c4024ca5f88ea73749de231a3736e64c4209fa4543b7caf05ad7334a5a74033acdc130137d7afa1f6733509f6d4377c30d7a474797065a46166727a", + "blob": "7B226E6574776F726B4964223A226D61696E6E65743031222C227061796C6F6164223A7B2265786563223A7B2264617461223A7B7D2C22636F6465223A2228636F696E2E7472616E73666572205C22383339333463306639623030356633373862613335323066396465613935326662306139306535616133366631623566663833376439623330633437313739305C22205C22393739306431313935383961323631313465316134326439323539386233663633323535316335363638313965633438653065386335346461653665626234325C222031312E3029227D7D2C227369676E657273223A5B7B227075624B6579223A2238333933346330663962303035663337386261333532306639646561393532666230613930653561613336663162356666383337643962333063343731373930222C22636C697374223A5B7B2261726773223A5B5D2C226E616D65223A22636F696E2E474153227D2C7B2261726773223A5B2238333933346330663962303035663337386261333532306639646561393532666230613930653561613336663162356666383337643962333063343731373930222C2239373930643131393538396132363131346531613432643932353938623366363332353531633536363831396563343865306538633534646165366562623432222C31315D2C226E616D65223A22636F696E2E5452414E53464552227D5D7D5D2C226D657461223A7B226372656174696F6E54696D65223A313633343030393231342C2274746C223A32383830302C226761734C696D6974223A3630302C22636861696E4964223A2230222C226761735072696365223A312E30652D352C2273656E646572223A2238333933346330663962303035663337386261333532306639646561393532666230613930653561613336663162356666383337643962333063343731373930227D2C226E6F6E6365223A225C22323032312D31302D31325430333A32373A35332E3730305A5C22227D", "output": [ - "0 | Txn type : Asset Freeze", - "1 | Sender [1/2] : T6SFIO34V4C224ZUUWTUAM5M3QJQCN6XV6Q7M4", - "1 | Sender [2/2] : ZVBH3NIN34GDLSK2ETKM", - "2 | Rekey to [1/2] : WARNING: NIEPSNO6HO46WZNZ2IDFTD25GRAZE", - "2 | Rekey to [2/2] : V2JDRACJSS7RDVHG5E54IYZOIA3MI", - "3 | Fee : ALGO 1.971775", - "4 | Genesis ID : mainnet-v1.0", - "5 | Genesis hash [1/2] : wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qz", - "5 | Genesis hash [2/2] : kkit8=", - "6 | Note : 28 bytes", - "7 | Asset ID : 747287", - "8 | Asset account [1/2] : JMVEVWOU3EAOUFXZ3TXFGS44AGE5VINMXTFM44", - "8 | Asset account [2/2] : 6XSS7RNC4KOPR5HR537U", - "9 | Freeze flag : Unfrozen" + "0 | Signing : Transaction", + "1 | On Network : mainnet01", + "2 | Requiring : Capabilities", + "3 | Of Key : 83934c0f9b005f378ba3520f9dea952fb0a90e5aa36f1b5ff837d9b30c471790", + "4 | Paying Gas : ", + "5 | Transfer 1 : 11 from \"83934c0f9b005f378ba3520f9dea952fb0a90e5aa36f1b5ff837d9b30c471790\" to \"9790d119589a26114e1a42d92598b3f632551c566819ec48e0e8c54dae6ebb42\"", + "6 | On Chain : 0", + "7 | Using Gas : at most 600 at price 1.0e-5", + "8 | Transaction hash : fPSCfMUaoK1N31qwhwBFUPwG-YR_guPP894uixsNZgk", + "9 | Sign for Address : 8d5d63bb1071a8dfc5c09ac96cfa50341a74eb91b6ea9ee5724cde09ef758bf2" ], "output_expert": [ - "0 | Txn type : Asset Freeze", - "1 | Sender [1/2] : T6SFIO34V4C224ZUUWTUAM5M3QJQCN6XV6Q7M4", - "1 | Sender [2/2] : ZVBH3NIN34GDLSK2ETKM", - "2 | Rekey to [1/2] : WARNING: NIEPSNO6HO46WZNZ2IDFTD25GRAZE", - "2 | Rekey to [2/2] : V2JDRACJSS7RDVHG5E54IYZOIA3MI", - "3 | Fee : ALGO 1.971775", - "4 | Genesis ID : mainnet-v1.0", - "5 | Genesis hash [1/2] : wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qz", - "5 | Genesis hash [2/2] : kkit8=", - "6 | Note : 28 bytes", - "7 | Asset ID : 747287", - "8 | Asset account [1/2] : JMVEVWOU3EAOUFXZ3TXFGS44AGE5VINMXTFM44", - "8 | Asset account [2/2] : 6XSS7RNC4KOPR5HR537U", - "9 | Freeze flag : Unfrozen" - ] - }, - { - "index": 1, - "name": "Asset_Freeze", - "blob": "8aa466616464c4204b2a4ad9d4d900ea16f9dcee534b9c0189daa1acbccace73d794bf168b8a73e3a466616964ce00038ce6a3666565ce00272460a26676cd6584a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce0001cd3ca572656b6579c42075895bccedfa0c747cea13601babff1de5cecf4dd7a3fe86eaeccb74b60124dea3736e64c420aba83eaca22287f68cad3906661fc72b1c3537dbbae4ba2c6e35a51c03c87f80a474797065a46166727a", - "output": [ - "0 | Txn type : Asset Freeze", - "1 | Sender [1/2] : VOUD5LFCEKD7NDFNHEDGMH6HFMODKN63XLSLUL", - "1 | Sender [2/2] : DOGWSRYA6IP6ADQBWT6Y", - "2 | Rekey to [1/2] : WARNING: OWEVXTHN7IGHI7HKCNQBXK77DXS45", - "2 | Rekey to [2/2] : T2N26R75BXK5TFXJNQBETPAS7VK74", - "3 | Fee : ALGO 2.565216", - "4 | Genesis ID : mainnet-v1.0", - "5 | Genesis hash [1/2] : wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qz", - "5 | Genesis hash [2/2] : kkit8=", - "6 | Asset ID : 232678", - "7 | Asset account [1/2] : JMVEVWOU3EAOUFXZ3TXFGS44AGE5VINMXTFM44", - "7 | Asset account [2/2] : 6XSS7RNC4KOPR5HR537U", - "8 | Freeze flag : Unfrozen" - ], - "output_expert": [ - "0 | Txn type : Asset Freeze", - "1 | Sender [1/2] : VOUD5LFCEKD7NDFNHEDGMH6HFMODKN63XLSLUL", - "1 | Sender [2/2] : DOGWSRYA6IP6ADQBWT6Y", - "2 | Rekey to [1/2] : WARNING: OWEVXTHN7IGHI7HKCNQBXK77DXS45", - "2 | Rekey to [2/2] : T2N26R75BXK5TFXJNQBETPAS7VK74", - "3 | Fee : ALGO 2.565216", - "4 | Genesis ID : mainnet-v1.0", - "5 | Genesis hash [1/2] : wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qz", - "5 | Genesis hash [2/2] : kkit8=", - "6 | Asset ID : 232678", - "7 | Asset account [1/2] : JMVEVWOU3EAOUFXZ3TXFGS44AGE5VINMXTFM44", - "7 | Asset account [2/2] : 6XSS7RNC4KOPR5HR537U", - "8 | Freeze flag : Unfrozen" + "0 | Signing : Transaction", + "1 | On Network : mainnet01", + "2 | Requiring : Capabilities", + "3 | Of Key : 83934c0f9b005f378ba3520f9dea952fb0a90e5aa36f1b5ff837d9b30c471790", + "4 | Paying Gas : ", + "5 | Transfer 1 : 11 from \"83934c0f9b005f378ba3520f9dea952fb0a90e5aa36f1b5ff837d9b30c471790\" to \"9790d119589a26114e1a42d92598b3f632551c566819ec48e0e8c54dae6ebb42\"", + "6 | On Chain : 0", + "7 | Using Gas : at most 600 at price 1.0e-5", + "8 | Transaction hash : fPSCfMUaoK1N31qwhwBFUPwG-YR_guPP894uixsNZgk", + "9 | Sign for Address : 8d5d63bb1071a8dfc5c09ac96cfa50341a74eb91b6ea9ee5724cde09ef758bf2" ] } ] \ No newline at end of file diff --git a/tests/ui_tests.cpp b/tests/ui_tests.cpp index 1c76533..b2066b5 100644 --- a/tests/ui_tests.cpp +++ b/tests/ui_tests.cpp @@ -111,19 +111,18 @@ void check_testcase(const testcase_t &tc, bool expert_mode) { std::vector expected = app_mode_expert() ? tc.expected_expert : tc.expected; -// #{TODO} --> After updating testvector, enable this part -#if 0 EXPECT_EQ(output.size(), expected.size()); for (size_t i = 0; i < expected.size(); i++) { if (i < output.size()) { EXPECT_THAT(output[i], testing::Eq(expected[i])); } } -#endif } INSTANTIATE_TEST_SUITE_P (JsonTestCasesCurrentTxVer, JsonTestsA, ::testing::ValuesIn(GetJsonTestCases("testcases.json")), JsonTestsA::PrintToStringParamName()); -TEST_P(JsonTestsA, CheckUIOutput_CurrentTX_Expert) { check_testcase(GetParam(), true); } + +//TEST_P(JsonTestsA, CheckUIOutput_CurrentTX_Expert) { check_testcase(GetParam(), true); } +TEST_P(JsonTestsA, CheckUIOutput_CurrentTX) { check_testcase(GetParam(), false); } \ No newline at end of file diff --git a/tests/utils/common.h b/tests/utils/common.h index 7bfc863..78ae772 100644 --- a/tests/utils/common.h +++ b/tests/utils/common.h @@ -13,9 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ +#pragma once + #include #include +#include "parser_impl.h" #include "parser_common.h" +#include +#include + +#define EXPECT_EQ_STR(_STR1, _STR2, _ERROR_MESSAGE) { if ((_STR1) != nullptr & (_STR2) != nullptr) \ +EXPECT_TRUE(!strcmp(_STR1, _STR2)) << (_ERROR_MESSAGE) << ", expected: " << (_STR2) << ", received: " << (_STR1); \ +else FAIL() << "One of the strings is null"; } + std::vector dumpUI(parser_context_t *ctx, uint16_t maxKeyLen, uint16_t maxValueLen); + +parser_error_t parse_tx(parsed_json_t *parsed_json, const char *tx); + +std::vector dumpUI(parser_context_t *ctx, uint16_t maxKeyLen, uint16_t maxValueLen); + +#define JSON_PARSE(parsed_json, buffer) json_parse(parsed_json, buffer, strlen(buffer)) \ No newline at end of file