diff --git a/external/eosjs b/external/eosjs index 11fbd89..2e92be8 160000 --- a/external/eosjs +++ b/external/eosjs @@ -1 +1 @@ -Subproject commit 11fbd89d1fe78eb6807b89e1da38b4dc02bfb0f5 +Subproject commit 2e92be8936fc751332135ed54a18e29c6b6c691a diff --git a/src/abieos.hpp b/src/abieos.hpp index 5a3dca5..8a3ec45 100644 --- a/src/abieos.hpp +++ b/src/abieos.hpp @@ -98,6 +98,8 @@ struct member_ptr { // Pseudo objects never exist, except in serialized form struct pseudo_optional; struct pseudo_extension; +struct pseudo_sized; +struct pseudo_fixed_sized; struct pseudo_object; struct pseudo_array; struct pseudo_variant; @@ -319,6 +321,8 @@ struct jvalue_to_bin_stack_entry { bool allow_extensions = false; const jvalue* value = nullptr; int position = -1; + size_t size_insertion_index = 0; + size_t byte_position = 0; }; struct json_to_bin_stack_entry { @@ -326,6 +330,7 @@ struct json_to_bin_stack_entry { bool allow_extensions = false; int position = -1; size_t size_insertion_index = 0; + size_t byte_position = 0; size_t variant_type_index = 0; }; @@ -334,6 +339,7 @@ struct bin_to_json_stack_entry { bool allow_extensions = false; int position = -1; uint32_t array_size = 0; + input_buffer saved_bin = {}; }; struct json_to_jvalue_state : json_reader_handler { @@ -358,8 +364,9 @@ struct bin_to_native_state { struct jvalue_to_bin_state { std::string& error; - std::vector& bin; + std::vector bin; const jvalue* received_value = nullptr; + std::vector size_insertions{}; std::vector stack{}; bool skipped_extension = false; @@ -470,6 +477,10 @@ ABIEOS_NODISCARD bool json_to_bin(pseudo_optional*, jvalue_to_bin_state& state, const abi_type* type, event_type event, bool); ABIEOS_NODISCARD bool json_to_bin(pseudo_extension*, jvalue_to_bin_state& state, bool allow_extensions, const abi_type* type, event_type event, bool); +ABIEOS_NODISCARD bool json_to_bin(pseudo_sized*, jvalue_to_bin_state& state, bool allow_extensions, + const abi_type* type, event_type event, bool); +ABIEOS_NODISCARD bool json_to_bin(pseudo_fixed_sized*, jvalue_to_bin_state& state, bool allow_extensions, + const abi_type* type, event_type event, bool); ABIEOS_NODISCARD bool json_to_bin(pseudo_object*, jvalue_to_bin_state& state, bool allow_extensions, const abi_type* type, event_type event, bool start); ABIEOS_NODISCARD bool json_to_bin(pseudo_array*, jvalue_to_bin_state& state, bool allow_extensions, @@ -491,6 +502,10 @@ ABIEOS_NODISCARD bool json_to_bin(pseudo_optional*, json_to_bin_state& state, bo const abi_type* type, event_type event, bool start); ABIEOS_NODISCARD bool json_to_bin(pseudo_extension*, json_to_bin_state& state, bool allow_extensions, const abi_type* type, event_type event, bool start); +ABIEOS_NODISCARD bool json_to_bin(pseudo_sized*, json_to_bin_state& state, bool allow_extensions, const abi_type* type, + event_type event, bool start); +ABIEOS_NODISCARD bool json_to_bin(pseudo_fixed_sized*, json_to_bin_state& state, bool allow_extensions, + const abi_type* type, event_type event, bool start); ABIEOS_NODISCARD bool json_to_bin(pseudo_object*, json_to_bin_state& state, bool allow_extensions, const abi_type* type, event_type event, bool start); ABIEOS_NODISCARD bool json_to_bin(pseudo_array*, json_to_bin_state& state, bool allow_extensions, const abi_type* type, @@ -507,6 +522,10 @@ ABIEOS_NODISCARD bool bin_to_json(pseudo_optional*, bin_to_json_state& state, bo const abi_type* type, bool start); ABIEOS_NODISCARD bool bin_to_json(pseudo_extension*, bin_to_json_state& state, bool allow_extensions, const abi_type* type, bool start); +ABIEOS_NODISCARD bool bin_to_json(pseudo_sized*, bin_to_json_state& state, bool allow_extensions, const abi_type* type, + bool start); +ABIEOS_NODISCARD bool bin_to_json(pseudo_fixed_sized*, bin_to_json_state& state, bool allow_extensions, + const abi_type* type, bool start); ABIEOS_NODISCARD bool bin_to_json(pseudo_object*, bin_to_json_state& state, bool allow_extensions, const abi_type* type, bool start); ABIEOS_NODISCARD bool bin_to_json(pseudo_array*, bin_to_json_state& state, bool allow_extensions, const abi_type* type, @@ -1074,6 +1093,16 @@ inline void push_varuint32(std::vector& bin, uint32_t v) { } while (val); } +inline uint32_t varuint32_size(uint32_t v) { + uint32_t size = 0; + uint64_t val = v; + do { + val >>= 7; + ++size; + } while (val); + return size; +} + ABIEOS_NODISCARD inline bool read_varuint32(input_buffer& bin, std::string& error, uint32_t& dest) { dest = 0; int shift = 0; @@ -2286,8 +2315,11 @@ struct abi_type { abi_type* alias_of{}; abi_type* optional_of{}; abi_type* extension_of{}; + abi_type* sized_of{}; + abi_type* fixed_sized_of{}; abi_type* array_of{}; abi_type* base{}; + uint32_t fixed_size{}; std::vector fields{}; bool filled_struct{}; bool filled_variant{}; @@ -2312,6 +2344,32 @@ bool ends_with(const std::string& s, const char (&suffix)[i]) { return s.size() >= i - 1 && !strcmp(s.c_str() + s.size() - (i - 1), suffix); } +struct size_suffix { + uint32_t value = 0; + size_t suffix_length = 0; +}; + +ABIEOS_NODISCARD inline std::optional get_size_suffix(const std::string& type) { + size_suffix result; + uint64_t multiplier = 1; + while (result.suffix_length < type.size()) { + auto ch = type[type.size() - 1 - result.suffix_length++]; + if (ch == '#') + break; + if (ch < '0' || ch > '9') + return {}; + uint32_t v = result.value + (ch - '0') * multiplier; + multiplier *= 10; + if (v < result.value) + return {}; + result.value = v; + } + if (result.suffix_length >= 2) + return result; + else + return {}; +} + ABIEOS_NODISCARD inline bool get_type(abi_type*& result, std::string& error, std::map& abi_types, const std::string& name, int depth) { if (depth >= 32) @@ -2352,6 +2410,26 @@ ABIEOS_NODISCARD inline bool get_type(abi_type*& result, std::string& error, std type.ser = &abi_serializer_for; result = &type; return true; + } else if (ends_with(name, "#")) { + abi_type& type = abi_types[name]; + type.name = name; + if (!get_type(type.sized_of, error, abi_types, name.substr(0, name.size() - 1), depth + 1)) + return false; + type.ser = &abi_serializer_for; + result = &type; + return true; + } else if (auto suffix = get_size_suffix(name)) { + abi_type& type = abi_types[name]; + type.name = name; + type.fixed_size = suffix->value; + if (type.fixed_size < 1 || type.fixed_size > 1024) + return set_error(error, "size suffix (#) is out of range 1-1024"); + if (!get_type(type.fixed_sized_of, error, abi_types, name.substr(0, name.size() - suffix->suffix_length), + depth + 1)) + return false; + type.ser = &abi_serializer_for; + result = &type; + return true; } else return set_error(error, "unknown type \"" + name + "\""); } @@ -2487,6 +2565,17 @@ ABIEOS_NODISCARD inline bool fill_contract(contract& c, std::string& error, cons // json_to_bin (jvalue) /////////////////////////////////////////////////////////////////////////////// +inline void insert_sizes(std::vector& dest, const char* src, const char* src_end, const size_insertion* sizes, + const size_insertion* sizes_end, size_t size_offset = 0) { + size_t pos = 0; + for (auto size = sizes; size != sizes_end; ++size) { + dest.insert(dest.end(), src + pos, src + (size->position + size_offset)); + push_varuint32(dest, size->size); + pos = size->position; + } + dest.insert(dest.end(), src + pos, src_end); +} + ABIEOS_NODISCARD inline bool json_to_bin(std::vector& bin, std::string& error, const abi_type* type, const jvalue& value) { jvalue_to_bin_state state{error, bin, &value}; @@ -2501,29 +2590,33 @@ ABIEOS_NODISCARD inline bool json_to_bin(std::vector& bin, std::string& er } return true; }(); - if (result) + if (result) { + insert_sizes(bin, state.bin.data(), state.bin.data() + state.bin.size(), state.size_insertions.data(), + state.size_insertions.data() + state.size_insertions.size()); return true; - std::string s; - if (!state.stack.empty() && state.stack[0].type->filled_struct) - s += state.stack[0].type->name; - for (auto& entry : state.stack) { - if (entry.type->array_of) - s += "[" + std::to_string(entry.position) + "]"; - else if (entry.type->filled_struct) { - if (entry.position >= 0 && entry.position < (int)entry.type->fields.size()) - s += "." + entry.type->fields[entry.position].name; - } else if (entry.type->optional_of) { - s += ""; - } else if (entry.type->filled_variant) { - s += ""; - } else { - s += ""; + } else { + std::string s; + if (!state.stack.empty() && state.stack[0].type->filled_struct) + s += state.stack[0].type->name; + for (auto& entry : state.stack) { + if (entry.type->array_of) + s += "[" + std::to_string(entry.position) + "]"; + else if (entry.type->filled_struct) { + if (entry.position >= 0 && entry.position < (int)entry.type->fields.size()) + s += "." + entry.type->fields[entry.position].name; + } else if (entry.type->optional_of) { + s += ""; + } else if (entry.type->filled_variant) { + s += ""; + } else { + s += ""; + } } + if (!s.empty()) + s += ": "; + error = s + error; + return false; } - if (!s.empty()) - s += ": "; - error = s + error; - return false; } ABIEOS_NODISCARD inline bool json_to_bin(pseudo_optional*, jvalue_to_bin_state& state, bool allow_extensions, @@ -2543,6 +2636,55 @@ ABIEOS_NODISCARD inline bool json_to_bin(pseudo_extension*, jvalue_to_bin_state& type->extension_of->ser->json_to_bin(state, allow_extensions, type->extension_of, event, true); } +ABIEOS_NODISCARD inline bool json_to_bin(pseudo_sized*, jvalue_to_bin_state& state, bool, const abi_type* type, + event_type event, bool start) { + if (start) { + if (trace_jvalue_to_bin) + printf("%*s#{\n", int(state.stack.size() * 4), ""); + state.stack.push_back({type, true, state.received_value, -1, state.size_insertions.size()}); + state.size_insertions.push_back({state.bin.size()}); + return type->sized_of->ser && type->sized_of->ser->json_to_bin(state, true, type->sized_of, event, true); + } + auto& stack_entry = state.stack.back(); + auto& insertion = state.size_insertions[stack_entry.size_insertion_index]; + size_t size = state.bin.size() - insertion.position; + for (auto i = stack_entry.size_insertion_index + 1; i < state.size_insertions.size(); ++i) + size += varuint32_size(state.size_insertions[i].size); + if (size > 0xffff'ffffull) + return set_error(state, "sized data is too large"); + insertion.size = size; + if (trace_jvalue_to_bin) + printf("%*s#} size=%u\n", int((state.stack.size() - 1) * 4), "", unsigned(insertion.size)); + state.stack.pop_back(); + return true; +} + +ABIEOS_NODISCARD inline bool json_to_bin(pseudo_fixed_sized*, jvalue_to_bin_state& state, bool, const abi_type* type, + event_type event, bool start) { + if (start) { + if (trace_jvalue_to_bin) + printf("%*s##{\n", int(state.stack.size() * 4), ""); + state.stack.push_back({type, true, state.received_value, -1, state.size_insertions.size(), state.bin.size()}); + return type->fixed_sized_of->ser && + type->fixed_sized_of->ser->json_to_bin(state, true, type->fixed_sized_of, event, true); + } + auto& stack_entry = state.stack.back(); + std::vector bin; + insert_sizes(bin, state.bin.data() + stack_entry.byte_position, state.bin.data() + state.bin.size(), + state.size_insertions.data() + stack_entry.size_insertion_index, + state.size_insertions.data() + state.size_insertions.size(), -stack_entry.byte_position); + state.bin.resize(stack_entry.byte_position); + state.size_insertions.resize(stack_entry.size_insertion_index); + if (bin.size() > type->fixed_size) + return set_error(state, "data exceeds fixed-size limit (#)"); + bin.resize(type->fixed_size); + state.bin.insert(state.bin.end(), bin.begin(), bin.end()); + if (trace_jvalue_to_bin) + printf("%*s##}\n", int((state.stack.size() - 1) * 4), ""); + state.stack.pop_back(); + return true; +} + ABIEOS_NODISCARD inline bool json_to_bin(pseudo_object*, jvalue_to_bin_state& state, bool allow_extensions, const abi_type* type, event_type event, bool start) { if (start) { @@ -2686,7 +2828,15 @@ ABIEOS_NODISCARD inline bool receive_event(struct json_to_bin_state& state, even state.stack.clear(); if (state.stack.size() > max_stack_size) return set_error(state, "recursion limit reached"); - return type->ser && type->ser->json_to_bin(state, entry.allow_extensions, type, event, start); + if (!type->ser || !type->ser->json_to_bin(state, entry.allow_extensions, type, event, start)) + return false; + while (!state.stack.empty() && (state.stack.back().type->sized_of || state.stack.back().type->fixed_sized_of)) { + auto entry = state.stack.back(); + auto* type = entry.type; + if (!type->ser || !type->ser->json_to_bin(state, entry.allow_extensions, type, event, false)) + return false; + } + return true; } ABIEOS_NODISCARD inline bool json_to_bin(std::vector& bin, std::string& error, const abi_type* type, @@ -2725,13 +2875,8 @@ ABIEOS_NODISCARD inline bool json_to_bin(std::vector& bin, std::string& er return false; } - size_t pos = 0; - for (auto& insertion : state.size_insertions) { - bin.insert(bin.end(), state.bin.begin() + pos, state.bin.begin() + insertion.position); - push_varuint32(bin, insertion.size); - pos = insertion.position; - } - bin.insert(bin.end(), state.bin.begin() + pos, state.bin.end()); + insert_sizes(bin, state.bin.data(), state.bin.data() + state.bin.size(), state.size_insertions.data(), + state.size_insertions.data() + state.size_insertions.size()); return true; } @@ -2752,6 +2897,55 @@ ABIEOS_NODISCARD inline bool json_to_bin(pseudo_extension*, json_to_bin_state& s type->extension_of->ser->json_to_bin(state, allow_extensions, type->extension_of, event, true); } +ABIEOS_NODISCARD inline bool json_to_bin(pseudo_sized*, json_to_bin_state& state, bool, const abi_type* type, + event_type event, bool start) { + if (start) { + if (trace_json_to_bin) + printf("%*s#{\n", int(state.stack.size() * 4), ""); + state.stack.push_back({type, true, -1, state.size_insertions.size()}); + state.size_insertions.push_back({state.bin.size()}); + return type->sized_of->ser && type->sized_of->ser->json_to_bin(state, true, type->sized_of, event, true); + } + auto& stack_entry = state.stack.back(); + auto& insertion = state.size_insertions[stack_entry.size_insertion_index]; + size_t size = state.bin.size() - insertion.position; + for (auto i = stack_entry.size_insertion_index + 1; i < state.size_insertions.size(); ++i) + size += varuint32_size(state.size_insertions[i].size); + if (size > 0xffff'ffffull) + return set_error(state, "sized data is too large"); + insertion.size = size; + if (trace_json_to_bin) + printf("%*s#} size=%u\n", int((state.stack.size() - 1) * 4), "", unsigned(insertion.size)); + state.stack.pop_back(); + return true; +} + +ABIEOS_NODISCARD inline bool json_to_bin(pseudo_fixed_sized*, json_to_bin_state& state, bool, const abi_type* type, + event_type event, bool start) { + if (start) { + if (trace_json_to_bin) + printf("%*s##{\n", int(state.stack.size() * 4), ""); + state.stack.push_back({type, true, -1, state.size_insertions.size(), state.bin.size()}); + return type->fixed_sized_of->ser && + type->fixed_sized_of->ser->json_to_bin(state, true, type->fixed_sized_of, event, true); + } + auto& stack_entry = state.stack.back(); + std::vector bin; + insert_sizes(bin, state.bin.data() + stack_entry.byte_position, state.bin.data() + state.bin.size(), + state.size_insertions.data() + stack_entry.size_insertion_index, + state.size_insertions.data() + state.size_insertions.size(), -stack_entry.byte_position); + state.bin.resize(stack_entry.byte_position); + state.size_insertions.resize(stack_entry.size_insertion_index); + if (bin.size() > type->fixed_size) + return set_error(state, "data exceeds fixed-size limit (#)"); + bin.resize(type->fixed_size); + state.bin.insert(state.bin.end(), bin.begin(), bin.end()); + if (trace_json_to_bin) + printf("%*s##}\n", int((state.stack.size() - 1) * 4), ""); + state.stack.pop_back(); + return true; +} + ABIEOS_NODISCARD inline bool json_to_bin(pseudo_object*, json_to_bin_state& state, bool allow_extensions, const abi_type* type, event_type event, bool start) { if (start) { @@ -2934,6 +3128,52 @@ ABIEOS_NODISCARD inline bool bin_to_json(pseudo_extension*, bin_to_json_state& s type->extension_of->ser->bin_to_json(state, allow_extensions, type->extension_of, true); } +ABIEOS_NODISCARD inline bool bin_to_json(pseudo_sized*, bin_to_json_state& state, bool allow_extensions, + const abi_type* type, bool start) { + if (start) { + if (trace_bin_to_json) + printf("%*s#{\n", int(state.stack.size() * 4), ""); + uint32_t size; + if (!read_varuint32(state.bin, state.error, size)) + return false; + auto content_begin = state.bin.pos; + if (!skip_raw(state.bin, state.error, size)) + return false; + auto content_end = state.bin.pos; + state.stack.push_back({type, true}); + state.stack.back().saved_bin = state.bin; + state.bin = {content_begin, content_end}; + return type->sized_of->ser && type->sized_of->ser->bin_to_json(state, true, type->sized_of, true); + } + state.bin = state.stack.back().saved_bin; + state.stack.pop_back(); + if (trace_bin_to_json) + printf("%*s#}\n", int((state.stack.size()) * 4), ""); + return true; +} + +ABIEOS_NODISCARD inline bool bin_to_json(pseudo_fixed_sized*, bin_to_json_state& state, bool allow_extensions, + const abi_type* type, bool start) { + if (start) { + if (trace_bin_to_json) + printf("%*s##{\n", int(state.stack.size() * 4), ""); + auto content_begin = state.bin.pos; + if (!skip_raw(state.bin, state.error, type->fixed_size)) + return false; + auto content_end = state.bin.pos; + state.stack.push_back({type, true}); + state.stack.back().saved_bin = state.bin; + state.bin = {content_begin, content_end}; + return type->fixed_sized_of->ser && + type->fixed_sized_of->ser->bin_to_json(state, true, type->fixed_sized_of, true); + } + state.bin = state.stack.back().saved_bin; + state.stack.pop_back(); + if (trace_bin_to_json) + printf("%*s##}\n", int((state.stack.size()) * 4), ""); + return true; +} + ABIEOS_NODISCARD inline bool bin_to_json(pseudo_object*, bin_to_json_state& state, bool allow_extensions, const abi_type* type, bool start) { if (start) { diff --git a/src/test.cpp b/src/test.cpp index bc98cfa..3b3b7ea 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -309,6 +309,18 @@ void check_error(abieos_context* context, const std::string& s, F f) { check_except(s, [&] { check_context(context, f()); }); } +void check_size_suffix(const std::string& type, std::optional expected) { + auto result = abieos::get_size_suffix(type); + printf("get_size_suffix: '%s' ", type.c_str()); + if (result) + printf("value=%u suffix_length=%u\n", unsigned(result->value), unsigned(result->suffix_length)); + else + printf("{}\n"); + if ((result && !expected) || (!result && expected) || + (result && expected && (result->value != expected->value || result->suffix_length != expected->suffix_length))) + throw std::runtime_error("get_size_suffix doesn't match expected"); +} + void check_types() { auto context = check(abieos_create()); auto token = check_context(context, abieos_string_to_name(context, "eosio.token")); @@ -396,6 +408,12 @@ void check_types() { check_type(context, 0, "bool", R"(true)"); check_type(context, 0, "bool", R"(false)"); + check_type(context, 0, "bool#", R"(false)"); + check_type(context, 0, "bool#", R"(true)"); + check_type(context, 0, "bool#1", R"(false)"); + check_type(context, 0, "bool#1", R"(true)"); + check_type(context, 0, "bool#3", R"(false)"); + check_type(context, 0, "bool#3", R"(true)"); check_error(context, "read past end", [&] { return abieos_hex_to_json(context, 0, "bool", ""); }); check_error(context, "failed to parse", [&] { return abieos_json_to_bin(context, 0, "bool", R"(trues)"); }); check_error(context, "expected number or boolean", @@ -416,6 +434,22 @@ void check_types() { check_type(context, 0, "uint8[]", R"([10])"); check_type(context, 0, "uint8[]", R"([10,9])"); check_type(context, 0, "uint8[]", R"([10,9,8])"); + check_type(context, 0, "uint8[]#", R"([])"); + check_type(context, 0, "uint8[]#", R"([10])"); + check_type(context, 0, "uint8[]#", R"([10,9])"); + check_type(context, 0, "uint8[]#", R"([10,9,8])"); + check_type(context, 0, "uint8#[]", R"([])"); + check_type(context, 0, "uint8#[]", R"([10])"); + check_type(context, 0, "uint8#[]", R"([10,9])"); + check_type(context, 0, "uint8#[]", R"([10,9,8])"); + check_type(context, 0, "uint8[]#3", R"([])"); + check_type(context, 0, "uint8[]#3", R"([10])"); + check_type(context, 0, "uint8[]#3", R"([10,9])"); + check_error(context, ": data exceeds fixed-size limit (#)", + [&] { return abieos_json_to_bin(context, 0, "uint8[]#3", "[10,9,8]"); }); + check_type(context, 0, "uint8#2[]", R"([])"); + check_type(context, 0, "uint8#2[]", R"([10])"); + check_type(context, 0, "uint8#2[]", R"([10,9])"); check_type(context, 0, "int16", R"(0)"); check_type(context, 0, "int16", R"(32767)"); check_type(context, 0, "int16", R"(-32768)"); @@ -495,6 +529,12 @@ void check_types() { check_type(context, 0, "varuint32", R"(268435457)"); check_type(context, 0, "varuint32", R"(4294967294)"); check_type(context, 0, "varuint32", R"(4294967295)"); + check_type(context, 0, "varuint32#", R"(0)"); + check_type(context, 0, "varuint32#", R"(127)"); + check_type(context, 0, "varuint32#", R"(128)"); + check_type(context, 0, "varuint32#3", R"(0)"); + check_type(context, 0, "varuint32#3", R"(127)"); + check_type(context, 0, "varuint32#3", R"(128)"); check_type(context, 0, "varint32", R"(0)"); check_type(context, 0, "varint32", R"(-1)"); check_type(context, 0, "varint32", R"(1)"); @@ -566,10 +606,18 @@ void check_types() { check_type(context, 0, "string", R"("' + '*'.repeat(128) + '")"); check_type(context, 0, "string", R"("\u0000 这是一个测试 Это тест هذا اختبار 👍")"); check_error(context, "invalid string size", [&] { return abieos_hex_to_json(context, 0, "string", "01"); }); + check_type(context, 0, "string[]", R"(["a","b"])"); + check_type(context, 0, "string[]#5", R"(["a","b"])"); + check_type(context, 0, "string[]#6", R"(["a","b"])"); + check_type(context, 0, "string#2[]", R"(["a","b"])"); + check_type(context, 0, "string#3[]", R"(["a","b"])"); check_type(context, 0, "checksum160", R"("0000000000000000000000000000000000000000")"); check_type(context, 0, "checksum160", R"("123456789ABCDEF01234567890ABCDEF70123456")"); check_type(context, 0, "checksum256", R"("0000000000000000000000000000000000000000000000000000000000000000")"); check_type(context, 0, "checksum256", R"("0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF")"); + check_type(context, 0, "checksum256#", R"("0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF")"); + check_type(context, 0, "checksum256#32", R"("0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF")"); + check_type(context, 0, "checksum256#33", R"("0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF")"); check_type( context, 0, "checksum512", R"("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")"); @@ -668,6 +716,9 @@ void check_types() { check_type(context, 0, "asset", R"("0.000 FOO")"); check_type(context, 0, "asset", R"("1.2345 SYS")"); check_type(context, 0, "asset", R"("-1.2345 SYS")"); + check_type(context, 0, "asset#", R"("1.2345 SYS")"); + check_type(context, 0, "asset#16", R"("1.2345 SYS")"); + check_type(context, 0, "asset#17", R"("1.2345 SYS")"); check_error(context, "expected string containing asset", [&] { return abieos_json_to_bin(context, 0, "asset", "null"); }); check_type(context, 0, "asset[]", R"([])"); @@ -677,6 +728,9 @@ void check_types() { check_type(context, 0, "asset?", R"("0.123456 SIX")"); check_type(context, 0, "extended_asset", R"({"quantity":"0 FOO","contract":"bar"})"); check_type(context, 0, "extended_asset", R"({"quantity":"0.123456 SIX","contract":"seven"})"); + check_type(context, 0, "extended_asset#", R"({"quantity":"0.123456 SIX","contract":"seven"})"); + check_type(context, 0, "extended_asset#24", R"({"quantity":"0.123456 SIX","contract":"seven"})"); + check_type(context, 0, "extended_asset#25", R"({"quantity":"0.123456 SIX","contract":"seven"})"); check_type(context, token, "transfer", R"({"from":"useraaaaaaaa","to":"useraaaaaaab","quantity":"0.0001 SYS","memo":"test memo"})"); @@ -713,17 +767,19 @@ void check_types() { [&] { return abieos_json_to_bin(context, 0, "int8$[]", ""); }); check_error(context, "binary extensions ($) may not contain binary extensions ($)", [&] { return abieos_json_to_bin(context, 0, "int8$$", ""); }); + check_error(context, "size suffix (#) is out of range 1-1024", + [&] { return abieos_json_to_bin(context, 0, "int8#0", ""); }); + check_error(context, "size suffix (#) is out of range 1-1024", + [&] { return abieos_json_to_bin(context, 0, "int8#1025", ""); }); check_error(context, "unknown type \"fee\"", [&] { return abieos_json_to_bin(context, 0, "fee", ""); }); check_error(context, "abi has a type with a missing name", [&] { return abieos_set_abi( // - context, 0, - R"({"version":"eosio::abi/1.1","types":[{"new_type_name":"","type":"int8"}]})"); + context, 0, R"({"version":"eosio::abi/1.1","types":[{"new_type_name":"","type":"int8"}]})"); }); check_error(context, "can't use extensions ($) within typedefs", [&] { return abieos_set_abi( // - context, 0, - R"({"version":"eosio::abi/1.1","types":[{"new_type_name":"a","type":"int8$"}]})"); + context, 0, R"({"version":"eosio::abi/1.1","types":[{"new_type_name":"a","type":"int8$"}]})"); }); check_error(context, "abi redefines type \"a\"", [&] { return abieos_set_abi( @@ -896,6 +952,14 @@ void check_types() { context, testAbiName, "s5", R"({"x1":9,"x2":10,"x3":{"c1":4,"c2":[{"x1":7,"x2":true,"x3":{"c1":0,"c2":[],"c3":7}},{"x1":null} ]}} )"); }); + check_size_suffix("", {}); + check_size_suffix("#", {}); + check_size_suffix("#0", abieos::size_suffix{0, 2}); + check_size_suffix("#1", abieos::size_suffix{1, 2}); + check_size_suffix("#1234", abieos::size_suffix{1234, 5}); + check_size_suffix("#001234", abieos::size_suffix{1234, 7}); + check_size_suffix("#4294967295", abieos::size_suffix{4294967295, 11}); + check_size_suffix("#4294967296", {}); auto testWith = [&](auto& abiName) { check_type(context, abiName, "v1", R"(["int8",7])"); diff --git a/test/src/test.ts b/test/src/test.ts index c43c07e..064ec34 100644 --- a/test/src/test.ts +++ b/test/src/test.ts @@ -240,6 +240,12 @@ function check_types() { check_type(0, js2Types, 'bool', 'true'); check_type(0, js2Types, 'bool', 'false'); + check_type(0, js2Types, 'bool#', 'false'); + check_type(0, js2Types, 'bool#', 'true'); + check_type(0, js2Types, 'bool#1', 'false'); + check_type(0, js2Types, 'bool#1', 'true'); + check_type(0, js2Types, 'bool#3', 'false'); + check_type(0, js2Types, 'bool#3', 'true'); check_throw('Error: Read past end of buffer', () => eosjs_hex_to_json(js2Types, 'bool', '')); check_throw('Error: Expected true or false', () => eosjs_json_to_hex(js2Types, 'bool', 'trues')); check_throw('Error: Expected true or false', () => eosjs_json_to_hex(js2Types, 'bool', null)); @@ -262,6 +268,21 @@ function check_types() { check_type(0, js2Types, 'uint8[]', '[10]'); check_type(0, js2Types, 'uint8[]', '[10,9]'); check_type(0, js2Types, 'uint8[]', '[10,9,8]'); + check_type(0, js2Types, 'uint8[]#', '[]'); + check_type(0, js2Types, 'uint8[]#', '[10]'); + check_type(0, js2Types, 'uint8[]#', '[10,9]'); + check_type(0, js2Types, 'uint8[]#', '[10,9,8]'); + check_type(0, js2Types, 'uint8#[]', '[]'); + check_type(0, js2Types, 'uint8#[]', '[10]'); + check_type(0, js2Types, 'uint8#[]', '[10,9]'); + check_type(0, js2Types, 'uint8#[]', '[10,9,8]'); + check_type(0, js2Types, 'uint8[]#3', '[]'); + check_type(0, js2Types, 'uint8[]#3', '[10]'); + check_type(0, js2Types, 'uint8[]#3', '[10,9]'); + check_throw('Error: data exceeds fixed-size limit (#3)', () => eosjs_json_to_hex(js2Types, 'uint8[]#3', [10, 9, 8])); + check_type(0, js2Types, 'uint8#2[]', '[]'); + check_type(0, js2Types, 'uint8#2[]', '[10]'); + check_type(0, js2Types, 'uint8#2[]', '[10,9]'); check_type(0, js2Types, 'int16', '0'); check_type(0, js2Types, 'int16', '32767'); check_type(0, js2Types, 'int16', '-32768'); @@ -331,6 +352,12 @@ function check_types() { check_type(0, js2Types, 'varuint32', '268435457'); check_type(0, js2Types, 'varuint32', '4294967294'); check_type(0, js2Types, 'varuint32', '4294967295'); + check_type(0, js2Types, 'varuint32#', '0'); + check_type(0, js2Types, 'varuint32#', '127'); + check_type(0, js2Types, 'varuint32#', '128'); + check_type(0, js2Types, 'varuint32#3', '0'); + check_type(0, js2Types, 'varuint32#3', '127'); + check_type(0, js2Types, 'varuint32#3', '128'); check_type(0, js2Types, 'varint32', '0'); check_type(0, js2Types, 'varint32', '-1'); check_type(0, js2Types, 'varint32', '1'); @@ -393,10 +420,18 @@ function check_types() { check_type(0, js2Types, 'string', '"' + '*'.repeat(128) + '"'); check_type(0, js2Types, 'string', `"\\u0000 这是一个测试 Это тест هذا اختبار 👍"`); check_throw('Error: Read past end of buffer', () => eosjs_hex_to_json(js2Types, 'string', '01')); + check_type(0, js2Types, 'string[]', '["a","b"]'); + check_type(0, js2Types, 'string[]#5', '["a","b"]'); + check_type(0, js2Types, 'string[]#6', '["a","b"]'); + check_type(0, js2Types, 'string#2[]', '["a","b"]'); + check_type(0, js2Types, 'string#3[]', '["a","b"]'); check_type(0, js2Types, 'checksum160', '"0000000000000000000000000000000000000000"'); check_type(0, js2Types, 'checksum160', '"123456789ABCDEF01234567890ABCDEF70123456"'); check_type(0, js2Types, 'checksum256', '"0000000000000000000000000000000000000000000000000000000000000000"'); check_type(0, js2Types, 'checksum256', '"0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF"'); + check_type(0, js2Types, 'checksum256#', '"0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF"'); + check_type(0, js2Types, 'checksum256#32', '"0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF"'); + check_type(0, js2Types, 'checksum256#33', '"0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF"'); check_type(0, js2Types, 'checksum512', '"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"'); check_type(0, js2Types, 'checksum512', '"0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF0987654321ABCDEF0987654321FFFF1234567890ABCDEF001234567890ABCDEF"'); check_throw('Error: Expected hex string', () => eosjs_json_to_hex(js2Types, 'checksum256', 'yz')); @@ -458,6 +493,9 @@ function check_types() { check_type(0, js2Types, 'asset', '"0.000 FOO"'); check_type(0, js2Types, 'asset', '"1.2345 SYS"'); check_type(0, js2Types, 'asset', '"-1.2345 SYS"'); + check_type(0, js2Types, 'asset#', '"1.2345 SYS"'); + check_type(0, js2Types, 'asset#16', '"1.2345 SYS"'); + check_type(0, js2Types, 'asset#17', '"1.2345 SYS"'); check_throw('Error: Expected string containing asset', () => eosjs_json_to_hex(js2Types, 'asset', null)); check_type(0, js2Types, 'asset[]', '[]'); check_type(0, js2Types, 'asset[]', '["0 FOO"]'); @@ -466,6 +504,9 @@ function check_types() { check_type(0, js2Types, 'asset?', '"0.123456 SIX"'); check_type(0, js2Types, 'extended_asset', '{"quantity":"0 FOO","contract":"bar"}'); check_type(0, js2Types, 'extended_asset', '{"quantity":"0.123456 SIX","contract":"seven"}'); + check_type(0, js2Types, 'extended_asset#', '{"quantity":"0.123456 SIX","contract":"seven"}'); + check_type(0, js2Types, 'extended_asset#24', '{"quantity":"0.123456 SIX","contract":"seven"}'); + check_type(0, js2Types, 'extended_asset#25', '{"quantity":"0.123456 SIX","contract":"seven"}'); check_type(token, tokenTypes, "transfer", '{"from":"useraaaaaaaa","to":"useraaaaaaab","quantity":"0.0001 SYS","memo":"test memo"}'); check_type(0, js2Types, "transaction", '{"expiration":"2009-02-13T23:31:31.000","ref_block_num":1234,"ref_block_prefix":5678,"max_net_usage_words":0,"max_cpu_usage_ms":0,"delay_sec":0,"context_free_actions":[],"actions":[{"account":"eosio.token","name":"transfer","authorization":[{"actor":"useraaaaaaaa","permission":"active"}],"data":"608C31C6187315D6708C31C6187315D60100000000000000045359530000000000"}],"transaction_extensions":[]}');