#pragma once #include "name.hpp" #include "types.hpp" #include #include #include #include #include #include "fixed_bytes.hpp" #include "crypto.hpp" #include "varint.hpp" #include "float.hpp" #include "time.hpp" #include "bytes.hpp" #include "asset.hpp" namespace eosio { enum class abi_error { no_error, recursion_limit_reached, invalid_nesting, unknown_type, missing_name, redefined_type, base_not_a_struct, extension_typedef, bad_abi }; constexpr inline std::string_view convert_abi_error(eosio::abi_error e) { switch (e) { case abi_error::no_error: return "No error"; case abi_error::recursion_limit_reached: return "Recursion limit reached"; case abi_error::invalid_nesting: return "Invalid nesting"; case abi_error::unknown_type: return "Unknown type"; case abi_error::missing_name: return "Missing name"; case abi_error::redefined_type: return "Redefined type"; case abi_error::base_not_a_struct: return "Base not a struct"; case abi_error::extension_typedef: return "Extension typedef"; case abi_error::bad_abi: return "Bad ABI"; default: return "internal failure"; }; } struct abi_serializer; template struct might_not_exist { T value{}; }; template void from_bin(might_not_exist& obj, S& stream) { if (stream.remaining()) return from_bin(obj.value, stream); } template void to_bin(const might_not_exist& obj, S& stream) { return to_bin(obj.value, stream); } template void from_json(might_not_exist& obj, S& stream) { return from_json(obj.value, stream); } template void to_json(const might_not_exist& val, S& stream) { return to_json(val.value, stream); } using abi_extensions_type = std::vector>>; struct type_def { std::string new_type_name{}; std::string type{}; }; EOSIO_REFLECT(type_def, new_type_name, type); struct field_def { std::string name{}; std::string type{}; }; EOSIO_REFLECT(field_def, name, type); struct struct_def { std::string name{}; std::string base{}; std::vector fields{}; }; EOSIO_REFLECT(struct_def, name, base, fields); struct action_def { eosio::name name{}; std::string type{}; std::string ricardian_contract{}; }; EOSIO_REFLECT(action_def, name, type, ricardian_contract); struct table_def { eosio::name name{}; std::string index_type{}; std::vector key_names{}; std::vector key_types{}; std::string type{}; }; EOSIO_REFLECT(table_def, name, index_type, key_names, key_types, type); struct clause_pair { std::string id{}; std::string body{}; }; EOSIO_REFLECT(clause_pair, id, body); struct error_message { uint64_t error_code{}; std::string error_msg{}; }; EOSIO_REFLECT(error_message, error_code, error_msg); struct variant_def { std::string name{}; std::vector types{}; }; EOSIO_REFLECT(variant_def, name, types); struct action_result_def { eosio::name name{}; std::string result_type{}; }; EOSIO_REFLECT(action_result_def, name, result_type); struct primary_key_index_def { eosio::name name{}; std::string type; }; EOSIO_REFLECT(primary_key_index_def, name, type); struct secondary_index_def { std::string type; }; EOSIO_REFLECT(secondary_index_def, type); struct abi_def { std::string version{}; std::vector types{}; std::vector structs{}; std::vector actions{}; std::vector tables{}; std::vector ricardian_clauses{}; std::vector error_messages{}; abi_extensions_type abi_extensions{}; might_not_exist> variants{}; might_not_exist> action_results{}; }; EOSIO_REFLECT(abi_def, version, types, structs, actions, tables, ricardian_clauses, error_messages, abi_extensions, variants, action_results); struct abi_type; struct abi_field { std::string name; const abi_type* type; }; struct abi_type { std::string name; struct builtin {}; using alias_def = std::string; struct alias { abi_type* type; }; struct optional { abi_type* type; }; struct extension { abi_type* type; }; struct array { abi_type* type; }; struct struct_ { abi_type* base = nullptr; std::vector fields; }; using variant = std::vector; std::variant _data; const abi_serializer* ser = nullptr; template abi_type(std::string name, T&& arg, const abi_serializer* ser) : name(std::move(name)), _data(std::forward(arg)), ser(ser) {} abi_type(const abi_type&) = delete; abi_type& operator=(const abi_type&) = delete; // result json_to_bin(std::vector& bin, std::string_view json); const abi_type* optional_of() const { if (auto* t = std::get_if(&_data)) return t->type; else return nullptr; } const abi_type* extension_of() const { if (auto* t = std::get_if(&_data)) return t->type; else return nullptr; } const abi_type* array_of() const { if (auto* t = std::get_if(&_data)) return t->type; else return nullptr; } const struct_* as_struct() const { return std::get_if(&_data); } const variant* as_variant() const { return std::get_if(&_data); } std::string bin_to_json( input_stream& bin, std::function f = [] {}) const; std::vector json_to_bin( std::string_view json, std::function f = [] {}) const; std::vector json_to_bin_reorderable( std::string_view json, std::function f = [] {}) const; }; struct abi { std::map action_types; std::map table_types; std::map abi_types; std::map action_result_types; const abi_type* get_type(const std::string& name); // Adds a type to the abi. Has no effect if the type is already present. // If the type is a struct, all members will be added recursively. // Exception Safety: basic. If add_type fails, some objects may have // an incomplete list of fields. template abi_type* add_type(); }; void convert(const abi_def& def, abi&); void convert(const abi& def, abi_def&); extern const abi_serializer* const object_abi_serializer; extern const abi_serializer* const variant_abi_serializer; extern const abi_serializer* const array_abi_serializer; extern const abi_serializer* const extension_abi_serializer; extern const abi_serializer* const optional_abi_serializer; using basic_abi_types = std::tuple; namespace detail { template constexpr bool contains(std::tuple*) { return (std::is_same_v || ...); } } // namespace detail template constexpr bool is_basic_abi_type = detail::contains((basic_abi_types*)nullptr); template auto add_type(abi& a, T*) -> std::enable_if_t && !is_basic_abi_type, abi_type*> { std::string name = get_type_name((T*)nullptr); auto [iter, inserted] = a.abi_types.try_emplace(name, name, abi_type::struct_{}, object_abi_serializer); if (!inserted) return &iter->second; auto& s = std::get(iter->second._data); for_each_field([&](const char* name, auto&& member) { auto member_type = a.add_type>(); s.fields.push_back({ name, member_type }); }); return &iter->second; } template auto add_type(abi& a, T* t) -> std::enable_if_t, abi_type*> { auto iter = a.abi_types.find(get_type_name(t)); check(iter != a.abi_types.end(), convert_abi_error(abi_error::unknown_type)); return &iter->second; } template abi_type* add_type(abi& a, std::vector*) { auto element_type = a.add_type(); check(!(element_type->optional_of() || element_type->array_of() || element_type->extension_of()), convert_abi_error(abi_error::invalid_nesting)); std::string name = get_type_name((std::vector*)nullptr); auto [iter, inserted] = a.abi_types.try_emplace(name, name, abi_type::array{ element_type }, array_abi_serializer); return &iter->second; } template auto add_type(abi& a, std::variant*) -> std::enable_if_t>, abi_type*> { abi_type::variant types; ( [&](auto* t) { auto type = add_type(a, t); types.push_back({ type->name, type }); }((T*)nullptr), ...); std::string name = get_type_name((std::variant*)nullptr); auto [iter, inserted] = a.abi_types.try_emplace(name, name, std::move(types), variant_abi_serializer); return &iter->second; } template abi_type* add_type(abi& a, std::optional*) { auto element_type = a.add_type(); check(!(element_type->optional_of() || element_type->array_of() || element_type->extension_of()), convert_abi_error(abi_error::invalid_nesting)); std::string name = get_type_name((std::optional*)nullptr); auto [iter, inserted] = a.abi_types.try_emplace(name, name, abi_type::optional{ element_type }, optional_abi_serializer); return &iter->second; } template abi_type* add_type(abi& a, might_not_exist*) { auto element_type = a.add_type(); check(!element_type->extension_of(), convert_abi_error(abi_error::invalid_nesting)); std::string name = element_type->name + "$"; auto [iter, inserted] = a.abi_types.try_emplace(name, name, abi_type::extension{ element_type }, extension_abi_serializer); return &iter->second; } template abi_type* abi::add_type() { using eosio::add_type; return add_type(*this, (T*)nullptr); } template void to_json_write_helper(const T& field, const std::string_view field_name, const bool need_comma, S& stream) { if (need_comma) { stream.write(','); } to_json(field_name, stream); stream.write(':'); to_json(field, stream); } template void to_json(const abi_def& def, S& stream) { stream.write('{'); to_json_write_helper(def.version, "version", false, stream); to_json_write_helper(def.types, "types", true, stream); to_json_write_helper(def.structs, "structs", true, stream); to_json_write_helper(def.actions, "actions", true, stream); to_json_write_helper(def.tables, "tables", true, stream); to_json_write_helper(def.ricardian_clauses, "ricardian_clauses", true, stream); to_json_write_helper(def.error_messages, "error_messages", true, stream); to_json_write_helper(def.variants.value, "variants", true, stream); to_json_write_helper(def.action_results.value, "action_results", true, stream); stream.write('}'); } } // namespace eosio