diff --git a/src/currency_core/account.cpp b/src/currency_core/account.cpp index def20255..04260dd3 100644 --- a/src/currency_core/account.cpp +++ b/src/currency_core/account.cpp @@ -60,7 +60,7 @@ namespace currency return m_keys; } //----------------------------------------------------------------- - void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password) + void account_base::crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password) { crypto::chacha8_key key = AUTO_VAL_INIT(key); crypto::generate_chacha8_key(password, key); @@ -71,16 +71,23 @@ namespace currency crypto::chacha8(scr_data, src_length, key, iv, (char*)dst_data); } //----------------------------------------------------------------- - std::string account_base::get_seed_phrase(const std::string& password) const + std::string account_base::get_seed_phrase(const std::string& password) const { if (m_keys_seed_binary.empty()) return ""; + return get_seed_phrase(password, m_keys_seed_binary); + } + //----------------------------------------------------------------- + std::string account_base::get_seed_phrase(const std::string& password, const std::vector& keys_seed_binary) const + { + if (keys_seed_binary.empty()) + return ""; - std::vector processed_seed_binary = m_keys_seed_binary; + std::vector processed_seed_binary = keys_seed_binary; if (!password.empty()) { //encrypt seed phrase binary data - crypt_with_pass(&m_keys_seed_binary[0], m_keys_seed_binary.size(), &processed_seed_binary[0], password); + crypt_with_pass(&keys_seed_binary[0], keys_seed_binary.size(), &processed_seed_binary[0], password); } std::string keys_seed_text = tools::mnemonic_encoding::binary2text(processed_seed_binary); @@ -92,7 +99,7 @@ namespace currency CHECK_AND_ASSERT_THROW_MES(self_check_is_password_used == !password.empty(), "Account seed phrase internal error: password flag encoded wrong"); constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum - std::string binary_for_check_sum((const char*)&m_keys_seed_binary[0], m_keys_seed_binary.size()); + std::string binary_for_check_sum((const char*)&keys_seed_binary[0], keys_seed_binary.size()); binary_for_check_sum.append(password); crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size()); *reinterpret_cast(&h) = creation_timestamp_rounded; diff --git a/src/currency_core/account.h b/src/currency_core/account.h index 37a0c953..d732181d 100644 --- a/src/currency_core/account.h +++ b/src/currency_core/account.h @@ -56,6 +56,7 @@ namespace currency std::string get_public_address_str() const; std::string get_seed_phrase(const std::string& seed_password) const; + std::string get_seed_phrase(const std::string& password, const std::vector& keys_seed_binary) const; std::string get_tracking_seed() const; bool restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password); bool restore_from_tracking_seed(const std::string& tracking_seed); @@ -82,6 +83,8 @@ namespace currency static std::vector string_to_vector_of_chars(const std::string& v) { return std::vector(v.begin(), v.end()); } static bool is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected); static bool is_seed_tracking(const std::string& seed_phrase); + static void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password); + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_keys) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 4cbf730b..974502d0 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -116,7 +116,7 @@ namespace currency secret_index = ring.size() - 1; } - CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": can't find a corresponding asset id in inputs, asset id: " << H); + CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "out #" << j << ": cannot find a corresponding asset id in inputs or asset operations; asset id: " << H); result.bge_proofs.emplace_back(crypto::BGE_proof_s{}); uint8_t err = 0; @@ -322,7 +322,7 @@ namespace currency CHECK_AND_ASSERT_MES(ogc.asset_id_blinding_mask_x_amount_sum.is_zero(), false, "it's expected that all asset ids for this tx are obvious and thus explicit"); // because this tx has no ZC inputs => all outs clearly have native asset id CHECK_AND_ASSERT_MES(ogc.ao_amount_blinding_mask.is_zero(), false, "asset emmission is not allowed for txs without ZC inputs"); - // (sum(bare inputs' amounts) - fee) * H + sum(pseudo out amount commitments) - sum(outputs' commitments) = lin(G) + // (sum(bare inputs' amounts) - fee) * H - sum(outputs' commitments) = lin(G) crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt - ogc.amount_commitments_sum; crypto::scalar_t secret_x = -ogc.amount_blinding_masks_sum; @@ -336,6 +336,8 @@ namespace currency { // there're ZC inputs => in main balance equation we only need to cancel out X-component, because G-component cancelled out by choosing blinding mask for the last pseudo out amount commitment + // (sum(bare inputs' amounts) - fee) * H + sum(pseudo out amount commitments) + asset_op_commitment - sum(outputs' commitments) = lin(X) + crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * currency::native_coin_asset_id_pt + ogc.pseudo_out_amount_commitments_sum + (ogc.ao_commitment_in_outputs ? -ogc.ao_amount_commitment : ogc.ao_amount_commitment) - ogc.amount_commitments_sum; crypto::scalar_t secret_x = ogc.real_in_asset_id_blinding_mask_x_amount_sum - ogc.asset_id_blinding_mask_x_amount_sum; @@ -472,7 +474,7 @@ namespace currency } CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS || height == 0, false, "Too many outs (" << destinations.size() << ")! Miner tx can't be constructed."); - tx = AUTO_VAL_INIT_T(transaction); + // tx is not cleared intentionally to allow passing additional args in the extra/attachments tx.version = tx_version; tx_generation_context tx_gen_context{}; diff --git a/src/gui/qt-daemon/application/mainwindow.cpp b/src/gui/qt-daemon/application/mainwindow.cpp index 90f3b422..2f34fba1 100644 --- a/src/gui/qt-daemon/application/mainwindow.cpp +++ b/src/gui/qt-daemon/application/mainwindow.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Boolberry developers // Distributed under the MIT/X11 software license, see the accompanying @@ -714,9 +714,12 @@ bool MainWindow::show_inital() { TRY_ENTRY(); if (load_app_config()) + { restore_pos(true); + } else { + LOG_PRINT_L1("set defaults values to config"); m_config = AUTO_VAL_INIT(m_config); this->show(); QSize sz = AUTO_VAL_INIT(sz); @@ -976,6 +979,11 @@ QString MainWindow::start_backend(const QString& params) CATCH_ENTRY_FAIL_API_RESPONCE(); } +void MainWindow::show_notification(const QString& title, const QString& message) +{ + show_notification(title.toStdString(), message.toStdString()); +} + QString MainWindow::sync_call(const QString& func_name, const QString& params) { if (func_name == "test_call") @@ -1107,6 +1115,7 @@ bool MainWindow::get_is_disabled_notifications(const QString& param) } bool MainWindow::set_is_disabled_notifications(const bool& param) { + LOG_PRINT_L1("set_is_disabled_notifications: notifications were " << (m_config.disable_notifications ? "DISABLED" : "ENABLED") << " -> now " << (param ? "DISABLED" : "ENABLED")); m_config.disable_notifications = param; return m_config.disable_notifications; } @@ -2468,6 +2477,10 @@ QString MainWindow::print_log(const QString& param) void MainWindow::show_notification(const std::string& title, const std::string& message) { TRY_ENTRY(); + + if (m_config.disable_notifications) + return; + LOG_PRINT_L1("system notification: \"" << title << "\", \"" << message << "\""); // it's expected that title and message are utf-8 encoded! @@ -2482,5 +2495,3 @@ void MainWindow::show_notification(const std::string& title, const std::string& #endif CATCH_ENTRY2(void()); } - - diff --git a/src/gui/qt-daemon/application/mainwindow.h b/src/gui/qt-daemon/application/mainwindow.h index 0f575b6b..c0b37cf2 100644 --- a/src/gui/qt-daemon/application/mainwindow.h +++ b/src/gui/qt-daemon/application/mainwindow.h @@ -190,6 +190,7 @@ public: void on_menu_show(const QString& param); QString is_remnotenode_mode_preconfigured(const QString& param); QString start_backend(const QString& params); + void show_notification(const QString& title, const QString& message); QString async_call(const QString& func_name, const QString& params); QString sync_call(const QString& func_name, const QString& params); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 6ab7bfa7..a27b58d8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -124,6 +124,7 @@ namespace { const command_line::arg_descriptor arg_wallet_file ("wallet-file", "Use wallet ", ""); const command_line::arg_descriptor arg_generate_new_wallet ("generate-new-wallet", "Generate new wallet and save it to or
.wallet by default", ""); + const command_line::arg_descriptor arg_derive_custom_seed("derive_custom_seed", "Derive seed phrase from custom 24-words secret(advanced option, do it on your own risk)", ""); const command_line::arg_descriptor arg_generate_new_auditable_wallet ("generate-new-auditable-wallet", "Generate new auditable wallet and store it to ", ""); const command_line::arg_descriptor arg_daemon_address ("daemon-address", "Use daemon instance at :", ""); const command_line::arg_descriptor arg_daemon_host ("daemon-host", "Use daemon instance at host instead of localhost", ""); @@ -2870,6 +2871,266 @@ bool search_for_wallet_file(const std::wstring &search_here/*, const std::string return false; } +int custom_seed_builder() +{ + success_msg_writer() << + "**********************************************************************\n" << + "This is an experimental tool that helps you create a custom seed phrase \n" + "based on your own 24 words. It can be extremely unsafe, so only use it \n" + "if you're confident in what you're doing.\n" + "**********************************************************************"; + + success_msg_writer() << "Please enter 24 words that you want to use as base for the seed:"; + std::string seed_24; + std::getline(std::cin, seed_24); + + + std::list words; + std::string trimed_seed_24 = epee::string_tools::trim(seed_24); + boost::split(words, trimed_seed_24, boost::is_space(), boost::token_compress_on); + seed_24 = boost::algorithm::join(words, " "); + + std::string passphrase; + success_msg_writer() << "Please enter seed passphrase(it's highly recommended to use passphrase for custom seed):"; + std::getline(std::cin, passphrase); + if (passphrase.empty()) + { + success_msg_writer() << "Using unsecured seed(no passphrase)"; + } + else + { + std::string passphrase_confirmation; + success_msg_writer() << "Please confirm passphrase:"; + std::getline(std::cin, passphrase_confirmation); + if (passphrase_confirmation != passphrase) + { + success_msg_writer() << "Passphrase mismatched, try again"; + return EXIT_FAILURE; + } + } + + account_base acc; + acc.generate(); + std::string pass_protected_or_not = ""; + std::vector binary_from_seed = tools::mnemonic_encoding::text2binary(seed_24); + std::vector processed_binary_from_seed = binary_from_seed; + if (!passphrase.empty()) + { + //encrypt seed phrase binary data + account_base::crypt_with_pass(&binary_from_seed[0], binary_from_seed.size(), &processed_binary_from_seed[0], passphrase); + pass_protected_or_not = "(secured with passphrase)"; + } + { + pass_protected_or_not = "(!without passphrase!)"; + } + const std::string new_seed = acc.get_seed_phrase(passphrase, processed_binary_from_seed); + + success_msg_writer() << "Here is your seed" << pass_protected_or_not << "\n " << new_seed; + return EXIT_SUCCESS; +} + +int seed_doctor() +{ + success_msg_writer() << + "**********************************************************************\n" << + "This is experimental tool that might help you to recover your wallet's corrupted seed phrase. \n" << + "It might help to sort out only trivial situations where one word was written wrong or \n" << + "two word was written in wrong order\n" << + "**********************************************************************"; + + + success_msg_writer() << "Please enter problematic seed phrase:"; + std::string seed; + std::getline(std::cin, seed); + + success_msg_writer() << "Please enter wallet address if you have it(press enter if you don't know it):"; + std::string address; + std::getline(std::cin, address); + address = epee::string_tools::trim(address); + + std::string passphrase; + bool check_summ_available = false; + + //cut the last timestamp word from restore_dats + std::vector words; + boost::split(words, seed, boost::is_space()); + + std::set failed_words; + size_t i = 0; + //let's validate each word + for (const auto& w : words) + { + if (!tools::mnemonic_encoding::valid_word(w)) + { + success_msg_writer() << "Word " << i << " '" << w << "' is invalid, attempting to restore it"; + failed_words.insert(i); + } + i++; + } + + if (words.size() == SEED_PHRASE_V1_WORDS_COUNT) + { + // 24 seed words + one timestamp word = 25 total + success_msg_writer() << "SEED_PHRASE_V1_WORDS_COUNT, checksum is unavailable"; + if (address.empty()) + { + success_msg_writer() << "With SEED_PHRASE_V1_WORDS_COUNT and address unknown it's not enough information to recover it, sorry"; + return EXIT_FAILURE; + } + } + else if (words.size() == SEED_PHRASE_V2_WORDS_COUNT) + { + // 24 seed words + one timestamp word + one flags & checksum = 26 total + + success_msg_writer() << "SEED_PHRASE_V2_WORDS_COUNT, checksum is available"; + check_summ_available = true; + } + else if (words.size() == 24) + { + //seed only with no date, add date to it + std::string zero_word = tools::mnemonic_encoding::word_by_num(0); //zero_word is likely to be "like" + words.push_back(zero_word); // + } + else + { + success_msg_writer() << "Impossible to recover something with " << words.size() << " words only"; + return EXIT_FAILURE; + } + + bool pass_protected = false; + bool success = account_base::is_seed_password_protected(seed, pass_protected); + success_msg_writer() << "SECURED_SEED: " << (pass_protected ? "true" : "false"); + + if (pass_protected) + { + success_msg_writer() << "Please enter seed passphrase(if passphrase is wrong then chances to correct seed recovery is nearly zero):"; + std::getline(std::cin, passphrase); + } + + size_t global_candidates_count = 0; + + auto brute_force_func = [&](size_t word_index) -> bool { + const map& all_words = tools::mnemonic_encoding::get_words_map(); + success_msg_writer() << "Brute forcing word " << word_index << " ...."; + size_t candidates_count = 0; + std::vector words_local = words; + + for (auto it = all_words.begin(); it != all_words.end(); it++) + { + words_local[word_index] = it->first; + + std::string result = boost::algorithm::join(words_local, " "); + account_base acc; + bool r = acc.restore_from_seed_phrase(result, passphrase); + if (r) + { + if (!address.empty()) + { + //check against address + if (acc.get_public_address_str() == address) + { + success_msg_writer(true) << "!!!SUCCESS!!!"; + success_msg_writer() << "Seed recovered, please write down recovered seed and use it to restore the wallet:\n" << result; + return true; + } + } + else + { + success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str(); + candidates_count++; + } + } + } + global_candidates_count += candidates_count; + return false; + }; + + + auto swap_func = [&](size_t word_index) -> bool { + size_t candidates_count = 0; + std::vector words_local = words; + + std::string tmp = words_local[word_index]; + words_local[word_index] = words_local[word_index + 1]; + words_local[word_index + 1] = tmp; + std::string result = boost::algorithm::join(words_local, " "); + account_base acc; + bool r = acc.restore_from_seed_phrase(result, passphrase); + if (r) + { + if (!address.empty()) + { + //check against address + if (acc.get_public_address_str() == address) + { + success_msg_writer(true) << "!!!SUCCESS!!!"; + success_msg_writer() << "Seed recovered, please write down recovered seed and use it to restore the wallet:\n" << result; + return true; + } + } + else + { + success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str(); + candidates_count++; + } + } + global_candidates_count += candidates_count; + return false; + }; + + + if (failed_words.size()) + { + if (failed_words.size() > 1) + { + success_msg_writer() << "Restoring more then 1 broken words not implemented yet, sorry"; + return EXIT_FAILURE; + } + if (!check_summ_available && address.empty()) + { + success_msg_writer() << "No address and no checksum, recovery is impossible, sorry"; + return EXIT_FAILURE; + } + + size_t broken_word_index = *failed_words.begin(); + bool r = brute_force_func(broken_word_index); + success_msg_writer() << "Brute forcing finished, " << global_candidates_count << " potential candidates found"; + return r ? EXIT_SUCCESS : EXIT_FAILURE; + } + else + { + if (!check_summ_available && address.empty()) + { + success_msg_writer() << "No address and no checksum, recovery is limited only to date reset"; + std::string result = boost::algorithm::join(words, " "); + account_base acc; + bool r = acc.restore_from_seed_phrase(result, passphrase); + success_msg_writer() << "Potential seed candidate:\n" << result << "\nAddress: " << acc.get_public_address_str(); + return EXIT_FAILURE; + } + success_msg_writer() << "Brute forcing all each word"; + for (size_t i = 0; i != words.size() && i != 24; i++) + { + bool r = brute_force_func(i); + if(r) + return EXIT_SUCCESS; + } + } + + success_msg_writer() << "Brute forcing finished, " << global_candidates_count << " potential candidates found"; + + success_msg_writer() << "Swap check..."; + + for (size_t i = 0; i != words.size() && i != 23; i++) + { + bool r = swap_func(i); + if (r) + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} + //---------------------------------------------------------------------------------------------------- #ifdef WIN32 int wmain( int argc, wchar_t* argv_w[ ], wchar_t* envp[ ] ) @@ -2934,7 +3195,10 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_set_timeout); command_line::add_arg(desc_params, arg_voting_config_file); command_line::add_arg(desc_params, arg_no_password_confirmations); - command_line::add_arg(desc_params, command_line::arg_generate_rpc_autodoc); + command_line::add_arg(desc_params, command_line::arg_generate_rpc_autodoc); + command_line::add_arg(desc_params, arg_seed_doctor); + command_line::add_arg(desc_params, arg_derive_custom_seed); + tools::wallet_rpc_server::init_options(desc_params); @@ -3012,6 +3276,16 @@ int main(int argc, char* argv[]) bool offline_mode = command_line::get_arg(vm, arg_offline_mode); + if (command_line::has_arg(vm, arg_seed_doctor)) + { + return seed_doctor(); + } + + if (command_line::has_arg(vm, arg_derive_custom_seed)) + { + return custom_seed_builder(); + } + if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) { // runs wallet as RPC server diff --git a/src/version.h.in b/src/version.h.in index 72619ace..806a15d7 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "0" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 336 +#define PROJECT_VERSION_BUILD_NO 341 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 2c830dd6..69ad2a28 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -817,3 +817,241 @@ bool chain_switching_when_out_spent_in_alt_chain_ref_id::generate(std::vector& events) const +{ + /* Test idea: check chain switching rules. + Rules before and after HF4 for PoW blocks are different. There're only PoW + blocks in the test situation. If the last blocks contain transactions (non + empty blocks), then the chain with the largest this_block_tx_fee_median on its + head becomes the main. + 0 10 11 21 22 + (0 ) - ... - (0r) - (1 ) - ... - (1r) - (2 ) + | main | \ + | | [tx_0] + | | + | | main + \ - (1a) \ - (2a) + \ + [tx_1] + Chain with head blk_1 versus chain with head blk_1a: chain with head blk_1 + is the main, because blocks 1, 1a are empty. + Chain with head blk_2 versus chain with head blk_2a: chain with head blk_2a + is the main, because blocks 2, 2a aren't empty and the fee of tx_1 is larger + than the fee of tx_0. + */ + + bool success{}; + bool hf4_active{}; + std::vector sources{}; + std::vector destinations{}; + transaction tx_0{}, tx_1{}; + uint64_t tx_version{}; + crypto::hash top_block{}; + + GENERATE_ACCOUNT(miner); + + MAKE_GENESIS_BLOCK(events, + blk_0, + miner, + test_core_time::get_time()); + + DO_CALLBACK(events, "configure_core"); + + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner, + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner); + MAKE_NEXT_BLOCK(events, blk_1a, blk_0r, miner); + + /* It is decided which chain will be the main: with the head blk_1 or with the + head blk_1a. + 0 10 11 + / - (1 ) + | + (0 ) - ... - (0r) + | + \ - (1a) + */ + + CHECK_AND_ASSERT_EQ(is_pos_block(blk_1), false); + CHECK_AND_ASSERT_EQ(is_pos_block(blk_1a), false); + CHECK_AND_ASSERT_EQ(get_block_height(blk_1), 11); + CHECK_AND_ASSERT_EQ(get_block_height(blk_1), get_block_height(blk_1a)); + + /* Blocks blk_1, blk_1a do not contain transactions (they are empty blocks). + Switching to the alternative chain with head blk_1a will not occur. The main + chain is the chain with the head blk_1. */ + + DO_CALLBACK_PARAMS(events, + "check_top_block", + params_top_block(get_block_height(blk_1), + get_block_hash(blk_1))); + + REWIND_BLOCKS_N(events, blk_1r, blk_1, miner, + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + // Transaction tx_0 is constructed and placed in block blk_2. + + success = fill_tx_sources_and_destinations( + events, + /* head = */ blk_1r, + /* from = */ miner.get_keys(), + /* to = */ miner.get_public_address(), + /* amount = */ MK_TEST_COINS(10), + /* fee = */ m_fee_tx_0_blk_2, + /* nmix = */ 0, + sources, + destinations); + + CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations"); + + tx_version = get_tx_version(get_block_height(blk_1r), + m_hardforks); + + success = construct_tx(miner.get_keys(), + sources, + destinations, + empty_attachment, + tx_0, + tx_version, + 0); + + CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_0"); + + ADD_CUSTOM_EVENT(events, tx_0); + MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner, tx_0); + + sources.clear(); + destinations.clear(); + + // Transaction tx_1 is constructed and placed in block blk_2a. + + tx_version = get_tx_version(get_block_height(blk_1r), + m_hardforks); + + success = fill_tx_sources_and_destinations( + events, + /* head = */ blk_1r, + /* from = */ miner.get_keys(), + /* to = */ miner.get_public_address(), + /* amount = */ MK_TEST_COINS(10), + /* fee = */ m_fee_tx_1_blk_2a, + /* nmix = */ 0, + sources, + destinations); + + CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations"); + + success = construct_tx(miner.get_keys(), + sources, + destinations, + empty_attachment, + tx_1, + tx_version, + 0); + + CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_1"); + + ADD_CUSTOM_EVENT(events, tx_1); + MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner, tx_1); + + /* It is decided which chain will be the main: with the head blk_2 or with the + head blk_2a. + 0 21 22 + / - (2 ) + | \ + | [tx_0] + | + (0 ) - ... - (1r) + | + | + | + \ - (2a) + \ + [tx_1] + */ + + CHECK_AND_ASSERT_EQ(is_pos_block(blk_2), false); + CHECK_AND_ASSERT_EQ(is_pos_block(blk_2a), false); + CHECK_AND_ASSERT_GREATER(m_fee_tx_1_blk_2a, m_fee_tx_0_blk_2); + CHECK_AND_ASSERT_EQ(get_block_height(blk_2), 22); + CHECK_AND_ASSERT_EQ(get_block_height(blk_2), get_block_height(blk_2a)); + + hf4_active = + m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, + get_block_height(blk_2) + 1); + + if (hf4_active) + { + /* With HF4 active, the chain with head blk_2a wins because transaction tx_1 + has a greater fee than transaction tx_0. The main chain is the chain with + the head blk_2a. */ + + DO_CALLBACK(events, "check_after_hf4"); + top_block = get_block_hash(blk_2a); + } + + else + { + /* The chains have the same commulative difficulty. Therefore, with HF4 + inactive, switching to the chain with the blk_2a head will not occur. The + main chain is the chain with the head blk_2. */ + + DO_CALLBACK(events, "check_before_hf4"); + top_block = get_block_hash(blk_2); + } + + DO_CALLBACK_PARAMS(events, + "check_top_block", + params_top_block(/* height = */ 22, top_block)); + + return true; +} + +bool alt_chain_and_block_tx_fee_median::check_after_hf4( + currency::core& c, + size_t ev_index, + const std::vector& events) +{ + block_extended_info bei{}; + const uint64_t height_block{22}; + + CHECK_AND_ASSERT_EQ( + m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, + height_block), + true); + + c.get_blockchain_storage().get_block_extended_info_by_height(height_block, + bei); + CHECK_AND_ASSERT_EQ(bei.this_block_tx_fee_median, m_fee_tx_1_blk_2a); + + return true; +} + +bool alt_chain_and_block_tx_fee_median::check_before_hf4( + currency::core& c, + size_t ev_index, + const std::vector& events) +{ + block_extended_info bei{}; + const uint64_t height_block{22}; + + CHECK_AND_ASSERT_EQ( + m_hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, + height_block), + false); + + c.get_blockchain_storage().get_block_extended_info_by_height(height_block, + bei); + CHECK_AND_ASSERT_EQ(bei.this_block_tx_fee_median, m_fee_tx_0_blk_2); + + return true; +} diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index f19e99b9..3d3b011b 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -82,3 +82,21 @@ struct chain_switching_when_out_spent_in_alt_chain_ref_id : public test_chain_un { bool generate(std::vector& events) const; }; + +struct alt_chain_and_block_tx_fee_median : public test_chain_unit_enchanced +{ + alt_chain_and_block_tx_fee_median(); + + bool generate(std::vector& events) const; + bool check_after_hf4(currency::core& c, + size_t ev_index, + const std::vector& events); + + bool check_before_hf4(currency::core& c, + size_t ev_index, + const std::vector& events); + +private: + const uint64_t m_fee_tx_0_blk_2{TESTS_DEFAULT_FEE}; + const uint64_t m_fee_tx_1_blk_2a{2 * m_fee_tx_0_blk_2}; +}; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 8fd3b40d..d58568c3 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1814,6 +1814,48 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s return construct_tx(keys_from, sources, destinations, empty_attachment, tx, tx_version, 0); } +bool construct_tx(const account_keys& sender_account_keys, + const std::vector& sources, + const std::vector& destinations, + const std::vector& extra, + const std::vector& attachments, + transaction& tx, + uint64_t tx_version, + crypto::secret_key& one_time_secret_key, + uint64_t unlock_time, + uint64_t expiration_time, + uint8_t tx_outs_attr, + bool shuffle, + uint64_t flags, + uint64_t explicit_consolidated_tx_fee, + tx_generation_context& gen_context) +{ + // extra copy operation, but creating transaction is not sensitive to this + finalize_tx_param ftp {}; + ftp.tx_version = tx_version; + ftp.sources = sources; + ftp.prepared_destinations = destinations; + ftp.extra = extra; + ftp.attachments = attachments; + ftp.unlock_time = unlock_time; + // ftp.crypt_address = crypt_destination_addr; + ftp.expiration_time = expiration_time; + ftp.tx_outs_attr = tx_outs_attr; + ftp.shuffle = shuffle; + ftp.flags = flags; + ftp.mode_separate_fee = explicit_consolidated_tx_fee; + + finalized_tx ft = AUTO_VAL_INIT(ft); + ft.tx = tx; + ft.one_time_key = one_time_secret_key; + ftp.gen_context = gen_context; // ftp, not ft here, this is UGLY -- sowle + bool r = construct_tx(sender_account_keys, ftp, ft); + tx = ft.tx; + one_time_secret_key = ft.one_time_key; + gen_context = ft.ftp.gen_context; + return r; +} + uint64_t get_balance(const currency::account_keys& addr, const std::vector& blockchain, const map_hash2tx_t& mtx, bool dbg_log) { uint64_t res = 0; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 58500577..c12a8fb6 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -673,6 +673,22 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s const currency::account_keys& keys_from, const currency::account_public_address& addr_to, uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx, bool use_ref_by_id = false); +bool construct_tx(const currency::account_keys& sender_account_keys, + const std::vector& sources, + const std::vector& destinations, + const std::vector& extra, + const std::vector& attachments, + currency::transaction& tx, + uint64_t tx_version, + crypto::secret_key& one_time_secret_key, + uint64_t unlock_time, + uint64_t expiration_time, + uint8_t tx_outs_attr, + bool shuffle, + uint64_t flags, + uint64_t explicit_consolidated_tx_fee, + currency::tx_generation_context& gen_context); + void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs); bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); bool fill_tx_sources(std::vector& sources, const std::vector& events, diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 7371e1a7..5e8102ce 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1140,7 +1140,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_HF(alt_blocks_with_the_same_txs, "3-*"); GENERATE_AND_PLAY_HF(chain_switching_when_out_spent_in_alt_chain_mixin, "3-*"); GENERATE_AND_PLAY_HF(chain_switching_when_out_spent_in_alt_chain_ref_id, "3-*"); - + GENERATE_AND_PLAY_HF(alt_chain_and_block_tx_fee_median, "3-*"); // miscellaneous tests GENERATE_AND_PLAY(test_blockchain_vs_spent_keyimges); @@ -1291,15 +1291,16 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(asset_depoyment_and_few_zc_utxos); GENERATE_AND_PLAY_HF(assets_and_pos_mining, "4-*"); GENERATE_AND_PLAY_HF(asset_emission_and_unconfirmed_balance, "4-*"); + GENERATE_AND_PLAY_HF(asset_operation_in_consolidated_tx, "4-*"); + GENERATE_AND_PLAY_HF(asset_operation_and_hardfork_checks, "4-*"); GENERATE_AND_PLAY_HF(eth_signed_asset_basics, "4-*"); // TODO GENERATE_AND_PLAY_HF(eth_signed_asset_via_rpc, "4-*"); - + GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*"); GENERATE_AND_PLAY_HF(attachment_isolation_test, "4-*"); - // GENERATE_AND_PLAY(gen_block_reward); // END OF TESTS */ diff --git a/tests/core_tests/hard_fork_4.cpp b/tests/core_tests/hard_fork_4.cpp index 396ac2ec..a87a1479 100644 --- a/tests/core_tests/hard_fork_4.cpp +++ b/tests/core_tests/hard_fork_4.cpp @@ -7,51 +7,7 @@ using namespace currency; -namespace currency -{ - bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, - const std::vector& destinations, - const std::vector& extra, - const std::vector& attachments, - transaction& tx, - uint64_t tx_version, - crypto::secret_key& one_time_secret_key, - uint64_t unlock_time, - uint64_t expiration_time, - uint8_t tx_outs_attr, - bool shuffle, - uint64_t flags, - uint64_t explicit_consolidated_tx_fee, - tx_generation_context& gen_context) - { - //extra copy operation, but creating transaction is not sensitive to this - finalize_tx_param ftp{}; - ftp.tx_version = tx_version; - ftp.sources = sources; - ftp.prepared_destinations = destinations; - ftp.extra = extra; - ftp.attachments = attachments; - ftp.unlock_time = unlock_time; - // ftp.crypt_address = crypt_destination_addr; - ftp.expiration_time = expiration_time; - ftp.tx_outs_attr = tx_outs_attr; - ftp.shuffle = shuffle; - ftp.flags = flags; - ftp.mode_separate_fee = explicit_consolidated_tx_fee; - - finalized_tx ft = AUTO_VAL_INIT(ft); - ft.tx = tx; - ft.one_time_key = one_time_secret_key; - ftp.gen_context = gen_context; // ftp, not ft here, this is UGLY -- sowle - bool r = construct_tx(sender_account_keys, ftp, ft); - tx = ft.tx; - one_time_secret_key = ft.one_time_key; - gen_context = ft.ftp.gen_context; - return r; - } -} // namespace currency - -void add_flags_to_all_destination_entries(const uint64_t flags, std::vector& destinations) +static void add_flags_to_all_destination_entries(const uint64_t flags, std::vector& destinations) { for(auto& de : destinations) de.flags |= flags; diff --git a/tests/core_tests/multiassets_test.cpp b/tests/core_tests/multiassets_test.cpp index 23326daa..3b16394f 100644 --- a/tests/core_tests/multiassets_test.cpp +++ b/tests/core_tests/multiassets_test.cpp @@ -892,6 +892,445 @@ bool asset_emission_and_unconfirmed_balance::c1(currency::core& c, size_t ev_ind //------------------------------------------------------------------------------ +asset_operation_and_hardfork_checks::asset_operation_and_hardfork_checks() +{ + m_adb_hello.total_max_supply = 1'000'000'000'000'000'000; + m_adb_hello.current_supply = 1'000'000'000'000'000'000; + m_adb_hello.ticker = "HLO"; + m_adb_hello.full_name = "HELLO_WORLD"; + m_adb_hello.meta_info = "Hello, world!"; + m_adb_hello.hidden_supply = false; + + m_ado_hello.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER; + m_ado_hello.opt_asset_id = currency::null_pkey; + + m_adb_bye.total_max_supply = 1'000'000'000'000'000'000; + m_adb_bye.current_supply = 1'000'000'000'000'000'000; + m_adb_bye.ticker = "BYE"; + m_adb_bye.full_name = "BYE_WORLD"; + m_adb_bye.meta_info = "Bye, world!"; + m_adb_bye.hidden_supply = false; + + m_ado_bye.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER; + m_ado_hello.opt_asset_id = currency::null_pkey; + + REGISTER_CALLBACK_METHOD(asset_operation_and_hardfork_checks, c1); + REGISTER_CALLBACK_METHOD(asset_operation_and_hardfork_checks, c2); +} + +bool asset_operation_and_hardfork_checks::generate( + std::vector& events) const +{ + /* + 0 10 11 21 22 + ( 0) - ... - (0r) - ( 1) - ... - (1r) - ( 2) + \ \ + [tx_0] [tx_1] + */ + + bool success{false}; + std::vector sources{}; + std::vector destinations{}; + transaction tx_0{}, tx_1{}, tx_2{}, tx_3{}, tx_4{}; + uint64_t tx_version{}; + crypto::secret_key stub{}; + + m_accounts.resize(2); + account_base& miner{m_accounts[MINER_ACC_IDX]}; + miner.generate(); + account_base& alice{m_accounts[ALICE_ACC_IDX]}; + alice.generate(); + + m_adb_hello.owner = alice.get_public_address().spend_public_key; + m_ado_hello.descriptor = m_adb_hello; + + m_adb_bye.owner = alice.get_public_address().spend_public_key; + m_ado_bye.descriptor = m_adb_bye; + + MAKE_GENESIS_BLOCK(events, + blk_0, + miner, + test_core_time::get_time()); + + DO_CALLBACK(events, "configure_core"); + + REWIND_BLOCKS_N_WITH_TIME(events, + blk_0r, + blk_0, + miner, + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + success = fill_tx_sources_and_destinations(events, + /* head = */ blk_0r, + /* from = */ miner.get_keys(), + /* to = */ alice.get_public_address(), + /* amount = */ MK_TEST_COINS(12), + /* fee = */ TESTS_DEFAULT_FEE, + /* nmix = */ 0, + sources, + destinations); + + CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations"); + + tx_version = get_tx_version(get_block_height(blk_0r), + m_hardforks); + + success = construct_tx(miner.get_keys(), + sources, + destinations, + empty_attachment, + tx_0, + tx_version, + 0); + + CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_0"); + + ADD_CUSTOM_EVENT(events, tx_0); + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner, tx_0); + + REWIND_BLOCKS_N_WITH_TIME(events, + blk_1r, + blk_1, + miner, + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + sources.clear(); + destinations.clear(); + + success = fill_tx_sources_and_destinations(events, + /* head = */ blk_1r, + /* from = */ alice, + /* to = */ alice, + /* amount = */ MK_TEST_COINS(5), + /* fee = */ TESTS_DEFAULT_FEE, + /* nmix = */ 0, + sources, + destinations); + + CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations"); + + destinations.emplace_back(/* amount = */ 1'000'000'000'000'000'000, + /* to = */ alice.get_public_address(), + /* asset_id = */ currency::null_pkey); + + tx_version = get_tx_version(get_block_height(blk_1r), m_hardforks); + + success = construct_tx(alice.get_keys(), + sources, + destinations, + /* extra = */ {m_ado_hello}, + empty_attachment, + tx_1, + tx_version, + stub, + 0); + + CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_1"); + ADD_CUSTOM_EVENT(events, tx_1); + + MAKE_NEXT_BLOCK_TX1(events, + blk_2, + blk_1r, + alice, + tx_1); + + REWIND_BLOCKS_N_WITH_TIME(events, + blk_2r, + blk_2, + miner, + CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + /* A transaction that has at least 2 registration operation descriptors in its + extra is rejected by the core. */ + + sources.clear(); + destinations.clear(); + + success = fill_tx_sources_and_destinations(events, + /* head = */ blk_2r, + /* from = */ alice, + /* to = */ alice, + /* amount = */ MK_TEST_COINS(2), + /* fee = */ TESTS_DEFAULT_FEE, + /* nmix = */ 0, + sources, + destinations); + + CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations"); + + tx_version = get_tx_version(get_block_height(blk_2r), + m_hardforks); + + success = construct_tx(alice.get_keys(), + sources, + destinations, + /* extra = */ {m_ado_hello, m_ado_hello}, + empty_attachment, + tx_2, + tx_version, + stub, + 0); + + CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_2"); + + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_2); + + sources.clear(); + destinations.clear(); + + /* A transaction that contains a registration operation descriptor in its + attachement, but extra is empty, is valid, but doesn't register the asset. The + fact that the asset is not registered is checked in the assertions in the + callback с2. */ + + success = fill_tx_sources_and_destinations(events, + /* head = */ blk_2r, + /* from = */ alice, + /* to = */ alice, + /* amount = */ MK_TEST_COINS(2), + /* fee = */ TESTS_DEFAULT_FEE, + /* nmix = */ 0, + sources, + destinations); + + CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations"); + + tx_version = get_tx_version(get_block_height(blk_2r), + m_hardforks); + + success = construct_tx(alice.get_keys(), + sources, + destinations, + /* attachments = */ {m_ado_bye}, + tx_3, + tx_version, + 0); + + CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_3"); + + ADD_CUSTOM_EVENT(events, tx_3); + DO_CALLBACK(events, "c2"); + + sources.clear(); + destinations.clear(); + + /* A transaction that contains a registration operation descriptor in its + attachement, but extra is empty, is valid, but doesn't register the asset. In + this case a different definition of the function construct_tx is used. */ + + success = fill_tx_sources_and_destinations(events, + /* head = */ blk_2r, + /* from = */ alice, + /* to = */ alice, + /* amount = */ MK_TEST_COINS(2), + /* fee = */ TESTS_DEFAULT_FEE, + /* nmix = */ 0, + sources, + destinations); + + CHECK_AND_ASSERT_MES(success, false, "fail to fill sources, destinations"); + + tx_version = get_tx_version(get_block_height(blk_2r), + m_hardforks); + + success = construct_tx(alice.get_keys(), + sources, + destinations, + empty_extra, + /* attachments = */ {m_ado_bye}, + tx_4, + tx_version, + stub, + 0); + + CHECK_AND_ASSERT_MES(success, false, "fail to construct tx_4"); + + ADD_CUSTOM_EVENT(events, tx_4); + DO_CALLBACK(events, "c2"); + DO_CALLBACK(events, "c1"); + + return true; +} + +bool asset_operation_and_hardfork_checks::c1( + currency::core& c, + size_t ev_index, + const std::vector& events) +{ + std::shared_ptr wallet{ + init_playtime_test_wallet_t(events, c, ALICE_ACC_IDX) + }; + + wallet->refresh(); + + crypto::point_t asset_id_point{}; + crypto::public_key asset_id_public_key{}; + currency::get_or_calculate_asset_id(m_ado_hello, + &asset_id_point, + &asset_id_public_key); + + CHECK_AND_ASSERT_MES( + check_balance_via_wallet(*wallet, + /* name = */ "Alice", + /* expected_total = */ 1'000'000'000'000'000'000, + /* expected_mined = */ 2 * COIN, + /* expected_unlocked = */ 1'000'000'000'000'000'000, + /* expected_awaiting_in = */ INVALID_BALANCE_VAL, + /* expected_awaiting_out = */ INVALID_BALANCE_VAL, + asset_id_public_key), + false, + "balance check failed"); + + return true; +} + +bool asset_operation_and_hardfork_checks::c2( + currency::core& c, + size_t ev_index, + const std::vector& events) +{ + crypto::point_t asset_id_pt{}; + crypto::public_key asset_id{}; + currency::asset_descriptor_base stub{}; + + CHECK_AND_ASSERT_MES( + get_or_calculate_asset_id(m_ado_bye, + &asset_id_pt, + &asset_id), + false, + "fail to calculate asset id"); + + CHECK_AND_ASSERT_MES( + !c.get_blockchain_storage().get_asset_info(asset_id, + stub), + false, + "unregistered asset has info"); + + return true; +} + +asset_operation_in_consolidated_tx::asset_operation_in_consolidated_tx() +{ + m_adb_alice_currency.total_max_supply = 1'000'000'000'000'000'000; + m_adb_alice_currency.current_supply = 1'000'000'000'000'000'000; + m_adb_alice_currency.ticker = "ALC"; + m_adb_alice_currency.full_name = "ALICE"; + m_adb_alice_currency.meta_info = "Currency created by Alice"; + m_adb_alice_currency.hidden_supply = false; + + m_ado_alice_currency.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER; + m_ado_alice_currency.opt_asset_id = currency::null_pkey; + + REGISTER_CALLBACK_METHOD(asset_operation_in_consolidated_tx, assert_balances); + REGISTER_CALLBACK_METHOD(asset_operation_in_consolidated_tx, assert_alice_currency_not_registered); +} + +bool asset_operation_in_consolidated_tx::generate(std::vector& events) const +{ + // Test idea: make sure that the core rule prohibiting operations with assets in TX_FLAG_SIGNATURE_MODE_SEPARATE transactions works. + bool success {}; + transaction tx_2 {}; + uint64_t tx_version {}; + crypto::secret_key one_time {}; + tx_generation_context context_tx_2 {}; + GENERATE_ACCOUNT(miner); + GENERATE_ACCOUNT(alice); + GENERATE_ACCOUNT(bob); + + m_accounts.push_back(miner); + m_accounts.push_back(alice); + m_accounts.push_back(bob); + m_adb_alice_currency.owner = m_accounts.at(ALICE_ACC_IDX).get_public_address().spend_public_key; + m_ado_alice_currency.descriptor = m_adb_alice_currency; + + MAKE_GENESIS_BLOCK(events, blk_0, miner, test_core_time::get_time()); + DO_CALLBACK(events, "configure_core"); + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + MAKE_TX(events, tx_0, miner, bob, MK_TEST_COINS(10), blk_0r); + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner, tx_0); + REWIND_BLOCKS_N(events, blk_1r, blk_1, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + MAKE_TX(events, tx_1, miner, alice, MK_TEST_COINS(10), blk_1r); + MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner, tx_1); + REWIND_BLOCKS_N(events, blk_2r, blk_2, miner, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + // Miner sent 10 coins to Alice, 10 coins to Bob. + DO_CALLBACK(events, "assert_balances"); + + { + std::vector sources {}; + std::vector destinations {}; + + success = fill_tx_sources(sources, events, blk_2r, alice.get_keys(), MK_TEST_COINS(5), 1); + CHECK_AND_ASSERT_MES(success, false, "failed to fill transaction sources on step 1"); + destinations.emplace_back(MK_TEST_COINS(5), bob.get_public_address()); + destinations.emplace_back(MK_TEST_COINS(/* 10 - 5 - 1 = */ 4), alice.get_public_address()); + tx_version = get_tx_version(get_block_height(blk_2r), m_hardforks); + success = construct_tx(alice.get_keys(), sources, destinations, empty_extra, empty_attachment, tx_2, tx_version, one_time, 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE, TESTS_DEFAULT_FEE, + context_tx_2); + CHECK_AND_ASSERT_MES(success, false, "failed to construct transaction tx_2 on step 1"); + } + + // Transaction tx_2 hasn't been constructed completely yet. Core rejects tx_2. + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_2); + + { + std::vector sources {}; + std::vector destinations {}; + + success = fill_tx_sources(sources, events, blk_2r, bob.get_keys(), MK_TEST_COINS(5), 0); + CHECK_AND_ASSERT_MES(success, false, "failed to fill transaction sources on step 2"); + for(tx_source_entry& source : sources) + { + source.separately_signed_tx_complete = true; + } + destinations.emplace_back(MK_TEST_COINS(5), alice.get_public_address()); + destinations.emplace_back(MK_TEST_COINS(/* 10 - 5 - 0 = */ 5), bob.get_public_address()); + destinations.emplace_back(m_adb_alice_currency.current_supply, alice.get_public_address(), null_pkey); + tx_version = get_tx_version(get_block_height(blk_2r), m_hardforks); + success = construct_tx(bob.get_keys(), sources, destinations, { m_ado_alice_currency }, empty_attachment, tx_2, tx_version, one_time, 0, 0, 0, true, TX_FLAG_SIGNATURE_MODE_SEPARATE, + /* fee = */ 0, context_tx_2); + CHECK_AND_ASSERT_MES(success, false, "failed to construct transaction tx_2 on step 2"); + } + + DO_CALLBACK(events, "mark_invalid_tx"); + ADD_CUSTOM_EVENT(events, tx_2); + // Core rejects transaction tx_2. The balances of Alice, Bob haven't changed: Alice has 10 coins, Bob has 10 coins. + DO_CALLBACK(events, "assert_balances"); + // Alice's asset hasn't registered, because transaction tx_2 was rejected. + DO_CALLBACK(events, "assert_alice_currency_not_registered"); + + return true; +} + +bool asset_operation_in_consolidated_tx::assert_balances(currency::core& c, size_t ev_index, const std::vector& events) +{ + std::shared_ptr alice_wallet{init_playtime_test_wallet(events, c, ALICE_ACC_IDX)}; + std::shared_ptr bob_wallet{init_playtime_test_wallet(events, c, BOB_ACC_IDX)}; + + alice_wallet->refresh(); + bob_wallet->refresh(); + + CHECK_AND_ASSERT_EQ(alice_wallet->balance(currency::native_coin_asset_id), MK_TEST_COINS(10)); + CHECK_AND_ASSERT_EQ(bob_wallet->balance(currency::native_coin_asset_id), MK_TEST_COINS(10)); + + return true; +} + +bool asset_operation_in_consolidated_tx::assert_alice_currency_not_registered(currency::core& c, size_t ev_index, const std::vector& events) +{ + crypto::point_t asset_id_point{}; + crypto::public_key asset_id{}; + currency::asset_descriptor_base stub{}; + + CHECK_AND_ASSERT_MES(get_or_calculate_asset_id(m_ado_alice_currency, &asset_id_point, &asset_id), false, "fail to calculate asset id"); + CHECK_AND_ASSERT_MES(!c.get_blockchain_storage().get_asset_info(asset_id, stub), false, "unregistered asset has info"); + + return true; +} + +//------------------------------------------------------------------------------ + eth_signed_asset_basics::eth_signed_asset_basics() { REGISTER_CALLBACK_METHOD(eth_signed_asset_basics, c1); @@ -938,7 +1377,7 @@ std::shared_ptr> construct_eth_sign_cb_helper(T&& cb) bool eth_signed_asset_basics::c1(currency::core& c, size_t ev_index, const std::vector& events) { bool r = false; - + crypto::eth_secret_key eth_sk{}; crypto::eth_public_key eth_pk{}; r = crypto::generate_eth_key_pair(eth_sk, eth_pk); @@ -1020,7 +1459,7 @@ bool eth_signed_asset_basics::c1(currency::core& c, size_t ev_index, const std:: auto signer = construct_eth_sign_cb_helper([&](const crypto::hash& h, const crypto::eth_public_key& asset_owner, crypto::eth_signature& sig)->bool { CHECK_AND_ASSERT_EQ(asset_owner, eth_pk); return crypto::generate_eth_signature(h, eth_sk, sig); - }); + }); // TODO reconsider this ===>> ctp.p_eth_signer = signer.get(); diff --git a/tests/core_tests/multiassets_test.h b/tests/core_tests/multiassets_test.h index bba172fc..2305b594 100644 --- a/tests/core_tests/multiassets_test.h +++ b/tests/core_tests/multiassets_test.h @@ -48,6 +48,39 @@ struct asset_emission_and_unconfirmed_balance : public wallet_test bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; +struct asset_operation_and_hardfork_checks : public wallet_test +{ + public: + asset_operation_and_hardfork_checks(); + + bool generate(std::vector& events) const; + bool c1(currency::core& c, + size_t ev_index, + const std::vector& events); + + bool c2(currency::core& c, + size_t ev_index, + const std::vector& events); + + private: + mutable currency::asset_descriptor_base m_adb_hello{}; + mutable currency::asset_descriptor_operation m_ado_hello{}; + mutable currency::asset_descriptor_base m_adb_bye{}; + mutable currency::asset_descriptor_operation m_ado_bye{}; +}; + +struct asset_operation_in_consolidated_tx : public wallet_test +{ +public: + asset_operation_in_consolidated_tx(); + bool generate(std::vector& events) const; + bool assert_balances(currency::core& c, size_t ev_index, const std::vector& events); + bool assert_alice_currency_not_registered(currency::core& c, size_t ev_index, const std::vector& events); + +private: + mutable currency::asset_descriptor_base m_adb_alice_currency{}; + mutable currency::asset_descriptor_operation m_ado_alice_currency{}; +}; struct eth_signed_asset_basics : public wallet_test { diff --git a/tests/core_tests/pos_block_builder.cpp b/tests/core_tests/pos_block_builder.cpp index 8d394d0e..62561121 100644 --- a/tests/core_tests/pos_block_builder.cpp +++ b/tests/core_tests/pos_block_builder.cpp @@ -169,6 +169,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, uint64_t block_reward_without_fee = 0; m_block_reward = 0; size_t estimated_block_size = m_txs_total_size; + m_block.miner_tx = transaction{}; bool r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, m_block_reward, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use); CHECK_AND_ASSERT_THROW_MES(r, "construct_miner_tx failed"); @@ -177,6 +178,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size, size_t cumulative_size = 0; for (size_t try_count = 0; try_count != 10; ++try_count) { + m_block.miner_tx = transaction{}; r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, reward_receiver_address, stakeholder_address, m_block.miner_tx, block_reward_without_fee, m_block_reward, tx_version, extra_nonce, max_outs, true, pe, &m_miner_tx_tgc, tx_one_time_key_to_use); CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed"); diff --git a/tests/unit_tests/multiassets_test.cpp b/tests/unit_tests/multiassets_test.cpp new file mode 100644 index 00000000..a996acad --- /dev/null +++ b/tests/unit_tests/multiassets_test.cpp @@ -0,0 +1,1314 @@ +// Copyright (c) 2024 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "serialization/serialization.h" +#include "common/crypto_serialization.h" +#include "serialization/keyvalue_serialization.h" // epee key-value serialization +#include "currency_core/currency_basic.h" +#include "currency_core/currency_format_utils.h" +#include "common/boost_serialization_helper.h" +#include "storages/portable_storage_template_helper.h" + +namespace currency +{ + struct asset_descriptor_base_v0; + struct asset_descriptor_operation_v0; +} + +// develop, commit 0c90262e8a1c4e5e5d052f8db84c60a36691414d +struct currency::asset_descriptor_base_v0 +{ + uint64_t total_max_supply = 0; + uint64_t current_supply = 0; + uint8_t decimal_point = 0; + std::string ticker; + std::string full_name; + std::string meta_info; + crypto::public_key owner = currency::null_pkey; // consider premultipling by 1/8 + bool hidden_supply = false; + uint8_t version = 0; + + BEGIN_VERSIONED_SERIALIZE(0, version) + FIELD(total_max_supply) + FIELD(current_supply) + FIELD(decimal_point) + FIELD(ticker) + FIELD(full_name) + FIELD(meta_info) + FIELD(owner) + FIELD(hidden_supply) + END_SERIALIZE() + + BEGIN_BOOST_SERIALIZATION() + BOOST_SERIALIZE(total_max_supply) + BOOST_SERIALIZE(current_supply) + BOOST_SERIALIZE(decimal_point) + BOOST_SERIALIZE(ticker) + BOOST_SERIALIZE(full_name) + BOOST_SERIALIZE(meta_info) + BOOST_SERIALIZE(owner) + BOOST_SERIALIZE(hidden_supply) + END_BOOST_SERIALIZATION() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(total_max_supply) DOC_DSCR("Maximum possible supply for given asset, can't be changed after deployment") DOC_EXMP(1000000000000000000) DOC_END + KV_SERIALIZE(current_supply) DOC_DSCR("Currently emitted supply for given asset (ignored for REGISTER operation)") DOC_EXMP(500000000000000000) DOC_END + KV_SERIALIZE(decimal_point) DOC_DSCR("Decimal point") DOC_EXMP(12) DOC_END + KV_SERIALIZE(ticker) DOC_DSCR("Ticker associated with asset") DOC_EXMP("ZUSD") DOC_END + KV_SERIALIZE(full_name) DOC_DSCR("Full name of the asset") DOC_EXMP("Zano wrapped USD") DOC_END + KV_SERIALIZE(meta_info) DOC_DSCR("Any other information assetiaded with asset in a free form") DOC_EXMP("Stable and private") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used only for EMIT and UPDATE validation, could be changed by transferring asset ownership") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE(hidden_supply) DOC_DSCR("This one reserved for future use, will be documented later") DOC_END + END_KV_SERIALIZE_MAP() + + operator currency::asset_descriptor_base() const + { + currency::asset_descriptor_base asset_descriptor{}; + asset_descriptor.total_max_supply = total_max_supply; + asset_descriptor.current_supply = current_supply; + asset_descriptor.decimal_point = decimal_point; + asset_descriptor.ticker = ticker; + asset_descriptor.full_name = full_name; + asset_descriptor.meta_info = meta_info; + asset_descriptor.owner = owner; + asset_descriptor.hidden_supply = hidden_supply; + asset_descriptor.version = version; + return asset_descriptor; + } +}; + +struct currency::asset_descriptor_operation_v0 +{ + uint8_t operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED; + asset_descriptor_base_v0 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; + + BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_OPERATION_STRUCTURE_VER, verion) + FIELD(operation_type) + FIELD(descriptor) + FIELD(amount_commitment) + END_VERSION_UNDER(1) + FIELD(opt_asset_id) + END_SERIALIZE() + + 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() + + 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 + END_KV_SERIALIZE_MAP() + + operator currency::asset_descriptor_operation() const + { + currency::asset_descriptor_operation operation_descriptor{}; + operation_descriptor.operation_type = operation_type; + operation_descriptor.descriptor = descriptor; + operation_descriptor.amount_commitment = amount_commitment; + operation_descriptor.opt_asset_id = opt_asset_id; + operation_descriptor.verion = verion; + + return operation_descriptor; + } +}; + +BOOST_CLASS_VERSION(currency::asset_descriptor_operation_v0, 1); + +currency::asset_descriptor_base get_asset_descriptor_for_test( + const crypto::public_key& owner = currency::null_pkey) +{ + currency::asset_descriptor_base descriptor_base{}; + descriptor_base.total_max_supply = 100; + descriptor_base.current_supply = 50; + descriptor_base.decimal_point = 0; + descriptor_base.ticker = "HLO"; + descriptor_base.full_name = "HELLO_WORLD"; + descriptor_base.meta_info = "Hello, world!"; + descriptor_base.owner = owner; + descriptor_base.hidden_supply = false; + descriptor_base.version = 1; + + return descriptor_base; +} + +currency::asset_descriptor_base_v0 get_asset_descriptor_v0_for_test( + const crypto::public_key& owner = currency::null_pkey) +{ + currency::asset_descriptor_base_v0 descriptor_base{}; + descriptor_base.total_max_supply = 100; + descriptor_base.current_supply = 50; + descriptor_base.decimal_point = 0; + descriptor_base.ticker = "HLO"; + descriptor_base.full_name = "HELLO_WORLD"; + descriptor_base.meta_info = "Hello, world!"; + descriptor_base.owner = owner; + descriptor_base.hidden_supply = false; + + return descriptor_base; +} + +currency::asset_descriptor_operation get_asset_descriptor_operation_for_test( + currency::asset_descriptor_base asset_descriptor, + std::uint8_t operation = ASSET_DESCRIPTOR_OPERATION_UNDEFINED +) +{ + currency::asset_descriptor_operation descriptor_operation{}; + descriptor_operation.operation_type = operation; + descriptor_operation.descriptor = asset_descriptor; + descriptor_operation.amount_commitment = currency::null_pkey; + + return descriptor_operation; +} + +currency::asset_descriptor_operation_v0 +get_asset_descriptor_operation_v0_for_test( + currency::asset_descriptor_base_v0 asset_descriptor, + std::uint8_t operation = ASSET_DESCRIPTOR_OPERATION_UNDEFINED +) +{ + currency::asset_descriptor_operation_v0 descriptor_operation{}; + descriptor_operation.operation_type = operation; + descriptor_operation.descriptor = asset_descriptor; + descriptor_operation.amount_commitment = currency::null_pkey; + + return descriptor_operation; +} + +TEST(multiassets, get_or_calculate_asset_id_register) +{ + bool success{false}; + + crypto::point_t pt_public_key{}; + success = pt_public_key.from_string( + "cf93bead4d2a8d6d174c1752237b2e5208a594b59618683ee50ef209cf1efb19"); + + ASSERT_TRUE(success); + + crypto::public_key owner_public_key{}; + pt_public_key.to_public_key(owner_public_key); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + const auto asset_descriptor{get_asset_descriptor_for_test(owner_public_key)}; + + const auto asset_operation_descriptor{ + get_asset_descriptor_operation_for_test(asset_descriptor, + ASSET_DESCRIPTOR_OPERATION_REGISTER)}; + + success = currency::get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "6f46324faae448b9e3b96dac94da17be6ab7eaaba398de86d8743042c98bace0"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_emit) +{ + bool success{false}; + + crypto::point_t pt_public_key{}; + success = pt_public_key.from_string( + "edab571c4be9eabfea5e7883036d744c097382eb6f739a914db06f72ba35099d"); + + ASSERT_TRUE(success); + + crypto::public_key owner_public_key{}; + pt_public_key.to_public_key(owner_public_key); + + const auto asset_descriptor{ + get_asset_descriptor_for_test(owner_public_key)}; + + auto asset_operation_descriptor{ + get_asset_descriptor_operation_for_test( + asset_descriptor, ASSET_DESCRIPTOR_OPERATION_REGISTER)}; + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "49a3d6652aaa0b3b77292c534e91ff80de9120aeb6fc1c5edc728047437d667e"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); + + asset_operation_descriptor.opt_asset_id = calculated_asset_id_key; + asset_operation_descriptor.operation_type = ASSET_DESCRIPTOR_OPERATION_EMIT; + + success = get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_update) +{ + bool success{false}; + + crypto::point_t pt_public_key{}; + success = pt_public_key.from_string( + "8cb6349f51da6599feeae7c0077293436eb6a5000f0e6e706e77886bb540e2c1"); + + ASSERT_TRUE(success); + + crypto::public_key owner_public_key{}; + pt_public_key.to_public_key(owner_public_key); + + const auto asset_descriptor{ + get_asset_descriptor_for_test(owner_public_key)}; + + auto asset_operation_descriptor{ + get_asset_descriptor_operation_for_test( + asset_descriptor, ASSET_DESCRIPTOR_OPERATION_REGISTER)}; + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "c371f60dd8333298c6aa746b71e1e20527b1ff5e1bed4ea9b5f592fadf90ed6b"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); + + asset_operation_descriptor.opt_asset_id = calculated_asset_id_key; + asset_operation_descriptor.operation_type = ASSET_DESCRIPTOR_OPERATION_UPDATE; + + success = get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_public_burn) +{ + bool success{false}; + + crypto::point_t pt_public_key{}; + success = pt_public_key.from_string( + "0c408cf8b7fb808f40593d6eb75890e2ab3d0ccdc7014a7fc6b6ab05163be060"); + + ASSERT_TRUE(success); + + crypto::public_key owner_public_key{}; + pt_public_key.to_public_key(owner_public_key); + + const auto asset_descriptor{ + get_asset_descriptor_for_test(owner_public_key)}; + + auto asset_operation_descriptor{ + get_asset_descriptor_operation_for_test( + asset_descriptor, ASSET_DESCRIPTOR_OPERATION_REGISTER)}; + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "54f3f72c72e5b014ad2b2b9001acef954fe82dd3ed56a38cd9ddc5db57673f8f"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); + + asset_operation_descriptor.opt_asset_id = calculated_asset_id_key; + asset_operation_descriptor.operation_type = + ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN; + + success = get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_undefined) +{ + bool success{false}; + + crypto::point_t pt_public_key{}; + success = pt_public_key.from_string( + "e91b9a73292d6ea46fb3d4f4cc79c34bfb7d14c2e684e58093a2471c92e51c16"); + + ASSERT_TRUE(success); + + crypto::public_key owner_public_key{}; + pt_public_key.to_public_key(owner_public_key); + + const auto asset_descriptor{ + get_asset_descriptor_for_test(owner_public_key)}; + + auto asset_operation_descriptor{ + get_asset_descriptor_operation_for_test( + asset_descriptor, ASSET_DESCRIPTOR_OPERATION_REGISTER)}; + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "979eb706ace2eb83f9125658b23fb352208480cb3b90c43e2df0d298f9754ebc"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); + + asset_operation_descriptor.opt_asset_id = calculated_asset_id_key; + asset_operation_descriptor.operation_type = + ASSET_DESCRIPTOR_OPERATION_UNDEFINED; + + success = get_or_calculate_asset_id(asset_operation_descriptor, + &calculated_asset_id_pt, + &calculated_asset_id_key); + + ASSERT_FALSE(success); +} + +TEST(multiassets, get_or_calculate_asset_id_register_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x01', '\x01', '\x00', 'd', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '2', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x03', 'H', 'L', 'O', '\x0b', 'H', 'E', + 'L', 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', + '\x0d', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', + 'o', 'r', 'l', 'd', '!', '\xcf', '\x93', '\xbe', '\xad', + 'M', '*', '\x8d', 'm', '\x17', 'L', '\x17', 'R', '#', + '{', '.', 'R', '\x08', '\xa5', '\x94', '\xb5', '\x96', '\x18', + 'h', '>', '\xe5', '\x0e', '\xf2', '\x09', '\xcf', '\x1e', '\xfb', + '\x19', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'o', + 'F', '2', 'O', '\xaa', '\xe4', 'H', '\xb9', '\xe3', '\xb9', + 'm', '\xac', '\x94', '\xda', '\x17', '\xbe', 'j', '\xb7', '\xea', + '\xab', '\xa3', '\x98', '\xde', '\x86', '\xd8', 't', '0', 'B', + '\xc9', '\x8b', '\xac', '\xe0'}; + + success = t_unserializable_object_from_blob(asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "6f46324faae448b9e3b96dac94da17be6ab7eaaba398de86d8743042c98bace0"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_emit_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x01', '\x02', '\x00', 'd', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '2', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x03', 'H', 'L', 'O', '\x0b', 'H', 'E', + 'L', 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', + '\x0d', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', + 'o', 'r', 'l', 'd', '!', '\xed', '\xab', 'W', '\x1c', + 'K', '\xe9', '\xea', '\xbf', '\xea', '^', 'x', '\x83', '\x03', + 'm', 't', 'L', '\x09', 's', '\x82', '\xeb', 'o', 's', + '\x9a', '\x91', 'M', '\xb0', 'o', 'r', '\xba', '5', '\x09', + '\x9d', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'I', + '\xa3', '\xd6', 'e', '*', '\xaa', '\x0b', ';', 'w', ')', + ',', 'S', 'N', '\x91', '\xff', '\x80', '\xde', '\x91', ' ', + '\xae', '\xb6', '\xfc', '\x1c', '^', '\xdc', 'r', '\x80', 'G', + 'C', '}', 'f', '~'}; + + success = t_unserializable_object_from_blob(asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "49a3d6652aaa0b3b77292c534e91ff80de9120aeb6fc1c5edc728047437d667e"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_update_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x01', '\x03', '\x00', 'd', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '2', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x03', 'H', 'L', 'O', '\x0b', 'H', 'E', + 'L', 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', + '\x0d', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', + 'o', 'r', 'l', 'd', '!', '\x8c', '\xb6', '4', '\x9f', + 'Q', '\xda', 'e', '\x99', '\xfe', '\xea', '\xe7', '\xc0', '\x07', + 'r', '\x93', 'C', 'n', '\xb6', '\xa5', '\x00', '\x0f', '\x0e', + 'n', 'p', 'n', 'w', '\x88', 'k', '\xb5', '@', '\xe2', + '\xc1', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc3', + 'q', '\xf6', '\x0d', '\xd8', '3', '2', '\x98', '\xc6', '\xaa', + 't', 'k', 'q', '\xe1', '\xe2', '\x05', '\'', '\xb1', '\xff', + '^', '\x1b', '\xed', 'N', '\xa9', '\xb5', '\xf5', '\x92', '\xfa', + '\xdf', '\x90', '\xed', 'k'}; + + success = t_unserializable_object_from_blob(asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "c371f60dd8333298c6aa746b71e1e20527b1ff5e1bed4ea9b5f592fadf90ed6b"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_public_burn_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x01', '\x04', '\x00', 'd', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '2', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x03', 'H', 'L', 'O', '\x0b', 'H', 'E', + 'L', 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', + '\x0d', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', + 'o', 'r', 'l', 'd', '!', '\x0c', '@', '\x8c', '\xf8', + '\xb7', '\xfb', '\x80', '\x8f', '@', 'Y', '=', 'n', '\xb7', + 'X', '\x90', '\xe2', '\xab', '=', '\x0c', '\xcd', '\xc7', '\x01', + 'J', '\x7f', '\xc6', '\xb6', '\xab', '\x05', '\x16', ';', '\xe0', + '`', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'T', + '\xf3', '\xf7', ',', 'r', '\xe5', '\xb0', '\x14', '\xad', '+', + '+', '\x90', '\x01', '\xac', '\xef', '\x95', 'O', '\xe8', '-', + '\xd3', '\xed', 'V', '\xa3', '\x8c', '\xd9', '\xdd', '\xc5', '\xdb', + 'W', 'g', '?', '\x8f'}; + + success = t_unserializable_object_from_blob(asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "54f3f72c72e5b014ad2b2b9001acef954fe82dd3ed56a38cd9ddc5db57673f8f"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_undefined_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x01', '\x00', '\x00', 'd', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '2', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x03', 'H', 'L', 'O', '\x0b', 'H', 'E', + 'L', 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', + '\x0d', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', + 'o', 'r', 'l', 'd', '!', '\xe9', '\x1b', '\x9a', 's', + ')', '-', 'n', '\xa4', 'o', '\xb3', '\xd4', '\xf4', '\xcc', + 'y', '\xc3', 'K', '\xfb', '}', '\x14', '\xc2', '\xe6', '\x84', + '\xe5', '\x80', '\x93', '\xa2', 'G', '\x1c', '\x92', '\xe5', '\x1c', + '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x97', + '\x9e', '\xb7', '\x06', '\xac', '\xe2', '\xeb', '\x83', '\xf9', '\x12', + 'V', 'X', '\xb2', '?', '\xb3', 'R', ' ', '\x84', '\x80', + '\xcb', ';', '\x90', '\xc4', '>', '-', '\xf0', '\xd2', '\x98', + '\xf9', 'u', 'N', '\xbc'}; + + success = t_unserializable_object_from_blob( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_FALSE(success); +} + +TEST(multiassets, get_or_calculate_asset_id_register_boost_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 's', + 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'a', 't', + 'i', 'o', 'n', ':', ':', 'a', 'r', 'c', 'h', + 'i', 'v', 'e', '\x11', '\x00', '\x04', '\x08', '\x04', '\x08', + '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x01', '\x00', '\x00', '\x00', '\x00', '\x00', 'd', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '2', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x03', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', 'H', 'L', 'O', '\x0b', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'E', 'L', + 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', '\x0d', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'e', + 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', + 'd', '!', '\x00', '\x00', '\x00', '\x00', '\x00', ' ', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xcf', '\x93', '\xbe', + '\xad', 'M', '*', '\x8d', 'm', '\x17', 'L', '\x17', 'R', + '#', '{', '.', 'R', '\x08', '\xa5', '\x94', '\xb5', '\x96', + '\x18', 'h', '>', '\xe5', '\x0e', '\xf2', '\x09', '\xcf', '\x1e', + '\xfb', '\x19', '\x00', ' ', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'}; + + success = tools::unserialize_obj_from_buff( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "6f46324faae448b9e3b96dac94da17be6ab7eaaba398de86d8743042c98bace0"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_emit_boost_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 's', + 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'a', 't', + 'i', 'o', 'n', ':', ':', 'a', 'r', 'c', 'h', + 'i', 'v', 'e', '\x11', '\x00', '\x04', '\x08', '\x04', '\x08', + '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', + '\x02', '\x00', '\x00', '\x00', '\x00', '\x00', 'd', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '2', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x03', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', 'H', 'L', 'O', '\x0b', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'E', 'L', + 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', '\x0d', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'e', + 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', + 'd', '!', '\x00', '\x00', '\x00', '\x00', '\x00', ' ', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xed', '\xab', 'W', + '\x1c', 'K', '\xe9', '\xea', '\xbf', '\xea', '^', 'x', '\x83', + '\x03', 'm', 't', 'L', '\x09', 's', '\x82', '\xeb', 'o', + 's', '\x9a', '\x91', 'M', '\xb0', 'o', 'r', '\xba', '5', + '\x09', '\x9d', '\x00', ' ', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', + '\x00', '\x00', '\x00', '\x01', ' ', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', 'I', '\xa3', '\xd6', 'e', '*', '\xaa', + '\x0b', ';', 'w', ')', ',', 'S', 'N', '\x91', '\xff', + '\x80', '\xde', '\x91', ' ', '\xae', '\xb6', '\xfc', '\x1c', '^', + '\xdc', 'r', '\x80', 'G', 'C', '}', 'f', '~'}; + + success = tools::unserialize_obj_from_buff( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "49a3d6652aaa0b3b77292c534e91ff80de9120aeb6fc1c5edc728047437d667e"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_update_boost_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 's', + 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'a', 't', + 'i', 'o', 'n', ':', ':', 'a', 'r', 'c', 'h', + 'i', 'v', 'e', '\x11', '\x00', '\x04', '\x08', '\x04', '\x08', + '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', + '\x03', '\x00', '\x00', '\x00', '\x00', '\x00', 'd', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '2', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x03', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', 'H', 'L', 'O', '\x0b', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'E', 'L', + 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', '\x0d', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'e', + 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', + 'd', '!', '\x00', '\x00', '\x00', '\x00', '\x00', ' ', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x8c', '\xb6', '4', + '\x9f', 'Q', '\xda', 'e', '\x99', '\xfe', '\xea', '\xe7', '\xc0', + '\x07', 'r', '\x93', 'C', 'n', '\xb6', '\xa5', '\x00', '\x0f', + '\x0e', 'n', 'p', 'n', 'w', '\x88', 'k', '\xb5', '@', + '\xe2', '\xc1', '\x00', ' ', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', + '\x00', '\x00', '\x00', '\x01', ' ', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\xc3', 'q', '\xf6', '\x0d', '\xd8', '3', + '2', '\x98', '\xc6', '\xaa', 't', 'k', 'q', '\xe1', '\xe2', + '\x05', '\'', '\xb1', '\xff', '^', '\x1b', '\xed', 'N', '\xa9', + '\xb5', '\xf5', '\x92', '\xfa', '\xdf', '\x90', '\xed', 'k'}; + + success = tools::unserialize_obj_from_buff( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "c371f60dd8333298c6aa746b71e1e20527b1ff5e1bed4ea9b5f592fadf90ed6b"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_public_burn_boost_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 's', + 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'a', 't', + 'i', 'o', 'n', ':', ':', 'a', 'r', 'c', 'h', + 'i', 'v', 'e', '\x11', '\x00', '\x04', '\x08', '\x04', '\x08', + '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', + '\x04', '\x00', '\x00', '\x00', '\x00', '\x00', 'd', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '2', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x03', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', 'H', 'L', 'O', '\x0b', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'E', 'L', + 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', '\x0d', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'e', + 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', + 'd', '!', '\x00', '\x00', '\x00', '\x00', '\x00', ' ', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x0c', '@', '\x8c', + '\xf8', '\xb7', '\xfb', '\x80', '\x8f', '@', 'Y', '=', 'n', + '\xb7', 'X', '\x90', '\xe2', '\xab', '=', '\x0c', '\xcd', '\xc7', + '\x01', 'J', '\x7f', '\xc6', '\xb6', '\xab', '\x05', '\x16', ';', + '\xe0', '`', '\x00', ' ', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', + '\x00', '\x00', '\x00', '\x01', ' ', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', 'T', '\xf3', '\xf7', ',', 'r', '\xe5', + '\xb0', '\x14', '\xad', '+', '+', '\x90', '\x01', '\xac', '\xef', + '\x95', 'O', '\xe8', '-', '\xd3', '\xed', 'V', '\xa3', '\x8c', + '\xd9', '\xdd', '\xc5', '\xdb', 'W', 'g', '?', '\x8f'}; + + success = tools::unserialize_obj_from_buff( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "54f3f72c72e5b014ad2b2b9001acef954fe82dd3ed56a38cd9ddc5db57673f8f"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_undefined_boost_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '\x16', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 's', + 'e', 'r', 'i', 'a', 'l', 'i', 'z', 'a', 't', + 'i', 'o', 'n', ':', ':', 'a', 'r', 'c', 'h', + 'i', 'v', 'e', '\x11', '\x00', '\x04', '\x08', '\x04', '\x08', + '\x01', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'd', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '2', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x03', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', 'H', 'L', 'O', '\x0b', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'E', 'L', + 'L', 'O', '_', 'W', 'O', 'R', 'L', 'D', '\x0d', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'H', 'e', + 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', + 'd', '!', '\x00', '\x00', '\x00', '\x00', '\x00', ' ', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xe9', '\x1b', '\x9a', + 's', ')', '-', 'n', '\xa4', 'o', '\xb3', '\xd4', '\xf4', + '\xcc', 'y', '\xc3', 'K', '\xfb', '}', '\x14', '\xc2', '\xe6', + '\x84', '\xe5', '\x80', '\x93', '\xa2', 'G', '\x1c', '\x92', '\xe5', + '\x1c', '\x16', '\x00', ' ', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', + '\x00', '\x00', '\x00', '\x01', ' ', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x00', '\x97', '\x9e', '\xb7', '\x06', '\xac', '\xe2', + '\xeb', '\x83', '\xf9', '\x12', 'V', 'X', '\xb2', '?', '\xb3', + 'R', ' ', '\x84', '\x80', '\xcb', ';', '\x90', '\xc4', '>', + '-', '\xf0', '\xd2', '\x98', '\xf9', 'u', 'N', '\xbc'}; + + success = tools::unserialize_obj_from_buff( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_FALSE(success); +} + +TEST(multiassets, get_or_calculate_asset_id_register_key_value_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '{', '\x0d', '\x0a', ' ', ' ', '"', 'a', 'm', 'o', + 'u', 'n', 't', '_', 'c', 'o', 'm', 'm', 'i', + 't', 'm', 'e', 'n', 't', '"', ':', ' ', '"', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '"', ',', '\x0d', '\x0a', ' ', ' ', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', + '"', ':', ' ', '{', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'c', 'u', 'r', 'r', 'e', 'n', 't', + '_', 's', 'u', 'p', 'p', 'l', 'y', '"', ':', + ' ', '5', '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'd', 'e', 'c', 'i', 'm', 'a', 'l', + '_', 'p', 'o', 'i', 'n', 't', '"', ':', ' ', + '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', + 'f', 'u', 'l', 'l', '_', 'n', 'a', 'm', 'e', + '"', ':', ' ', '"', 'H', 'E', 'L', 'L', 'O', + '_', 'W', 'O', 'R', 'L', 'D', '"', ',', '\x0d', + '\x0a', ' ', ' ', ' ', ' ', '"', 'h', 'i', 'd', + 'd', 'e', 'n', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', 'f', 'a', 'l', 's', 'e', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'm', + 'e', 't', 'a', '_', 'i', 'n', 'f', 'o', '"', + ':', ' ', '"', 'H', 'e', 'l', 'l', 'o', ',', + ' ', 'w', 'o', 'r', 'l', 'd', '!', '"', ',', + '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'o', 'w', + 'n', 'e', 'r', '"', ':', ' ', '"', 'c', 'f', + '9', '3', 'b', 'e', 'a', 'd', '4', 'd', '2', + 'a', '8', 'd', '6', 'd', '1', '7', '4', 'c', + '1', '7', '5', '2', '2', '3', '7', 'b', '2', + 'e', '5', '2', '0', '8', 'a', '5', '9', '4', + 'b', '5', '9', '6', '1', '8', '6', '8', '3', + 'e', 'e', '5', '0', 'e', 'f', '2', '0', '9', + 'c', 'f', '1', 'e', 'f', 'b', '1', '9', '"', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 't', + 'i', 'c', 'k', 'e', 'r', '"', ':', ' ', '"', + 'H', 'L', 'O', '"', ',', '\x0d', '\x0a', ' ', ' ', + ' ', ' ', '"', 't', 'o', 't', 'a', 'l', '_', + 'm', 'a', 'x', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', '1', '0', '0', '\x0d', '\x0a', + ' ', ' ', '}', ',', '\x0d', '\x0a', ' ', ' ', '"', + 'o', 'p', 'e', 'r', 'a', 't', 'i', 'o', 'n', + '_', 't', 'y', 'p', 'e', '"', ':', ' ', '1', + ',', '\x0d', '\x0a', ' ', ' ', '"', 'o', 'p', 't', + '_', 'a', 's', 's', 'e', 't', '_', 'i', 'd', + '"', ':', ' ', '"', '0', '1', '6', 'f', '4', + '6', '3', '2', '4', 'f', 'a', 'a', 'e', '4', + '4', '8', 'b', '9', 'e', '3', 'b', '9', '6', + 'd', 'a', 'c', '9', '4', 'd', 'a', '1', '7', + 'b', 'e', '6', 'a', 'b', '7', 'e', 'a', 'a', + 'b', 'a', '3', '9', '8', 'd', 'e', '8', '6', + 'd', '8', '7', '4', '3', '0', '4', '2', 'c', + '9', '8', 'b', 'a', 'c', 'e', '0', '"', '\x0d', + '\x0a', '}'}; + + success = epee::serialization::load_t_from_json( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "6f46324faae448b9e3b96dac94da17be6ab7eaaba398de86d8743042c98bace0"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + const crypto::public_key expected_asset_id_key{ + expected_asset_id_pt.to_public_key()}; + + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_emit_key_value_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '{', '\x0d', '\x0a', ' ', ' ', '"', 'a', 'm', 'o', + 'u', 'n', 't', '_', 'c', 'o', 'm', 'm', 'i', + 't', 'm', 'e', 'n', 't', '"', ':', ' ', '"', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '"', ',', '\x0d', '\x0a', ' ', ' ', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', + '"', ':', ' ', '{', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'c', 'u', 'r', 'r', 'e', 'n', 't', + '_', 's', 'u', 'p', 'p', 'l', 'y', '"', ':', + ' ', '5', '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'd', 'e', 'c', 'i', 'm', 'a', 'l', + '_', 'p', 'o', 'i', 'n', 't', '"', ':', ' ', + '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', + 'f', 'u', 'l', 'l', '_', 'n', 'a', 'm', 'e', + '"', ':', ' ', '"', 'H', 'E', 'L', 'L', 'O', + '_', 'W', 'O', 'R', 'L', 'D', '"', ',', '\x0d', + '\x0a', ' ', ' ', ' ', ' ', '"', 'h', 'i', 'd', + 'd', 'e', 'n', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', 'f', 'a', 'l', 's', 'e', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'm', + 'e', 't', 'a', '_', 'i', 'n', 'f', 'o', '"', + ':', ' ', '"', 'H', 'e', 'l', 'l', 'o', ',', + ' ', 'w', 'o', 'r', 'l', 'd', '!', '"', ',', + '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'o', 'w', + 'n', 'e', 'r', '"', ':', ' ', '"', 'e', 'd', + 'a', 'b', '5', '7', '1', 'c', '4', 'b', 'e', + '9', 'e', 'a', 'b', 'f', 'e', 'a', '5', 'e', + '7', '8', '8', '3', '0', '3', '6', 'd', '7', + '4', '4', 'c', '0', '9', '7', '3', '8', '2', + 'e', 'b', '6', 'f', '7', '3', '9', 'a', '9', + '1', '4', 'd', 'b', '0', '6', 'f', '7', '2', + 'b', 'a', '3', '5', '0', '9', '9', 'd', '"', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 't', + 'i', 'c', 'k', 'e', 'r', '"', ':', ' ', '"', + 'H', 'L', 'O', '"', ',', '\x0d', '\x0a', ' ', ' ', + ' ', ' ', '"', 't', 'o', 't', 'a', 'l', '_', + 'm', 'a', 'x', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', '1', '0', '0', '\x0d', '\x0a', + ' ', ' ', '}', ',', '\x0d', '\x0a', ' ', ' ', '"', + 'o', 'p', 'e', 'r', 'a', 't', 'i', 'o', 'n', + '_', 't', 'y', 'p', 'e', '"', ':', ' ', '2', + ',', '\x0d', '\x0a', ' ', ' ', '"', 'o', 'p', 't', + '_', 'a', 's', 's', 'e', 't', '_', 'i', 'd', + '"', ':', ' ', '"', '0', '1', '4', '9', 'a', + '3', 'd', '6', '6', '5', '2', 'a', 'a', 'a', + '0', 'b', '3', 'b', '7', '7', '2', '9', '2', + 'c', '5', '3', '4', 'e', '9', '1', 'f', 'f', + '8', '0', 'd', 'e', '9', '1', '2', '0', 'a', + 'e', 'b', '6', 'f', 'c', '1', 'c', '5', 'e', + 'd', 'c', '7', '2', '8', '0', '4', '7', '4', + '3', '7', 'd', '6', '6', '7', 'e', '"', '\x0d', + '\x0a', '}'}; + + success = epee::serialization::load_t_from_json( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "49a3d6652aaa0b3b77292c534e91ff80de9120aeb6fc1c5edc728047437d667e"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_update_key_value_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '{', '\x0d', '\x0a', ' ', ' ', '"', 'a', 'm', 'o', + 'u', 'n', 't', '_', 'c', 'o', 'm', 'm', 'i', + 't', 'm', 'e', 'n', 't', '"', ':', ' ', '"', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '"', ',', '\x0d', '\x0a', ' ', ' ', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', + '"', ':', ' ', '{', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'c', 'u', 'r', 'r', 'e', 'n', 't', + '_', 's', 'u', 'p', 'p', 'l', 'y', '"', ':', + ' ', '5', '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'd', 'e', 'c', 'i', 'm', 'a', 'l', + '_', 'p', 'o', 'i', 'n', 't', '"', ':', ' ', + '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', + 'f', 'u', 'l', 'l', '_', 'n', 'a', 'm', 'e', + '"', ':', ' ', '"', 'H', 'E', 'L', 'L', 'O', + '_', 'W', 'O', 'R', 'L', 'D', '"', ',', '\x0d', + '\x0a', ' ', ' ', ' ', ' ', '"', 'h', 'i', 'd', + 'd', 'e', 'n', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', 'f', 'a', 'l', 's', 'e', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'm', + 'e', 't', 'a', '_', 'i', 'n', 'f', 'o', '"', + ':', ' ', '"', 'H', 'e', 'l', 'l', 'o', ',', + ' ', 'w', 'o', 'r', 'l', 'd', '!', '"', ',', + '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'o', 'w', + 'n', 'e', 'r', '"', ':', ' ', '"', '8', 'c', + 'b', '6', '3', '4', '9', 'f', '5', '1', 'd', + 'a', '6', '5', '9', '9', 'f', 'e', 'e', 'a', + 'e', '7', 'c', '0', '0', '7', '7', '2', '9', + '3', '4', '3', '6', 'e', 'b', '6', 'a', '5', + '0', '0', '0', 'f', '0', 'e', '6', 'e', '7', + '0', '6', 'e', '7', '7', '8', '8', '6', 'b', + 'b', '5', '4', '0', 'e', '2', 'c', '1', '"', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 't', + 'i', 'c', 'k', 'e', 'r', '"', ':', ' ', '"', + 'H', 'L', 'O', '"', ',', '\x0d', '\x0a', ' ', ' ', + ' ', ' ', '"', 't', 'o', 't', 'a', 'l', '_', + 'm', 'a', 'x', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', '1', '0', '0', '\x0d', '\x0a', + ' ', ' ', '}', ',', '\x0d', '\x0a', ' ', ' ', '"', + 'o', 'p', 'e', 'r', 'a', 't', 'i', 'o', 'n', + '_', 't', 'y', 'p', 'e', '"', ':', ' ', '3', + ',', '\x0d', '\x0a', ' ', ' ', '"', 'o', 'p', 't', + '_', 'a', 's', 's', 'e', 't', '_', 'i', 'd', + '"', ':', ' ', '"', '0', '1', 'c', '3', '7', + '1', 'f', '6', '0', 'd', 'd', '8', '3', '3', + '3', '2', '9', '8', 'c', '6', 'a', 'a', '7', + '4', '6', 'b', '7', '1', 'e', '1', 'e', '2', + '0', '5', '2', '7', 'b', '1', 'f', 'f', '5', + 'e', '1', 'b', 'e', 'd', '4', 'e', 'a', '9', + 'b', '5', 'f', '5', '9', '2', 'f', 'a', 'd', + 'f', '9', '0', 'e', 'd', '6', 'b', '"', '\x0d', + '\x0a', '}'}; + + success = epee::serialization::load_t_from_json( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_TRUE(success); + + const std::string expected_asset_id_str{ + "c371f60dd8333298c6aa746b71e1e20527b1ff5e1bed4ea9b5f592fadf90ed6b"}; + + crypto::point_t expected_asset_id_pt{}; + success = expected_asset_id_pt.from_string(expected_asset_id_str); + ASSERT_TRUE(success); + + crypto::public_key expected_asset_id_key{}; + expected_asset_id_pt.to_public_key(expected_asset_id_key); + ASSERT_EQ(calculated_asset_id_pt, expected_asset_id_pt); + ASSERT_EQ(calculated_asset_id_key, expected_asset_id_key); +} + +TEST(multiassets, get_or_calculate_asset_id_undefined_key_value_serialization) +{ + bool success{false}; + currency::asset_descriptor_operation_v0 asset_descriptor_operation{}; + const std::string serialized_asset_descriptor_operation{ + '{', '\x0d', '\x0a', ' ', ' ', '"', 'a', 'm', 'o', + 'u', 'n', 't', '_', 'c', 'o', 'm', 'm', 'i', + 't', 'm', 'e', 'n', 't', '"', ':', ' ', '"', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '"', ',', '\x0d', '\x0a', ' ', ' ', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', + '"', ':', ' ', '{', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'c', 'u', 'r', 'r', 'e', 'n', 't', + '_', 's', 'u', 'p', 'p', 'l', 'y', '"', ':', + ' ', '5', '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', + ' ', '"', 'd', 'e', 'c', 'i', 'm', 'a', 'l', + '_', 'p', 'o', 'i', 'n', 't', '"', ':', ' ', + '0', ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', + 'f', 'u', 'l', 'l', '_', 'n', 'a', 'm', 'e', + '"', ':', ' ', '"', 'H', 'E', 'L', 'L', 'O', + '_', 'W', 'O', 'R', 'L', 'D', '"', ',', '\x0d', + '\x0a', ' ', ' ', ' ', ' ', '"', 'h', 'i', 'd', + 'd', 'e', 'n', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', 'f', 'a', 'l', 's', 'e', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'm', + 'e', 't', 'a', '_', 'i', 'n', 'f', 'o', '"', + ':', ' ', '"', 'H', 'e', 'l', 'l', 'o', ',', + ' ', 'w', 'o', 'r', 'l', 'd', '!', '"', ',', + '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 'o', 'w', + 'n', 'e', 'r', '"', ':', ' ', '"', 'e', '9', + '1', 'b', '9', 'a', '7', '3', '2', '9', '2', + 'd', '6', 'e', 'a', '4', '6', 'f', 'b', '3', + 'd', '4', 'f', '4', 'c', 'c', '7', '9', 'c', + '3', '4', 'b', 'f', 'b', '7', 'd', '1', '4', + 'c', '2', 'e', '6', '8', '4', 'e', '5', '8', + '0', '9', '3', 'a', '2', '4', '7', '1', 'c', + '9', '2', 'e', '5', '1', 'c', '1', '6', '"', + ',', '\x0d', '\x0a', ' ', ' ', ' ', ' ', '"', 't', + 'i', 'c', 'k', 'e', 'r', '"', ':', ' ', '"', + 'H', 'L', 'O', '"', ',', '\x0d', '\x0a', ' ', ' ', + ' ', ' ', '"', 't', 'o', 't', 'a', 'l', '_', + 'm', 'a', 'x', '_', 's', 'u', 'p', 'p', 'l', + 'y', '"', ':', ' ', '1', '0', '0', '\x0d', '\x0a', + ' ', ' ', '}', ',', '\x0d', '\x0a', ' ', ' ', '"', + 'o', 'p', 'e', 'r', 'a', 't', 'i', 'o', 'n', + '_', 't', 'y', 'p', 'e', '"', ':', ' ', '0', + ',', '\x0d', '\x0a', ' ', ' ', '"', 'o', 'p', 't', + '_', 'a', 's', 's', 'e', 't', '_', 'i', 'd', + '"', ':', ' ', '"', '0', '1', '9', '7', '9', + 'e', 'b', '7', '0', '6', 'a', 'c', 'e', '2', + 'e', 'b', '8', '3', 'f', '9', '1', '2', '5', + '6', '5', '8', 'b', '2', '3', 'f', 'b', '3', + '5', '2', '2', '0', '8', '4', '8', '0', 'c', + 'b', '3', 'b', '9', '0', 'c', '4', '3', 'e', + '2', 'd', 'f', '0', 'd', '2', '9', '8', 'f', + '9', '7', '5', '4', 'e', 'b', 'c', '"', '\x0d', + '\x0a', '}'}; + + success = epee::serialization::load_t_from_json( + asset_descriptor_operation, + serialized_asset_descriptor_operation); + ASSERT_TRUE(success); + + crypto::point_t calculated_asset_id_pt{}; + crypto::public_key calculated_asset_id_key{}; + + success = currency::get_or_calculate_asset_id(asset_descriptor_operation, + &calculated_asset_id_pt, + &calculated_asset_id_key); + ASSERT_FALSE(success); +} diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 47e0d4c4..bd53b009 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -757,30 +757,30 @@ struct A struct A_v1 : public A { std::vector vector_two; - + uint8_t current_version = 1; BEGIN_SERIALIZE() FIELD(one) FIELD(two) FIELD(vector_one) - VERSION(1) - if (s_version < 1) return true; + VERSION_TO_MEMBER(1, current_version) FIELD(vector_two) END_SERIALIZE() }; + struct A_v2 : public A_v1 { std::vector vector_3; std::vector vector_4; - + uint8_t current_version = 2; BEGIN_SERIALIZE() //CURRENT_VERSION(2) FIELD(one) FIELD(two) FIELD(vector_one) - VERSION(2) + VERSION_TO_MEMBER(2, current_version) if (s_version < 1) return true; FIELD(vector_two) if (s_version < 2) return true; @@ -792,13 +792,14 @@ struct A_v2 : public A_v1 struct A_v3 : public A_v2 { std::vector vector_5; + uint8_t current_version = 3; BEGIN_SERIALIZE() //CURRENT_VERSION(3) FIELD(one) FIELD(two) FIELD(vector_one) - VERSION(3) + VERSION_TO_MEMBER(3, current_version) if (s_version < 1) return true; FIELD(vector_two) if (s_version < 2) return true;