From b2e9872645ff321158135c0e7c090b5bf4841b70 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Fri, 18 Oct 2024 00:26:30 +0400 Subject: [PATCH] implemented proper versioning in serialization asset_descriptor_* structs + serialization refactoring + boost serialization maps versioning self-vaidation --- src/common/boost_serialization_maps.h | 53 +++++++++ src/currency_core/currency_basic.h | 108 +++++++++++++----- .../currency_basic_backward_comp.inl | 71 ++++++++++++ .../currency_boost_serialization.h | 11 +- src/currency_core/currency_format_utils.h | 8 +- src/currency_core/miner.cpp | 8 ++ 6 files changed, 221 insertions(+), 38 deletions(-) diff --git a/src/common/boost_serialization_maps.h b/src/common/boost_serialization_maps.h index 5fbecea0..2bd9fda3 100644 --- a/src/common/boost_serialization_maps.h +++ b/src/common/boost_serialization_maps.h @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once +#include +#include #define BEGIN_BOOST_SERIALIZATION() template void serialize(t_archive &_arch, const unsigned int ver) { @@ -26,6 +28,57 @@ template struct TAssertEquality { #define END_BOOST_SERIALIZATION() } + +/********************************************************************************************************************************** + This serialization closing macro adds self-validation by checking the total number of fields in the structure using boost::pfr. + + Note: "num_fields" does NOT represent the number of fields included in the serialization. Instead, it indicates the total number + of fields in the structure, some of which might not be included in the serialization for valid reasons. If someone adds new + fields to the structure but forgets to update the serialization map, the compilation will fail. Any update to "num_fields" must + be accompanied by a thorough review of the serialization map to ensure no fields are omitted. +**********************************************************************************************************************************/ +#define END_BOOST_SERIALIZATION_TOTAL_FIELDS(num_fields) static_assert(num_fields == boost::pfr::tuple_size::type>::value, "Unexpected number of fields!"); } + + +#define BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(current_version) static const unsigned int current_boost_version_serialization_version = current_version; + +#define LOOP_BACK_BOOST_SERIALIZATION_VERSION(type) BOOST_CLASS_VERSION(type, type::current_boost_version_serialization_version); + + + +template +struct boost_transition_t {}; + +template +struct boost_transition_t +{ + template + static void chain_serialize(archive& ar, const origin_type& origin_tx) + { + destination_t dst_tx = AUTO_VAL_INIT(dst_tx); + transition_convert(origin_tx, dst_tx); + ar & dst_tx; + } +}; + +template +struct boost_transition_t +{ + template + static void chain_serialize(archive& ar, origin_type& origin_tx) + { + // TODO: consider using move semantic for temporary 'dst_tx' + destination_t dst_tx = AUTO_VAL_INIT(dst_tx); + ar & dst_tx; + transition_convert(dst_tx, origin_tx); + } +}; + + +#define BOOST_CHAIN_TRANSITION_VER(obj_version, old_type) if (obj_version == ver) {boost_transition_t::chain_serialize(_arch, *this);return;} + +#define BOOST_CHAIN_TRANSITION_IF_COND_TRUE(condition, old_type) if (condition) {boost_transition_t::chain_serialize(_arch, *this);return;} + /* example of use: diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index c6581611..b968e617 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -696,9 +696,17 @@ namespace currency } }; -#define ASSET_DESCRIPTOR_BASE_STRUCTURE_VER 1 +#define ASSET_DESCRIPTOR_BASE_STRUCTURE_LAST_VER 2 - typedef boost::variant asset_owner_pub_key_v; + struct dummy{ + BEGIN_SERIALIZE() + END_SERIALIZE() + + BEGIN_BOOST_SERIALIZATION() + END_BOOST_SERIALIZATION_TOTAL_FIELDS(0) + }; + + typedef boost::variant asset_descriptor_base_etc_fields; struct asset_descriptor_base { @@ -710,11 +718,14 @@ namespace currency std::string meta_info; crypto::public_key owner = currency::null_pkey; // consider premultipling by 1/8 bool hidden_supply = false; + uint8_t version = 0; + //version 1 members boost::optional owner_eth_pub_key; // note: the size is 33 bytes (if present) // NOTE: using boost::optional instead of std::optional because of the Boost compilation issue: https://github.com/boostorg/serialization/issues/319 -- sowle + //version 2 members + std::vector etc; //container for future use if we would be adding some optional parameters that is not known yet, but without mess related to format version - uint8_t version = ASSET_DESCRIPTOR_BASE_STRUCTURE_VER; - BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_BASE_STRUCTURE_VER, version) + BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_BASE_STRUCTURE_LAST_VER, version) FIELD(total_max_supply) FIELD(current_supply) FIELD(decimal_point) @@ -725,8 +736,11 @@ namespace currency FIELD(hidden_supply) END_VERSION_UNDER(1) FIELD(owner_eth_pub_key) + END_VERSION_UNDER(2) + FIELD(etc) END_SERIALIZE() + BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(2) BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(total_max_supply) BOOST_SERIALIZE(current_supply) @@ -738,7 +752,10 @@ namespace currency BOOST_SERIALIZE(hidden_supply) BOOST_END_VERSION_UNDER(1) BOOST_SERIALIZE(owner_eth_pub_key) - END_BOOST_SERIALIZATION() + BOOST_END_VERSION_UNDER(2) + BOOST_SERIALIZE(etc) + BOOST_SERIALIZE(version) + END_BOOST_SERIALIZATION_TOTAL_FIELDS(11) BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(total_max_supply) DOC_DSCR("Maximum possible supply for a given asset, cannot be changed after deployment.") DOC_EXMP(1000000000000000000) DOC_END @@ -778,38 +795,56 @@ namespace currency #define ASSET_DESCRIPTOR_OPERATION_UPDATE 3 #define ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN 4 -#define ASSET_DESCRIPTOR_OPERATION_STRUCTURE_VER 1 +#define ASSET_DESCRIPTOR_OPERATION_LAST_VER 2 + + typedef boost::variant asset_descriptor_operator_etc_fields; struct asset_descriptor_operation { - uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED; - asset_descriptor_base descriptor; - crypto::public_key amount_commitment; // premultiplied by 1/8 - boost::optional opt_asset_id; // target asset_id - for update/emit - uint8_t verion = ASSET_DESCRIPTOR_OPERATION_STRUCTURE_VER; + uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED; + uint8_t version = 1; - BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_OPERATION_STRUCTURE_VER, verion) + boost::optional opt_amount_commitment; // premultiplied by 1/8 + boost::optional opt_asset_id; // target asset_id - for update/emit + boost::optional opt_descriptor; //used in deploy/update + boost::optional opt_amount; //used in burn/emit + std::vector etc; //reserved for future use + + + BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_OPERATION_LAST_VER, version) + CHAIN_TRANSITION_VER(0, asset_descriptor_operation_v1) + CHAIN_TRANSITION_VER(1, asset_descriptor_operation_v1) FIELD(operation_type) - FIELD(descriptor) - FIELD(amount_commitment) - END_VERSION_UNDER(1) + FIELD(opt_amount_commitment) FIELD(opt_asset_id) + FIELD(opt_descriptor) + FIELD(opt_amount) + FIELD(etc) END_SERIALIZE() + BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(2) BEGIN_BOOST_SERIALIZATION() + BOOST_CHAIN_TRANSITION_VER(1, asset_descriptor_operation_v1) + BOOST_CHAIN_TRANSITION_VER(0, asset_descriptor_operation_v1) + BOOST_SERIALIZE(version) BOOST_SERIALIZE(operation_type) - BOOST_SERIALIZE(descriptor) - BOOST_SERIALIZE(amount_commitment) - BOOST_END_VERSION_UNDER(1) + BOOST_SERIALIZE(opt_amount_commitment) BOOST_SERIALIZE(opt_asset_id) - END_BOOST_SERIALIZATION() + BOOST_SERIALIZE(opt_descriptor) + BOOST_SERIALIZE(opt_amount) + BOOST_SERIALIZE(etc) + END_BOOST_SERIALIZATION_TOTAL_FIELDS(7) BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(operation_type) DOC_DSCR("Asset operation type identifier") DOC_EXMP(1) DOC_END - KV_SERIALIZE(descriptor) DOC_DSCR("Asset descriptor") DOC_EXMP_AUTO() DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(amount_commitment) DOC_DSCR("Amount commitment") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(opt_asset_id) DOC_DSCR("ID of an asset.") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END + KV_SERIALIZE(version) DOC_DSCR("Asset operation type struct version") DOC_EXMP(2) DOC_END + KV_SERIALIZE(operation_type) DOC_DSCR("Asset operation type identifier") DOC_EXMP(1) DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(opt_amount_commitment) DOC_DSCR("Asset operation amount commitment(optional)") DOC_EXMP("5688b56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(opt_asset_id) DOC_DSCR("ID of an asset.(optional)") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END + KV_SERIALIZE(opt_descriptor) DOC_DSCR("Asset operation amount commitment(optional)") DOC_EXMP_AUTO() DOC_END + KV_SERIALIZE(opt_amount) DOC_DSCR("Asset operation amount(optional, needed only for burn/emit)") DOC_EXMP_AUTO() DOC_END + //KV_SERIALIZE(etc) DOC_DSCR("Extra operations") DOC_EXMP_AUTO() DOC_END <---- serialization for variant not supported yet END_KV_SERIALIZE_MAP() + }; struct asset_operation_proof @@ -824,6 +859,7 @@ namespace currency FIELD(opt_amount_commitment_g_proof) END_SERIALIZE() + BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(1) BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(opt_amount_commitment_composition_proof) BOOST_SERIALIZE(opt_amount_commitment_g_proof) @@ -842,6 +878,7 @@ namespace currency FIELD(gss) END_SERIALIZE() + BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(1) BEGIN_BOOST_SERIALIZATION() BOOST_SERIALIZE(gss) BOOST_END_VERSION_UNDER(1) @@ -1023,6 +1060,19 @@ namespace currency FIELD(signatures) FIELD(proofs) END_SERIALIZE() + + + BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(0) + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(version) + BOOST_SERIALIZE(vin) + BOOST_SERIALIZE(vout) + BOOST_SERIALIZE(extra) + BOOST_SERIALIZE(signatures) + BOOST_SERIALIZE(attachment) + BOOST_END_VERSION_UNDER(1) + BOOST_SERIALIZE(proofs) + END_BOOST_SERIALIZATION_TOTAL_FIELDS(4) }; @@ -1158,9 +1208,12 @@ BLOB_SERIALIZER(currency::txout_to_key); VARIANT_TAG(json_archive, type_name, json_tag) -BOOST_CLASS_VERSION(currency::asset_descriptor_operation, 1); -BOOST_CLASS_VERSION(currency::asset_operation_proof, 1); -BOOST_CLASS_VERSION(currency::asset_operation_ownership_proof, 1); + +LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_descriptor_operation); +LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_descriptor_base); +LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_operation_proof); +LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_operation_ownership_proof); +LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::transaction); // txin_v variant currency @@ -1235,7 +1288,8 @@ SET_VARIANT_TAGS(currency::asset_operation_ownership_proof, 51, "asset_operation SET_VARIANT_TAGS(currency::asset_operation_ownership_proof_eth, 52, "asset_operation_ownership_proof_eth"); SET_VARIANT_TAGS(crypto::eth_public_key, 60, "eth_public_key"); -//SET_VARIANT_TAGS(crypto::eth_signature, 61, "eth_signature");s +//SET_VARIANT_TAGS(crypto::eth_signature, 61, "eth_signature"); +SET_VARIANT_TAGS(currency::dummy, 62, "dummy"); diff --git a/src/currency_core/currency_basic_backward_comp.inl b/src/currency_core/currency_basic_backward_comp.inl index 7dffea0e..6ce249ee 100644 --- a/src/currency_core/currency_basic_backward_comp.inl +++ b/src/currency_core/currency_basic_backward_comp.inl @@ -116,3 +116,74 @@ bool transition_convert(const transaction_v1& from, transaction_current_t& to) } return true; } + +struct asset_descriptor_operation_v1 +{ + uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED; + asset_descriptor_base descriptor; + crypto::public_key amount_commitment; // premultiplied by 1/8 + boost::optional opt_asset_id; // target asset_id - for update/emit + uint8_t verion = 1; + + BEGIN_VERSIONED_SERIALIZE(1, verion) + FIELD(operation_type) + FIELD(descriptor) + FIELD(amount_commitment) + END_VERSION_UNDER(1) + FIELD(opt_asset_id) + END_SERIALIZE() + + + //this map doesn't store internal version member, but it set it by default to val "1", which then transfered via transition_convert() to destination struct + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(operation_type) + BOOST_SERIALIZE(descriptor) + BOOST_SERIALIZE(amount_commitment) + BOOST_END_VERSION_UNDER(1) + BOOST_SERIALIZE(opt_asset_id) + END_BOOST_SERIALIZATION() +}; + +template +bool transition_convert(const asset_descriptor_operation_t& from, asset_descriptor_operation_v1& to) +{ + to.verion = from.version; + to.operation_type = from.operation_type; + if(from.opt_descriptor.has_value()) + { + to.descriptor = *from.opt_descriptor; + } + else + { + throw std::runtime_error(std::string("Unexpected: missing descriptor in from transaction_current_t")); + } + + if (from.opt_amount_commitment.has_value()) + { + to.amount_commitment = *from.opt_amount_commitment; + } + else + { + throw std::runtime_error(std::string("Unexpected: missing amount_commitment in from transaction_current_t")); + } + + to.opt_asset_id = from.opt_asset_id; + + if(from.opt_amount.has_value() || from.etc.size()) + { + throw std::runtime_error(std::string("Unexpected: opt_amount or etc have values during convention, looks like object slicing with information getting lost")); + } + + return true; +} + +template +bool transition_convert(const asset_descriptor_operation_v1& from, asset_descriptor_operation_t& to) +{ + to.operation_type = from.operation_type; + to.opt_descriptor = from.descriptor; + to.opt_amount_commitment = from.amount_commitment; + to.opt_asset_id = to.opt_asset_id; // target asset_id - for update/emit + to.version = from.verion; + return true; +} \ No newline at end of file diff --git a/src/currency_core/currency_boost_serialization.h b/src/currency_core/currency_boost_serialization.h index 5d7c40f7..c12caf25 100644 --- a/src/currency_core/currency_boost_serialization.h +++ b/src/currency_core/currency_boost_serialization.h @@ -222,16 +222,7 @@ namespace boost a & x.buff; } - template - inline void serialize(Archive &a, currency::transaction &x, const boost::serialization::version_type ver) - { - a & x.version; - a & x.vin; - a & x.vout; - a & x.extra; - a & x.signatures; - a & x.attachment; - } + template inline void serialize(Archive &a, currency::keypair &kp, const boost::serialization::version_type ver) diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index e1899ea1..0928a142 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -1209,7 +1209,13 @@ namespace currency tv.short_view = std::string("op:") + get_asset_operation_type_string(ado.operation_type, true); if (ado.opt_asset_id.has_value()) tv.short_view += std::string(" , id:") + crypto::pod_to_hex(ado.opt_asset_id); - tv.details_view = tv.short_view + std::string(" , ticker:") + ado.descriptor.ticker + std::string(" , cur.supply:") + print_money_brief(ado.descriptor.current_supply, ado.descriptor.decimal_point); + tv.details_view = tv.short_view; + if (ado.opt_descriptor.has_value()) + { + tv.details_view += std::string(" , ticker:") + ado.opt_descriptor->ticker + std::string(" , cur.supply:") + print_money_brief(ado.opt_descriptor->current_supply, ado.opt_descriptor->decimal_point); + } + //@#@ TODO: add other info from asset_descriptor_operation v2+ + return true; } template diff --git a/src/currency_core/miner.cpp b/src/currency_core/miner.cpp index d52e51e2..8f4f9f82 100644 --- a/src/currency_core/miner.cpp +++ b/src/currency_core/miner.cpp @@ -124,6 +124,14 @@ namespace currency //----------------------------------------------------------------------------------------------------- void miner::do_print_hashrate(bool do_hr) { +#ifdef _DEBUG + currency::asset_descriptor_operation ado; + std::stringstream ss; + bool r = tools::portble_serialize_obj_to_stream(ado, ss); + std::cout << r; +#endif + + m_do_print_hashrate = do_hr; } //-----------------------------------------------------------------------------------------------------