diff --git a/contrib/db/CMakeLists.txt b/contrib/db/CMakeLists.txt index 6f2213b4..c558ad63 100644 --- a/contrib/db/CMakeLists.txt +++ b/contrib/db/CMakeLists.txt @@ -6,15 +6,18 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android") endif() - message("DB ENGINE: lmdb") - add_subdirectory(liblmdb) - if(MSVC) - target_compile_options(lmdb PRIVATE /wd4996 /wd4503 /wd4345 /wd4267 /wd4244 /wd4146 /wd4333 /wd4172) - else() - # Warnings as used by LMDB itself (LMDB_0.9.23) - target_compile_options(lmdb PRIVATE -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized) - endif() - if(NOT DISABLE_MDBX) - message("DB ENGINE: mdbx") - add_subdirectory(libmdbx) - endif() +message("DB ENGINE: lmdb") +add_subdirectory(liblmdb) +if(MSVC) + target_compile_options(lmdb PRIVATE /wd4996 /wd4503 /wd4345 /wd4267 /wd4244 /wd4146 /wd4333 /wd4172) +else() + # Warnings as used by LMDB itself (LMDB_0.9.23) + target_compile_options(lmdb PRIVATE -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized) +endif() + +if(NOT DISABLE_MDBX) + message("DB ENGINE: mdbx") + add_subdirectory(libmdbx) + # remove mdbx tools from the default MSVC build + set_target_properties(mdbx_chk mdbx_copy mdbx_dump mdbx_load mdbx_stat PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) +endif() diff --git a/contrib/epee/include/print_fixed_point_helper.h b/contrib/epee/include/print_fixed_point_helper.h index c652f601..5a1eace8 100644 --- a/contrib/epee/include/print_fixed_point_helper.h +++ b/contrib/epee/include/print_fixed_point_helper.h @@ -1,3 +1,4 @@ +// Copyright (c) 2024, Zano Project // Copyright (c) 2006-2017, Andrey N. Sabelnikov, www.sabelnikov.net // All rights reserved. // @@ -35,11 +36,14 @@ namespace epee inline std::string print_fixed_decimal_point(t_number amount, size_t decimal_point) { std::string s = boost::lexical_cast(amount); + if (decimal_point > 32) + return std::string("!!") + s; // avoiding overflow issues if (s.size() < decimal_point + 1) { s.insert(0, decimal_point + 1 - s.size(), '0'); } - s.insert(s.size() - decimal_point, "."); + if (decimal_point > 0) + s.insert(s.size() - decimal_point, "."); return s; } } diff --git a/contrib/epee/include/serialization/keyvalue_helpers.h b/contrib/epee/include/serialization/keyvalue_helpers.h index 9b5f0dcf..d76fc641 100644 --- a/contrib/epee/include/serialization/keyvalue_helpers.h +++ b/contrib/epee/include/serialization/keyvalue_helpers.h @@ -105,7 +105,7 @@ namespace epee std::string res; for (const auto& item : a) { - res += epee::string_tools::pod_to_hex(a) + ", "; + res += epee::string_tools::pod_to_hex(item) + ", "; } if (a.size()) { diff --git a/src/common/error_codes.h b/src/common/error_codes.h index a1278d4b..1e3dc2ef 100644 --- a/src/common/error_codes.h +++ b/src/common/error_codes.h @@ -19,6 +19,7 @@ #define API_RETURN_CODE_BAD_ARG_WRONG_FEE "BAD_ARG_WRONG_FEE" #define API_RETURN_CODE_BAD_ARG_INVALID_ADDRESS "BAD_ARG_INVALID_ADDRESS" #define API_RETURN_CODE_BAD_ARG_WRONG_AMOUNT "BAD_ARG_WRONG_AMOUNT" +#define API_RETURN_CODE_BAD_ARG_UNKNOWN_DECIMAL_POINT "BAD_ARG_UNKNOWN_DECIMAL_POINT" #define API_RETURN_CODE_BAD_ARG_WRONG_PAYMENT_ID "BAD_ARG_WRONG_PAYMENT_ID" #define API_RETURN_CODE_WRONG_PASSWORD "WRONG_PASSWORD" #define API_RETURN_CODE_WALLET_WRONG_ID "WALLET_WRONG_ID" diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 80ddef17..0d3e5891 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -700,7 +700,7 @@ namespace currency { uint64_t total_max_supply = 0; uint64_t current_supply = 0; - uint8_t decimal_point = 12; + uint8_t decimal_point = 0; std::string ticker; std::string full_name; std::string meta_info; @@ -733,12 +733,12 @@ namespace currency 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") DOC_EXMP(500000000000000000) 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 to validate any operations on the asset altering, could be changed in case of transfer ownership") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") 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() }; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 2c8e5c2a..afd632d7 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -793,8 +793,7 @@ namespace currency } } //--------------------------------------------------------------- - // TODO: reverse order of arguments - bool parse_amount(uint64_t& amount, const std::string& str_amount_) + bool parse_amount(const std::string& str_amount_, uint64_t& amount, const size_t decimal_point /* = CURRENCY_DISPLAY_DECIMAL_POINT */) { std::string str_amount = str_amount_; boost::algorithm::trim(str_amount); @@ -804,12 +803,12 @@ namespace currency if (std::string::npos != point_index) { fraction_size = str_amount.size() - point_index - 1; - while (CURRENCY_DISPLAY_DECIMAL_POINT < fraction_size && '0' == str_amount.back()) + while (decimal_point < fraction_size && '0' == str_amount.back()) { str_amount.erase(str_amount.size() - 1, 1); --fraction_size; } - if (CURRENCY_DISPLAY_DECIMAL_POINT < fraction_size) + if (decimal_point < fraction_size) return false; str_amount.erase(point_index, 1); } @@ -821,9 +820,9 @@ namespace currency if (str_amount.empty()) return false; - if (fraction_size < CURRENCY_DISPLAY_DECIMAL_POINT) + if (fraction_size < decimal_point) { - str_amount.append(CURRENCY_DISPLAY_DECIMAL_POINT - fraction_size, '0'); + str_amount.append(decimal_point - fraction_size, '0'); } return string_tools::get_xtype_from_string(amount, str_amount); @@ -3464,11 +3463,32 @@ namespace currency //--------------------------------------------------------------- std::string print_money_brief(uint64_t amount, size_t decimal_point /* = CURRENCY_DISPLAY_DECIMAL_POINT */) { + // TODO: temporary fix for insanely big decimal points + // TODO: remove it after setting the limit to 18 -- sowle + if (decimal_point > 32) + return std::string("!!") + std::to_string(amount); + if (decimal_point >= 20) + { + std::string r = std::to_string(amount); + if (decimal_point + 1 > r.size()) + r.insert(0, decimal_point - r.size() + 1, '0'); + r.insert(r.begin() + 1, '.'); + size_t p = r.find_last_not_of('0'); + if (p != r.npos) + { + if (r[p] != '.' && p + 1 < r.size()) + r.erase(p + 1); + else if (p + 2 < r.size()) + r.erase(p + 2); + } + return r; + } + uint64_t coin = decimal_point == CURRENCY_DISPLAY_DECIMAL_POINT ? COIN : crypto::constexpr_pow(decimal_point, 10); uint64_t remainder = amount % coin; amount /= coin; if (remainder == 0) - return std::to_string(amount) + ".0"; + return std::to_string(amount) + (decimal_point > 0 ? ".0" : ""); std::string r = std::to_string(remainder); if (r.size() < decimal_point) r.insert(0, decimal_point - r.size(), '0'); diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 5c4ea929..3e875c08 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -402,7 +402,7 @@ namespace currency uint64_t get_outs_money_amount(const transaction& tx, const currency::account_keys& acc_keys_for_hidden_amounts = currency::null_acc_keys); bool check_inputs_types_supported(const transaction& tx); bool check_outs_valid(const transaction& tx); - bool parse_amount(uint64_t& amount, const std::string& str_amount); + bool parse_amount(const std::string& str_amount, uint64_t& amount, const size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT); bool parse_tracking_seed(const std::string& tracking_seed, account_public_address& address, crypto::secret_key& view_sec_key, uint64_t& creation_timestamp); @@ -610,20 +610,25 @@ namespace currency return true; } //--------------------------------------------------------------- - // outputs "1391306.970000000000" + // outputs "1391306.970000000000" (decimal_point = 12) + // outputs "1391306970000000000" (decimal_point = 0) template std::string print_fixed_decimal_point(t_number amount, size_t decimal_point) { return epee::string_tools::print_fixed_decimal_point(amount, decimal_point); } //--------------------------------------------------------------- - // outputs "1391306.97 " + // outputs "1391306.97 " (decimal_point = 12) + // outputs "139130697 " (decimal_point = 0) template std::string print_fixed_decimal_point_with_trailing_spaces(t_number amount, size_t decimal_point) { std::string s = epee::string_tools::print_fixed_decimal_point(amount, decimal_point); - for(size_t n = s.size() - 1; n != 0 && s[n] == '0' && s[n-1] != '.'; --n) - s[n] = ' '; + if (s.find('.') != std::string::npos) + { + for(size_t n = s.size() - 1; n != 0 && s[n] == '0' && s[n-1] != '.'; --n) + s[n] = ' '; + } return s; } //--------------------------------------------------------------- @@ -1084,17 +1089,24 @@ namespace currency bool operator()(const extra_user_data& ee) { tv.type = "user_data"; - tv.short_view = std::to_string(ee.buff.size()) + " bytes"; tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.buff); + if (ee.buff.size() <= 8) + tv.short_view = tv.details_view; + else + tv.short_view = std::to_string(ee.buff.size()) + " bytes"; return true; } bool operator()(const extra_padding& ee) { tv.type = "extra_padding"; - tv.short_view = std::to_string(ee.buff.size()) + " bytes"; if (!ee.buff.empty()) tv.details_view = epee::string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(&ee.buff[0]), ee.buff.size())); + + if (ee.buff.size() <= 8) + tv.short_view = tv.details_view; + else + tv.short_view = std::to_string(ee.buff.size()) + " bytes"; return true; } @@ -1139,17 +1151,25 @@ namespace currency bool operator()(const tx_derivation_hint& ee) { tv.type = "derivation_hint"; - tv.short_view = std::to_string(ee.msg.size()) + " bytes"; tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee.msg); + if (ee.msg.size() <= 8) + tv.short_view = tv.details_view; + else + tv.short_view = std::to_string(ee.msg.size()) + " bytes"; + return true; } bool operator()(const std::string& ee) { tv.type = "string"; - tv.short_view = std::to_string(ee.size()) + " bytes"; tv.details_view = epee::string_tools::buff_to_hex_nodelimer(ee); + if (ee.size() <= 8) + tv.short_view = tv.details_view; + else + tv.short_view = std::to_string(ee.size()) + " bytes"; + return true; } bool operator()(const etc_tx_flags16_t& dh) @@ -1179,6 +1199,15 @@ namespace currency tv.type = "zc_balance_proof"; return true; } + bool operator()(const asset_descriptor_operation& ado) + { + tv.type = "asset operation"; + tv.short_view = std::string("op:") + get_asset_operation_type_string(ado.operation_type, true); + if (ado.opt_asset_id.has_value()) + tv.short_view += std::string(" , id:") + crypto::pod_to_hex(ado.opt_asset_id); + tv.details_view = tv.short_view + std::string(" , ticker:") + ado.descriptor.ticker + std::string(" , cur.supply:") + print_money_brief(ado.descriptor.current_supply, ado.descriptor.decimal_point); + return true; + } template bool operator()(const t_type& t_t) { diff --git a/src/currency_protocol/currency_protocol_handler.inl b/src/currency_protocol/currency_protocol_handler.inl index 9f4c22de..e6d75eb2 100644 --- a/src/currency_protocol/currency_protocol_handler.inl +++ b/src/currency_protocol/currency_protocol_handler.inl @@ -7,6 +7,7 @@ #include #include "currency_core/currency_format_utils.h" #include "profile_tools.h" +#include namespace currency { diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index 8f2aa24a..c960a39c 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -817,6 +817,7 @@ private: " amount cmt * 1/8: " << aop.amount_commitment << ENDL << " hidden supply: " << (aop.descriptor.hidden_supply ? "yes" : "no") << ENDL << ""; + ++idx; } LOG_PRINT_L0(ss.str()); diff --git a/src/gui/qt-daemon/application/mainwindow.cpp b/src/gui/qt-daemon/application/mainwindow.cpp index 40758a4a..90f3b422 100644 --- a/src/gui/qt-daemon/application/mainwindow.cpp +++ b/src/gui/qt-daemon/application/mainwindow.cpp @@ -1767,6 +1767,14 @@ QString MainWindow::validate_address(const QString& param) LOG_API_TIMING(); view::address_validation_response ar = AUTO_VAL_INIT(ar); ar.error_code = m_backend.validate_address(param.toStdString(), ar.payment_id); + + //@#@ +//#ifdef _DEBUG +// std::string json_body; +// bool r = epee::file_io_utils::load_file_to_string("C:\\Users\\roky\\home\\temp\\deploy_test.json", json_body); +// async_call_2a("call_wallet_rpc", "0", json_body.c_str()); +//#endif + return MAKE_RESPONSE(ar); CATCH_ENTRY_FAIL_API_RESPONCE(); } diff --git a/src/gui/qt-daemon/layout b/src/gui/qt-daemon/layout index 0710887a..2fb143cc 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit 0710887ae3231d329092691f326d91d90662149e +Subproject commit 2fb143cc67280f0e0cfcd3165e1c087ba8279edf diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 371d0a81..ee76e1ac 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -575,7 +575,7 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_pos_mining_details(const COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, COMMAND_RPC_GET_POS_MINING_DETAILS::response& res, connection_context& cntx) { - if (!m_p2p.get_connections_count()) + if (!m_ignore_status && !m_p2p.get_connections_count()) { res.status = API_RETURN_CODE_DISCONNECTED; return true; @@ -793,7 +793,7 @@ namespace currency return true; } - if (!m_p2p.get_payload_object().get_synchronized_connections_count()) + if (!m_ignore_status && !m_p2p.get_payload_object().get_synchronized_connections_count()) { LOG_PRINT_L0("[on_send_raw_tx]: Failed to send, daemon not connected to net"); res.status = API_RETURN_CODE_DISCONNECTED; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3935cb4c..2a92db5d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1577,7 +1577,10 @@ bool preprocess_asset_id(std::string& address_arg, crypto::public_key& asset_id) { auto p = address_arg.find(':'); if (p == std::string::npos) + { + asset_id = currency::native_coin_asset_id; return true; + } std::string asset_id_str = address_arg.substr(0, p); std::string address_itself = address_arg.substr(p+1, address_arg.size()); if (!epee::string_tools::parse_tpod_from_hex_string(asset_id_str, asset_id)) @@ -1636,19 +1639,28 @@ bool simple_wallet::transfer(const std::vector &args_) currency::tx_destination_entry de = AUTO_VAL_INIT(de); de.addr.resize(1); - bool ok = currency::parse_amount(de.amount, local_args[i + 1]); - if (!ok || 0 == de.amount) - { - fail_msg_writer() << "amount is wrong: " << local_args[i] << ' ' << local_args[i + 1] << - ", expected number from 0 to " << print_money(std::numeric_limits::max()); - return true; - } - if (!preprocess_asset_id(local_args[i], de.asset_id)) { fail_msg_writer() << "address is wrong: " << local_args[i]; return true; } + + uint32_t asset_flags = 0; + asset_descriptor_base asset_info{}; + if (!m_wallet->get_asset_info(de.asset_id, asset_info, asset_flags)) + { + fail_msg_writer() << "unknown asset id: " << de.asset_id; + return true; + } + + bool ok = currency::parse_amount(local_args[i + 1], de.amount, asset_info.decimal_point); + if (!ok || 0 == de.amount) + { + fail_msg_writer() << "amount is wrong: " << local_args[i] << ' ' << local_args[i + 1] << + ", expected number from 0 to " << print_money_brief(std::numeric_limits::max(), asset_info.decimal_point) << " with maximum " << (int)asset_info.decimal_point << " digits after decimal point"; + return true; + } + //check if address looks like wrapped address if (is_address_like_wrapped(local_args[i])) @@ -2150,24 +2162,29 @@ bool simple_wallet::emit_asset(const std::vector &args) bool r = epee::string_tools::parse_tpod_from_hex_string(args[0], asset_id); if (!r) { - fail_msg_writer() << "Failed to load asset_id from: " << args[0]; + fail_msg_writer() << "Failed to load asset id: " << args[0]; + return true; + } + + currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb); + uint32_t asset_flags = 0; + r = m_wallet->get_asset_info(asset_id, adb, asset_flags); + if (!r) + { + fail_msg_writer() << "Unknown asset id: " << args[0]; + return true; + } + if (!(asset_flags & tools::wallet2::aif_own)) + { + fail_msg_writer() << "The wallet appears to have no control over asset " << args[0]; return true; } uint64_t amount = 0; - r = epee::string_tools::get_xtype_from_string(amount, args[1]); + r = parse_amount(args[1], amount, adb.decimal_point); if (!r) { - fail_msg_writer() << "Failed to load amount from: " << args[1]; - return true; - } - - - currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb); - r = m_wallet->daemon_get_asset_info(asset_id, adb); - if (!r) - { - fail_msg_writer() << "Wallet seems to don't have control over asset: " << args[0]; + fail_msg_writer() << "Failed to read amount: " << args[1] << " (assuming decimal point is " << (int)adb.decimal_point << ")"; return true; } @@ -2180,12 +2197,14 @@ bool simple_wallet::emit_asset(const std::vector &args) currency::transaction result_tx = AUTO_VAL_INIT(result_tx); m_wallet->emit_asset(asset_id, destinations, result_tx); - success_msg_writer(true) << "Emitted " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL - << "Asset ID: " << asset_id << ENDL - << "Title: " << adb.full_name << ENDL - << "Ticker: " << adb.ticker << ENDL - << "Emitted: " << print_fixed_decimal_point(amount, adb.decimal_point) << ENDL - << "Max emission: " << print_fixed_decimal_point(adb.total_max_supply, adb.decimal_point) << ENDL + success_msg_writer(true) << "Emitted " << print_money_brief(amount, adb.decimal_point) << " in tx " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL + << "Asset ID: " << asset_id << ENDL + << "Title: " << adb.full_name << ENDL + << "Ticker: " << adb.ticker << ENDL + << "Emitted now: " << print_money_brief(amount, adb.decimal_point) << ENDL + << "Emitted before: " << print_money_brief(adb.current_supply, adb.decimal_point) << ENDL + << "Emitted total: " << print_money_brief(adb.current_supply + amount, adb.decimal_point) << ENDL + << "Max emission: " << print_money_brief(adb.total_max_supply, adb.decimal_point) << ENDL ; SIMPLE_WALLET_CATCH_TRY_ENTRY(); @@ -2205,36 +2224,40 @@ bool simple_wallet::burn_asset(const std::vector &args) bool r = epee::string_tools::parse_tpod_from_hex_string(args[0], asset_id); if (!r) { - fail_msg_writer() << "Failed to load asset_id from: " << args[0]; + fail_msg_writer() << "Failed to load asset id: " << args[0]; return true; } - uint64_t amount = 0; - r = epee::string_tools::get_xtype_from_string(amount, args[1]); - if (!r) - { - fail_msg_writer() << "Failed to load amount from: " << args[1]; - return true; - } - - currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb); - r = m_wallet->daemon_get_asset_info(asset_id, adb); + uint32_t asset_flags = 0; + r = m_wallet->get_asset_info(asset_id, adb, asset_flags); if (!r) { - fail_msg_writer() << "Wallet seems to don't have control over asset: " << args[0]; + fail_msg_writer() << "Unknown asset id: " << args[0]; + return true; + } + + // as this is asset burning, its not necessary for the wallet to own this asset, so we don't check tools::wallet2::aif_own here + + uint64_t amount = 0; + r = parse_amount(args[1], amount, adb.decimal_point); + if (!r) + { + fail_msg_writer() << "Failed to read amount: " << args[1] << " (assuming decimal point is " << (int)adb.decimal_point << ")"; return true; } currency::transaction result_tx = AUTO_VAL_INIT(result_tx); m_wallet->burn_asset(asset_id, amount, result_tx); - success_msg_writer(true) << "Burned " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL - << "Asset ID: " << asset_id << ENDL - << "Title: " << adb.full_name << ENDL - << "Ticker: " << adb.ticker << ENDL - << "Burned: " << print_fixed_decimal_point(amount, adb.decimal_point) << ENDL - << "Max emission: " << print_fixed_decimal_point(adb.total_max_supply, adb.decimal_point) << ENDL + success_msg_writer(true) << "Burned " << print_money_brief(amount, adb.decimal_point) << " in tx " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL + << "Asset ID: " << asset_id << ENDL + << "Title: " << adb.full_name << ENDL + << "Ticker: " << adb.ticker << ENDL + << "Burned now: " << print_money_brief(amount, adb.decimal_point) << ENDL + << "Emitted before: " << print_money_brief(adb.current_supply, adb.decimal_point) << ENDL + << "Current supply: " << print_money_brief(adb.current_supply - amount, adb.decimal_point) << ENDL + << "Max emission: " << print_money_brief(adb.total_max_supply, adb.decimal_point) << ENDL ; SIMPLE_WALLET_CATCH_TRY_ENTRY(); @@ -2268,10 +2291,16 @@ bool simple_wallet::update_asset(const std::vector &args) } currency::asset_descriptor_base adb = AUTO_VAL_INIT(adb); - r = m_wallet->daemon_get_asset_info(asset_id, adb); + uint32_t asset_flags = 0; + r = m_wallet->get_asset_info(asset_id, adb, asset_flags); if (!r) { - fail_msg_writer() << "Wallet seems to don't have control over asset: " << args[0]; + fail_msg_writer() << "Unknown asset id: " << args[0]; + return true; + } + if (!(asset_flags & tools::wallet2::aif_own)) + { + fail_msg_writer() << "The wallet appears to have no control over asset " << args[0]; return true; } @@ -2279,7 +2308,7 @@ bool simple_wallet::update_asset(const std::vector &args) currency::transaction result_tx = AUTO_VAL_INIT(result_tx); m_wallet->update_asset(asset_id, adb, result_tx); - success_msg_writer(true) << "Asset metainfo update tx sent: " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL + success_msg_writer(true) << "Asset metainfo successfully updated in tx " << get_transaction_hash(result_tx) << " (unconfirmed) : " << ENDL << "Asset ID: " << asset_id << ENDL << "Title: " << adb.full_name << ENDL << "Ticker: " << adb.ticker << ENDL @@ -2592,7 +2621,7 @@ bool simple_wallet::sweep_below(const std::vector &args) } uint64_t amount = 0; - r = currency::parse_amount(amount, args[2]); + r = currency::parse_amount(args[2], amount); if (!r || amount == 0) { fail_msg_writer() << "incorrect amount: " << args[2]; @@ -3127,7 +3156,7 @@ int main(int argc, char* argv[]) uint64_t max_amount = 0; CHECK_AND_ASSERT_MES(epee::string_tools::string_to_num_fast(params[0], outs_min) && outs_min > 0 && outs_min < 256, EXIT_FAILURE, "incorrect param: " << params[0]); CHECK_AND_ASSERT_MES(epee::string_tools::string_to_num_fast(params[1], outs_max) && outs_max > 0 && outs_max < 256, EXIT_FAILURE, "incorrect param: " << params[1]); - CHECK_AND_ASSERT_MES(currency::parse_amount(max_amount, params[2]), EXIT_FAILURE, "incorrect param: " << params[2]); + CHECK_AND_ASSERT_MES(currency::parse_amount(params[2], max_amount), EXIT_FAILURE, "incorrect param: " << params[2]); wal.set_defragmentation_tx_settings(true, outs_min, outs_max, max_amount); } } diff --git a/src/version.h.in b/src/version.h.in index d2dacde9..f1d6cbf4 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 321 +#define PROJECT_VERSION_BUILD_NO 333 #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/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 16aabd4e..891d76c4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3823,6 +3823,15 @@ bool wallet2::get_asset_info(const crypto::public_key& asset_id, currency::asset return true; } + // own asset? + auto it_own = m_own_asset_descriptors.find(asset_id); + if (it_own != m_own_asset_descriptors.end()) + { + asset_info = it_own->second; + asset_flags |= aif_own; + return true; + } + // whitelisted? auto it_white = m_whitelisted_assets.find(asset_id); if (it_white != m_whitelisted_assets.end()) @@ -3840,18 +3849,41 @@ bool wallet2::get_asset_info(const crypto::public_key& asset_id, currency::asset return true; } - auto it_own = m_own_asset_descriptors.find(asset_id); - if (it_own != m_own_asset_descriptors.end()) - { - asset_info = it_own->second; - asset_flags |= aif_own; - return true; - } - return false; } //---------------------------------------------------------------------------------------------------- +size_t wallet2::get_asset_decimal_point(const crypto::public_key& asset_id, size_t result_if_not_found /* = 0 */) const +{ + if (asset_id == currency::native_coin_asset_id) + return currency::get_native_coin_asset_descriptor().decimal_point; + // whitelisted? + auto it_white = m_whitelisted_assets.find(asset_id); + if (it_white != m_whitelisted_assets.end()) + return it_white->second.decimal_point; + + // custom asset? + auto it_cust = m_custom_assets.find(asset_id); + if (it_cust != m_custom_assets.end()) + return it_cust->second.decimal_point; + + auto it_own = m_own_asset_descriptors.find(asset_id); + if (it_own != m_own_asset_descriptors.end()) + return it_own->second.decimal_point; + + return result_if_not_found; // if not overriden, use the 0 decimal point (raw numbers) as the default +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::get_asset_decimal_point(const crypto::public_key& asset_id, size_t* p_decimal_point_result) const +{ + size_t decimal_point = get_asset_decimal_point(asset_id, SIZE_MAX); + if (decimal_point == SIZE_MAX) + return false; + if (p_decimal_point_result != nullptr) + *p_decimal_point_result = decimal_point; + return true; +} +//---------------------------------------------------------------------------------------------------- uint64_t wallet2::balance() const { uint64_t stub = 0; @@ -3956,7 +3988,7 @@ bool wallet2::generate_utxo_defragmentation_transaction_if_needed(currency::tran //---------------------------------------------------------------------------------------------------- std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/, bool show_only_unknown /*= false*/, const std::string& filter_asset_ticker /*= std::string{}*/) const { - static const char* header = "index amount ticker g_index flags block tx out# asset id"; + static const char* header = " index amount ticker g_index flags block tx out# asset id"; std::stringstream ss; ss << header << ENDL; size_t count = 0; @@ -3968,6 +4000,7 @@ std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool inclu if ((td.is_spent() && !include_spent) || (!td.is_spent() && !include_unspent)) continue; + bool is_locked = !is_transfer_unlocked(td); bool native_coin = td.is_native_coin(); asset_descriptor_base adb{}; uint32_t asset_info_flags{}; @@ -3981,7 +4014,7 @@ std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool inclu if (!filter_asset_ticker.empty() && adb.ticker != filter_asset_ticker) continue; - ss << std::right << + ss << std::right << (is_locked ? "*" : " ") << std::setw(5) << i << " " << std::setw(21) << print_asset_money(td.m_amount, adb.decimal_point) << " " << std::setw(6) << std::left << (native_coin ? std::string(" ") : adb.ticker) << " " << std::right << @@ -4012,11 +4045,12 @@ std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool inclu std::string wallet2::get_balance_str() const { // balance unlocked / [balance total] ticker asset id - // 1391306.970000000000 / 1391306.970000000000 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a - // 1391306.97 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a - // 106.971 / 206.4 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // 0.21 / 98.51 DP2 a6974d5874e97e5f4ed5ad0a62f0975edbccb1bb55502fc75c7fe808f12f44d3 + // 190.123456789012 / 199.123456789012 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // 98.0 BGTVUW af2b12f3033337f9aea1845a6bc3fc966ed4d13227a3ace7706fca7dbcdaa7e2 + // 1000.034 DP3 d4aba1020f26927571771e04b585b4ffb211f52708d5e4c465bbdfa4a12e6271 - static const char* header = " balance unlocked / [balance total] ticker asset id"; + static const char* header = " balance unlocked / [balance total] ticker asset id"; std::stringstream ss; ss << header << ENDL; @@ -4025,11 +4059,11 @@ std::string wallet2::get_balance_str() const balance(balances, mined); for (const tools::wallet_public::asset_balance_entry& b : balances) { - ss << " " << std::left << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(b.unlocked, b.asset_info.decimal_point); + ss << " " << std::left << std::setw(21) << print_fixed_decimal_point_with_trailing_spaces(b.unlocked, b.asset_info.decimal_point); if (b.total == b.unlocked) - ss << " "; + ss << std::string(21 + 3, ' '); else - ss << " / " << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(b.total, b.asset_info.decimal_point); + ss << " / " << std::setw(21) << print_fixed_decimal_point_with_trailing_spaces(b.total, b.asset_info.decimal_point); ss << " " << std::setw(8) << std::left << b.asset_info.ticker << " " << b.asset_info.asset_id << ENDL; } @@ -4038,12 +4072,16 @@ std::string wallet2::get_balance_str() const //---------------------------------------------------------------------------------------------------- std::string wallet2::get_balance_str_raw() const { - // balance unlocked / [balance total] ticker asset id - // 1391306.970000000000 / 1391306.970000000000 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a - // 1391306.97 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a - // 106.971 / 206.4 ZANO d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // balance unlocked / [balance total] DP asset id + // 0.21 / 98.51 2 a6974d5874e97e5f4ed5ad0a62f0975edbccb1bb55502fc75c7fe808f12f44d3 + // 190.123456789012 / 199.123456789012 12 d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a + // 98.0 12 af2b12f3033337f9aea1845a6bc3fc966ed4d13227a3ace7706fca7dbcdaa7e2 + // 1000.034 3 d4aba1020f26927571771e04b585b4ffb211f52708d5e4c465bbdfa4a12e6271 + //WHITELIST: + // 7d3f348fbebfffc4e61a3686189cf870ea393e1c88b8f636acbfdacf9e4b2db2 CT + // ... - static const char* header = " balance unlocked / [balance total] asset id"; + static const char* header = " balance unlocked / [balance total] DP asset id"; std::stringstream ss; ss << header << ENDL; @@ -4053,32 +4091,41 @@ std::string wallet2::get_balance_str_raw() const for(const auto& entry : balances_map) { - ss << " " << std::left << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(entry.second.unlocked, 12); + size_t decimal_point = 0; + bool has_known_decimal_point = get_asset_decimal_point(entry.first, &decimal_point); + ss << " " << std::left << std::setw(21) << print_fixed_decimal_point_with_trailing_spaces(entry.second.unlocked, decimal_point); if(entry.second.total == entry.second.unlocked) - ss << " "; + ss << std::string(21 + 3, ' '); else - ss << " / " << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(entry.second.total, 12); - ss << " " << std::setw(8) << std::left << entry.first << ENDL; + ss << " / " << std::setw(21) << print_fixed_decimal_point_with_trailing_spaces(entry.second.total, decimal_point); + + ss << " "; + + if (has_known_decimal_point) + ss << std::setw(2) << std::right << decimal_point; + else + ss << "??"; + + ss << " " << entry.first << ENDL; } //print whitelist ss << "WHITELIST: " << ENDL; - for(const auto& entry : m_whitelisted_assets) { ss << " " << std::left << entry.first << " " << entry.second.ticker << ENDL; } - // print whitelist + // print custom list ss << "CUSTOM LIST: " << ENDL; - for(const auto& entry : m_custom_assets) { ss << " " << std::left << entry.first << " " << entry.second.ticker << ENDL; } + // print own list ss << "OWN DESCRIPTORS LIST: " << ENDL; for(const auto& entry : m_own_asset_descriptors) @@ -5005,7 +5052,7 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco WLT_LOG_GREEN("Note: " << udtx.vin.size() << " inputs were aggregated into UTXO defragmentation tx " << get_transaction_hash(udtx), LOG_LEVEL_0); } m_core_proxy->call_COMMAND_RPC_GETBLOCKTEMPLATE(tmpl_req, tmpl_rsp); - WLT_CHECK_AND_ASSERT_MES(tmpl_rsp.status == API_RETURN_CODE_OK, false, "Failed to create block template after kernel hash found!"); + WLT_CHECK_AND_ASSERT_MES(tmpl_rsp.status == API_RETURN_CODE_OK, false, "Failed to create block template after kernel hash found! Status: " << tmpl_rsp.status); currency::block b = AUTO_VAL_INIT(b); currency::blobdata block_blob; @@ -5258,6 +5305,8 @@ void wallet2::request_alias_registration(currency::extra_alias_entry& ai, curren //---------------------------------------------------------------------------------------------------- void wallet2::deploy_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector& destinations, currency::transaction& result_tx, crypto::public_key& new_asset_id) { + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(asset_info.decimal_point <= 18, "too big decimal point: " << asset_info.decimal_point); + asset_descriptor_operation asset_reg_info = AUTO_VAL_INIT(asset_reg_info); asset_reg_info.descriptor = asset_info; asset_reg_info.operation_type = ASSET_DESCRIPTOR_OPERATION_REGISTER; @@ -5371,7 +5420,7 @@ void wallet2::burn_asset(const crypto::public_key asset_id, uint64_t amount_to_b asset_descriptor_operation asset_burn_info = AUTO_VAL_INIT(asset_burn_info); asset_burn_info.descriptor = rsp.asset_descriptor; - CHECK_AND_ASSERT_THROW_MES(asset_burn_info.descriptor.current_supply > amount_to_burn, "Wrong amount to burn (current_supply" << asset_burn_info.descriptor.current_supply << " is less then " << amount_to_burn << ")"); + CHECK_AND_ASSERT_THROW_MES(asset_burn_info.descriptor.current_supply >= amount_to_burn, "Wrong amount to burn (current_supply" << asset_burn_info.descriptor.current_supply << " is less then " << amount_to_burn << ")"); currency::tx_destination_entry dst_to_burn = AUTO_VAL_INIT(dst_to_burn); dst_to_burn.amount = amount_to_burn; @@ -6204,7 +6253,7 @@ bool wallet2::accept_ionic_swap_proposal(const wallet_public::ionic_swap_proposa { if (balances[item.asset_id].unlocked < item.amount) { - WLT_THROW_IF_FALSE_WALLET_EX_MES(false, error::not_enough_money, "", balances[item.asset_id].unlocked, item.amount, 0 /*fee*/, item.asset_id); + WLT_THROW_IF_FALSE_WALLET_EX_MES(false, error::not_enough_money, "", balances[item.asset_id].unlocked, item.amount, 0 /*fee*/, item.asset_id, get_asset_decimal_point(item.asset_id)); } if (item.asset_id == currency::native_coin_asset_id) { @@ -6557,10 +6606,13 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count_, bool use_all_decoys src.real_output = interted_it - src.outputs.begin(); src.real_output_in_tx_index = td.m_internal_output_index; - std::stringstream ss; - ss << "source entry [" << i << "], td_idx: " << J << ", "; - print_source_entry(ss, src); - WLT_LOG_L1(ss.str()); + if (epee::log_space::get_set_log_detalisation_level() >= LOG_LEVEL_1) + { + std::stringstream ss; + ss << "source entry [" << i << "], td_idx: " << J << ", "; + print_source_entry(ss, src); + WLT_LOG_L1(ss.str()); + } ++i; } @@ -7056,10 +7108,17 @@ bool wallet2::select_indices_for_transfer(assets_selection_context& needed_money { if(item.second.needed_amount == 0) continue; - auto asset_cashe_it = m_found_free_amounts.find(item.first); - WLT_THROW_IF_FALSE_WALLET_EX_MES(asset_cashe_it != m_found_free_amounts.end(), error::not_enough_money, "", item.second.found_amount, item.second.needed_amount, 0, item.first); - item.second.found_amount = select_indices_for_transfer(selected_indexes, asset_cashe_it->second, item.second.needed_amount, fake_outputs_count); - WLT_THROW_IF_FALSE_WALLET_EX_MES(item.second.found_amount >= item.second.needed_amount, error::not_enough_money, "", item.second.found_amount, item.second.needed_amount, 0, item.first); + + const crypto::public_key asset_id = item.first; + asset_descriptor_base asset_info{}; + uint32_t asset_flags = 0; + if (!get_asset_info(asset_id, asset_info, asset_flags)) + WLT_LOG_L1("select_indices_for_transfer: unknown asset id: " << asset_id); + + auto asset_cache_it = m_found_free_amounts.find(asset_id); + WLT_THROW_IF_FALSE_WALLET_EX_MES(asset_cache_it != m_found_free_amounts.end(), error::not_enough_money, "", item.second.found_amount, item.second.needed_amount, 0, asset_id, asset_info.decimal_point); + item.second.found_amount = select_indices_for_transfer(selected_indexes, asset_cache_it->second, item.second.needed_amount, fake_outputs_count, asset_id, asset_info.decimal_point); + WLT_THROW_IF_FALSE_WALLET_EX_MES(item.second.found_amount >= item.second.needed_amount, error::not_enough_money, "", item.second.found_amount, item.second.needed_amount, 0, asset_id, asset_info.decimal_point); } if (m_current_context.pconstruct_tx_param && m_current_context.pconstruct_tx_param->need_at_least_1_zc) { @@ -7081,11 +7140,13 @@ bool wallet2::select_indices_for_transfer(assets_selection_context& needed_money return true; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::select_indices_for_transfer(std::vector& selected_indexes, free_amounts_cache_type& found_free_amounts, uint64_t needed_money, uint64_t fake_outputs_count_) +uint64_t wallet2::select_indices_for_transfer(std::vector& selected_indexes, free_amounts_cache_type& found_free_amounts, uint64_t needed_money, uint64_t fake_outputs_count_, + const crypto::public_key& asset_id, size_t decimal_point) { - WLT_LOG_GREEN("Selecting indices for transfer of " << print_money_brief(needed_money) << " with " << fake_outputs_count_ << " fake outs, found_free_amounts.size()=" << found_free_amounts.size() << "...", LOG_LEVEL_0); + WLT_LOG_GREEN("Selecting indices for transfer of " << print_money_brief(needed_money, decimal_point) << " with " << fake_outputs_count_ << " fake outs, found_free_amounts.size()=" << found_free_amounts.size() << + (asset_id == native_coin_asset_id ? std::string() : std::string(", asset_id: ") + crypto::pod_to_hex(asset_id)) << "...", LOG_LEVEL_0); uint64_t found_money = 0; - //uint64_t found_zc_input = false; + size_t outputs_found = 0; std::string selected_amounts_str; while (found_money < needed_money && found_free_amounts.size()) { @@ -7105,7 +7166,8 @@ uint64_t wallet2::select_indices_for_transfer(std::vector& selected_in found_money += it->first; selected_indexes.push_back(*it->second.begin()); WLT_LOG_L2("Selected index: " << *it->second.begin() << ", transfer_details: " << ENDL << epee::serialization::store_t_to_json(m_transfers[*it->second.begin()])); - selected_amounts_str += (selected_amounts_str.empty() ? "" : "+") + print_money_brief(it->first); + selected_amounts_str += (selected_amounts_str.empty() ? "" : "+") + print_money_brief(it->first, decimal_point); + ++outputs_found; } it->second.erase(it->second.begin()); if (!it->second.size()) @@ -7113,7 +7175,8 @@ uint64_t wallet2::select_indices_for_transfer(std::vector& selected_in } - WLT_LOG_GREEN("Found " << print_money_brief(found_money) << " as " << selected_indexes.size() << " out(s): " << selected_amounts_str << ", found_free_amounts.size()=" << found_free_amounts.size(), LOG_LEVEL_0); + WLT_LOG_GREEN("Found " << print_money_brief(found_money, decimal_point) << " as " << outputs_found << " out(s): " << selected_amounts_str << ", found_free_amounts.size()=" << found_free_amounts.size() << + (asset_id == native_coin_asset_id ? std::string() : std::string(", asset_id: ") + crypto::pod_to_hex(asset_id)), LOG_LEVEL_0); return found_money; } //---------------------------------------------------------------------------------------------------- @@ -7337,13 +7400,15 @@ void wallet2::print_source_entry(std::stringstream& output, const currency::tx_s for(auto& el : src.outputs) ss << el.out_reference << " "; - output << "amount: " << print_money_brief(src.amount) << (src.is_zc() ? " (hidden)" : "") - << ", real_output: " << src.real_output + output << "amount: " << print_money_brief(src.amount, get_asset_decimal_point(src.asset_id)) << (src.is_zc() ? "" : " (bare)"); + + if (src.asset_id != currency::native_coin_asset_id) + output << " (" << print16(src.asset_id) << ")"; + + output << ", real_output: " << src.real_output << ", real_output_in_tx_index: " << src.real_output_in_tx_index << ", indexes: " << ss.str(); - if (src.asset_id != currency::native_coin_asset_id) - output << ", asset_id: " << print16(src.asset_id); } //---------------------------------------------------------------------------------------------------- bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const @@ -7588,8 +7653,8 @@ void wallet2::finalize_transaction(currency::finalize_tx_param& ftp, currency::f ftp, result); //TIME_MEASURE_FINISH_MS(construct_tx_time); THROW_IF_FALSE_WALLET_EX(r, error::tx_not_constructed, ftp.sources, ftp.prepared_destinations, ftp.unlock_time); - uint64_t effective_fee = get_tx_fee(result.tx); - THROW_IF_FALSE_WALLET_CMN_ERR_EX(effective_fee <= WALLET_TX_MAX_ALLOWED_FEE, "tx fee is WAY too big: " << print_money_brief(effective_fee) << ", max allowed is " << print_money_brief(WALLET_TX_MAX_ALLOWED_FEE)); + uint64_t effective_fee = 0; + THROW_IF_FALSE_WALLET_CMN_ERR_EX(!get_tx_fee(result.tx, effective_fee) || effective_fee <= WALLET_TX_MAX_ALLOWED_FEE, "tx fee is WAY too big: " << print_money_brief(effective_fee) << ", max allowed is " << print_money_brief(WALLET_TX_MAX_ALLOWED_FEE)); //TIME_MEASURE_START_MS(sign_ms_input_time); if (ftp.multisig_id != currency::null_hash) @@ -7605,7 +7670,8 @@ void wallet2::finalize_transaction(currency::finalize_tx_param& ftp, currency::f } //TIME_MEASURE_FINISH_MS(sign_ms_input_time); - THROW_IF_FALSE_WALLET_EX(get_object_blobsize(result.tx) < CURRENCY_MAX_TRANSACTION_BLOB_SIZE, error::tx_too_big, result.tx, m_upper_transaction_size_limit); + size_t tx_blob_size = tx_to_blob(result.tx).size(); + THROW_IF_FALSE_WALLET_EX(tx_blob_size < CURRENCY_MAX_TRANSACTION_BLOB_SIZE, error::tx_too_big, result.tx, m_upper_transaction_size_limit); if (store_tx_secret_key) m_tx_keys.insert(std::make_pair(get_transaction_hash(result.tx), result.one_time_key)); @@ -7823,7 +7889,7 @@ void wallet2::transfer(construct_tx_param& ctp, void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id, uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, uint64_t& amount_swept, currency::transaction* p_result_tx /* = nullptr */, std::string* p_filename_or_unsigned_tx_blob_str /* = nullptr */) { - static const size_t estimated_bytes_per_input = 78; + static const size_t estimated_bytes_per_input = 85; const size_t estimated_max_inputs = static_cast(CURRENCY_MAX_TRANSACTION_BLOB_SIZE / (estimated_bytes_per_input * (fake_outs_count + 1.5))); // estimated number of maximum tx inputs under the tx size limit const size_t tx_sources_for_querying_random_outs_max = estimated_max_inputs * 2; @@ -7838,9 +7904,10 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public for (uint64_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; + size_t fake_outs_count_for_td = is_auditable() ? 0 : (td.is_zc() ? m_core_runtime_config.hf4_minimum_mixins : fake_outs_count); uint64_t amount = td.amount(); - if (amount < threshold_amount && - is_transfer_ready_to_go(td, fake_outs_count)) + if (amount < threshold_amount && td.is_native_coin() && + is_transfer_ready_to_go(td, fake_outs_count_for_td)) { selected_transfers.push_back(i); outs_total += 1; @@ -7916,9 +7983,12 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public auto try_construct_tx = [this, &selected_transfers, &rpc_get_random_outs_resp, &fake_outs_count, &fee, &destination_addr] (size_t st_index_upper_boundary, currency::finalize_tx_param& ftp, uint64_t& amount_swept) -> try_construct_result_t { - // prepare inputs amount_swept = 0; + ftp.gen_context = tx_generation_context{}; ftp.sources.clear(); + ftp.prepared_destinations.clear(); + + // prepare inputs ftp.sources.resize(st_index_upper_boundary); WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(st_index_upper_boundary <= selected_transfers.size(), "index_upper_boundary = " << st_index_upper_boundary << ", selected_transfers.size() = " << selected_transfers.size()); for (size_t st_index = 0; st_index < st_index_upper_boundary; ++st_index) @@ -8013,7 +8083,7 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public size_t st_index_upper_boundary = std::min(selected_transfers.size(), estimated_max_inputs); try_construct_result_t res = try_construct_tx(st_index_upper_boundary, ftp, amount_swept); - + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(res != rc_too_few_outputs, st_index_upper_boundary << " biggest unspent outputs have total amount of " << print_money_brief(amount_swept) << " which is less than required fee: " << print_money_brief(fee) << ", transaction cannot be constructed"); @@ -8087,10 +8157,10 @@ void wallet2::sweep_below(size_t fake_outs_count, const currency::account_public transaction local_tx; transaction* p_tx = p_result_tx != nullptr ? p_result_tx : &local_tx; - *p_tx = AUTO_VAL_INIT_T(transaction); + *p_tx = transaction{}; try { - crypto::secret_key sk = AUTO_VAL_INIT(sk); + crypto::secret_key sk{}; finalize_transaction(ftp, *p_tx, sk, true); } catch (...) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 448af662..2fa7601b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -444,7 +444,9 @@ namespace tools enum asset_info_flags_t : uint32_t { aif_none = 0, aif_whitelisted = 1 << 0, aif_own = 1 << 1 }; bool get_asset_info(const crypto::public_key& asset_id, currency::asset_descriptor_base& asset_info, uint32_t& asset_flags) const; - + size_t get_asset_decimal_point(const crypto::public_key& asset_id, size_t result_if_not_found = 0) const; + bool get_asset_decimal_point(const crypto::public_key& asset_id, size_t* p_decimal_point_result) const; + void transfer(uint64_t amount, const currency::account_public_address& acc, const crypto::public_key& asset_id = currency::native_coin_asset_id); void transfer(uint64_t amount, size_t fake_outs_count, const currency::account_public_address& acc, uint64_t fee = TX_DEFAULT_FEE, const crypto::public_key& asset_id = currency::native_coin_asset_id); void transfer(uint64_t amount, const currency::account_public_address& acc, currency::transaction& result_tx, const crypto::public_key& asset_id = currency::native_coin_asset_id); @@ -607,7 +609,7 @@ namespace tools bool is_transfer_ready_to_go(const transfer_details& td, uint64_t fake_outputs_count) const; bool is_transfer_able_to_go(const transfer_details& td, uint64_t fake_outputs_count) const; - uint64_t select_indices_for_transfer(std::vector& ind, free_amounts_cache_type& found_free_amounts, uint64_t needed_money, uint64_t fake_outputs_count); + uint64_t select_indices_for_transfer(std::vector& ind, free_amounts_cache_type& found_free_amounts, uint64_t needed_money, uint64_t fake_outputs_count, const crypto::public_key& asset_id, size_t decimal_point); bool select_indices_for_transfer(assets_selection_context& needed_money_map, uint64_t fake_outputs_count, std::vector& selected_indexes); //PoS diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index bfab00d5..90dbf48c 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -350,12 +350,13 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct not_enough_money : public transfer_error { - not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee, const crypto::public_key& asset_id) + not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee, const crypto::public_key& asset_id, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT) : transfer_error(std::move(loc), "") , m_available(availbable) , m_tx_amount(tx_amount) , m_fee(fee) , m_asset_id(asset_id) + , m_decimal_point(decimal_point) { } @@ -367,9 +368,9 @@ namespace tools { std::ostringstream ss; ss << transfer_error::to_string() << - "available: " << currency::print_money_brief(m_available) << - ", required: " << currency::print_money_brief(m_tx_amount + m_fee) << - " = " << currency::print_money_brief(m_tx_amount) << " + " << currency::print_money_brief(m_fee) << " (fee)"; + "available: " << currency::print_money_brief(m_available, m_decimal_point) << + ", required: " << currency::print_money_brief(m_tx_amount + m_fee, m_decimal_point) << + " = " << currency::print_money_brief(m_tx_amount, m_decimal_point) << " + " << currency::print_money_brief(m_fee) << " (fee)"; if (m_asset_id != currency::native_coin_asset_id) ss << ", asset_id: " << m_asset_id; return ss.str(); @@ -380,6 +381,7 @@ namespace tools uint64_t m_tx_amount; uint64_t m_fee; crypto::public_key m_asset_id; + size_t m_decimal_point; }; struct no_zc_inputs : public transfer_error diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index b9a5ddb9..c71cb2d2 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -1989,7 +1989,7 @@ namespace wallet_public currency::asset_descriptor_base asset_descriptor; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in destinations should be set to 0000000000000000000000000000000000000000000000000000000000000000") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in the destinations is irreleant and can be omitted.") DOC_EXMP_AUTO(1) DOC_END KV_SERIALIZE(asset_descriptor) DOC_DSCR("Descriptor that holds all information about asset - ticker, emission, description etc") DOC_END END_KV_SERIALIZE_MAP() }; @@ -2018,7 +2018,7 @@ namespace wallet_public BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Id of the asset to emit more coins") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END - KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in destinations should be set to 0000000000000000000000000000000000000000000000000000000000000000") DOC_EXMP_AUTO(1) DOC_END + KV_SERIALIZE(destinations) DOC_DSCR("Addresses where to receive emitted coins. Asset id in the destinations is irreleant and can be omitted.") DOC_EXMP_AUTO(1) DOC_END END_KV_SERIALIZE_MAP() }; @@ -2058,6 +2058,30 @@ namespace wallet_public }; }; + struct COMMAND_ASSETS_BURN + { + DOC_COMMAND("Burn some owned amount of the coins for the given asset."); + + struct request + { + crypto::public_key asset_id; + uint64_t burn_amount; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Id of the asset to burn") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(burn_amount) DOC_DSCR("Amount to burn") DOC_EXMP(10000000) DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + crypto::hash result_tx; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(result_tx) DOC_DSCR("Id of transaction that carries asset burn operation") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; } // namespace wallet_rpc } // namespace tools diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 9efaf197..39c75e27 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1282,9 +1282,16 @@ namespace tools bool wallet_rpc_server::on_assets_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); + currency::transaction result_tx; std::vector currency_destinations; rpc_destinations_to_currency_destination(req.destinations, currency_destinations); + //fix for default asset_id + for (auto& d : currency_destinations) + { + d.asset_id = currency::null_pkey; + } + w.get_wallet()->deploy_new_asset(req.asset_descriptor, currency_destinations, result_tx, res.new_asset_id); res.result_tx = currency::get_transaction_hash(result_tx); return true; @@ -1297,6 +1304,11 @@ namespace tools currency::transaction result_tx; std::vector currency_destinations; rpc_destinations_to_currency_destination(req.destinations, currency_destinations); + //fix for default asset_id + for (auto& d : currency_destinations) + { + d.asset_id = currency::null_pkey; + } w.get_wallet()->emit_asset(req.asset_id, currency_destinations, result_tx); res.result_tx = currency::get_transaction_hash(result_tx); @@ -1316,6 +1328,16 @@ namespace tools WALLET_RPC_CATCH_TRY_ENTRY(); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_assets_burn(const wallet_public::COMMAND_ASSETS_BURN::request& req, wallet_public::COMMAND_ASSETS_BURN::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + currency::transaction result_tx; + w.get_wallet()->burn_asset(req.asset_id, req.burn_amount, result_tx); + res.result_tx = currency::get_transaction_hash(result_tx); + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 9ea31394..4eac9211 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -149,6 +149,8 @@ namespace tools MAP_JON_RPC_WE("deploy_asset", on_assets_deploy, wallet_public::COMMAND_ASSETS_DEPLOY) MAP_JON_RPC_WE("emit_asset", on_assets_emit, wallet_public::COMMAND_ASSETS_EMIT) MAP_JON_RPC_WE("update_asset", on_assets_update, wallet_public::COMMAND_ASSETS_UPDATE) + MAP_JON_RPC_WE("burn_asset", on_assets_burn, wallet_public::COMMAND_ASSETS_BURN) + //MULTIWALLET APIs MAP_JON_RPC_WE("mw_get_wallets", on_mw_get_wallets, wallet_public::COMMAND_MW_GET_WALLETS) @@ -219,7 +221,7 @@ namespace tools bool on_assets_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_assets_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_assets_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx); - + bool on_assets_burn(const wallet_public::COMMAND_ASSETS_BURN::request& req, wallet_public::COMMAND_ASSETS_BURN::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_mw_select_wallet(const wallet_public::COMMAND_MW_SELECT_WALLET::request& req, wallet_public::COMMAND_MW_SELECT_WALLET::response& res, epee::json_rpc::error& er, connection_context& cntx); diff --git a/src/wallet/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index b039e86a..3a08b448 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -1495,11 +1495,12 @@ std::string wallets_manager::request_alias_update(const currency::alias_rpc_deta std::string wallets_manager::transfer(uint64_t wallet_id, const view::transfer_params& tp, currency::transaction& res_tx) { - std::vector dsts; if(!tp.destinations.size()) return API_RETURN_CODE_BAD_ARG_EMPTY_DESTINATIONS; + GET_WALLET_BY_ID(wallet_id, w); + uint64_t fee = tp.fee; //payment_id std::vector attachments; @@ -1538,8 +1539,13 @@ std::string wallets_manager::transfer(uint64_t wallet_id, const view::transfer_p return API_RETURN_CODE_BAD_ARG_INVALID_ADDRESS; } - - if(!currency::parse_amount(dsts.back().amount, d.amount)) + size_t decimal_point = 0; + if (!w->get()->get_asset_decimal_point(d.asset_id, &decimal_point)) + { + return API_RETURN_CODE_BAD_ARG_UNKNOWN_DECIMAL_POINT; + } + + if(!currency::parse_amount(d.amount, dsts.back().amount, decimal_point)) { return API_RETURN_CODE_BAD_ARG_WRONG_AMOUNT; } @@ -1552,8 +1558,6 @@ std::string wallets_manager::transfer(uint64_t wallet_id, const view::transfer_p dsts.back().asset_id = d.asset_id; } - GET_WALLET_BY_ID(wallet_id, w); - if (payment_id.size()) { if (!currency::is_payment_id_size_ok(payment_id)) diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 010c2230..51e02734 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1070,7 +1070,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_wallet_refreshing_on_chain_switch_2); GENERATE_AND_PLAY(gen_wallet_unconfirmed_tx_from_tx_pool); GENERATE_AND_PLAY_HF(gen_wallet_save_load_and_balance, "*"); - GENERATE_AND_PLAY_HF(gen_wallet_mine_pos_block, "3"); + GENERATE_AND_PLAY_HF(gen_wallet_mine_pos_block, "3-*"); GENERATE_AND_PLAY(gen_wallet_unconfirmed_outdated_tx); GENERATE_AND_PLAY(gen_wallet_unlock_by_block_and_by_time); GENERATE_AND_PLAY(gen_wallet_payment_id); @@ -1102,6 +1102,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(wallet_rpc_transfer); GENERATE_AND_PLAY(wallet_rpc_alias_tests); GENERATE_AND_PLAY_HF(wallet_rpc_exchange_suite, "3,4"); + GENERATE_AND_PLAY_HF(wallet_true_rpc_pos_mining, "4-*"); GENERATE_AND_PLAY(wallet_chain_switch_with_spending_the_same_ki); GENERATE_AND_PLAY(wallet_sending_to_integrated_address); GENERATE_AND_PLAY_HF(block_template_blacklist_test, "4-*"); @@ -1288,6 +1289,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(zarcanum_block_with_txs); 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(pos_fuse_test, "4-*"); diff --git a/tests/core_tests/multiassets_test.cpp b/tests/core_tests/multiassets_test.cpp index 240b3bb3..94a938d2 100644 --- a/tests/core_tests/multiassets_test.cpp +++ b/tests/core_tests/multiassets_test.cpp @@ -823,3 +823,67 @@ bool assets_and_pos_mining::c1(currency::core& c, size_t ev_index, const std::ve return true; } + +//------------------------------------------------------------------------------ + +asset_emission_and_unconfirmed_balance::asset_emission_and_unconfirmed_balance() +{ + REGISTER_CALLBACK_METHOD(asset_emission_and_unconfirmed_balance, c1); +} + +bool asset_emission_and_unconfirmed_balance::generate(std::vector& events) const +{ + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + //account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + miner_acc.generate(); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool asset_emission_and_unconfirmed_balance::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + miner_wlt->refresh(); + + asset_descriptor_base adb{}; + adb.total_max_supply = UINT64_MAX; + adb.full_name = "2**64"; + adb.ticker = "2POWER64"; + + std::vector destinations; + destinations.emplace_back(adb.total_max_supply, m_accounts[MINER_ACC_IDX].get_public_address(), null_pkey); + + currency::transaction tx{}; + crypto::public_key asset_id = currency::null_pkey; + miner_wlt->deploy_new_asset(adb, destinations, tx, asset_id); + LOG_PRINT_L0("Deployed new asset: " << asset_id << ", tx_id: " << currency::get_transaction_hash(tx)); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + bool stub_bool = 0; + miner_wlt->refresh(); + miner_wlt->scan_tx_pool(stub_bool); + uint64_t total, unlocked, awaiting_in, awaiting_out, mined; + balance_via_wallet(*miner_wlt, asset_id, &total, &unlocked, &awaiting_in, &awaiting_out, &mined); + CHECK_AND_ASSERT_EQ(total, UINT64_MAX); + CHECK_AND_ASSERT_EQ(unlocked, 0); + CHECK_AND_ASSERT_EQ(awaiting_in, UINT64_MAX); + CHECK_AND_ASSERT_EQ(awaiting_out, 0); + //CHECK_AND_ASSERT_EQ(mined, 0); + + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + return true; +} diff --git a/tests/core_tests/multiassets_test.h b/tests/core_tests/multiassets_test.h index 12fefcce..bb2fdacc 100644 --- a/tests/core_tests/multiassets_test.h +++ b/tests/core_tests/multiassets_test.h @@ -41,3 +41,9 @@ struct assets_and_pos_mining : public wallet_test bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; +struct asset_emission_and_unconfirmed_balance : public wallet_test +{ + asset_emission_and_unconfirmed_balance(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index 3fa4b2d7..47b6d21b 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -801,3 +801,126 @@ bool wallet_rpc_exchange_suite::c1(currency::core& c, size_t ev_index, const std return true; } + +//------------------------------------------------------------------------------ + +wallet_true_rpc_pos_mining::wallet_true_rpc_pos_mining() +{ + REGISTER_CALLBACK_METHOD(wallet_true_rpc_pos_mining, c1); +} + +bool wallet_true_rpc_pos_mining::generate(std::vector& events) const +{ + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3); + + DO_CALLBACK(events, "c1"); + + return true; +} + +#include + +bool wallet_true_rpc_pos_mining::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr miner_wlt = init_playtime_test_wallet_with_true_http_rpc(events, c, MINER_ACC_IDX); + std::shared_ptr alice_wlt = init_playtime_test_wallet_with_true_http_rpc(events, c, ALICE_ACC_IDX); + + /*currency::t_currency_protocol_handler m_cprotocol(c, nullptr); + nodetool::node_server > p2p(m_cprotocol); + bc_services::bc_offers_service bos(nullptr); + bos.set_disabled(true); + currency::core_rpc_server core_rpc_wrapper(c, p2p, bos); + core_rpc_wrapper.set_ignore_connectivity_status(true); + + boost::program_options::options_description desc_options; + currency::core_rpc_server::init_options(desc_options); + boost::program_options::variables_map vm{}; + char* argv[] = {"--rpc-bind-ip=127.0.0.1", "--rpc-bind-port=33777"}; + boost::program_options::store(boost::program_options::parse_command_line(sizeof argv / sizeof argv[0], argv, desc_options), vm); + r = core_rpc_wrapper.init(vm); + CHECK_AND_ASSERT_MES(r, false, "rpc server init failed"); + r = core_rpc_wrapper.run(1, false); + CHECK_AND_ASSERT_MES(r, 1, "rpc server run failed"); + auto slh = epee::misc_utils::create_scope_leave_handler([&](){ + core_rpc_wrapper.deinit(); + }); + + + auto http_core_proxy = std::shared_ptr(new tools::default_http_core_proxy()); + http_core_proxy->set_connectivity(5000, 1); + CHECK_AND_ASSERT_MES(http_core_proxy->set_connection_addr("127.0.0.1:33777"), false, ""); + CHECK_AND_ASSERT_MES(http_core_proxy->check_connection(), false, "no connection"); + + miner_wlt->set_core_proxy(http_core_proxy);*/ + + uint64_t top_height = c.get_top_block_height(); + + size_t blocks_fetched = 0; + miner_wlt->refresh(blocks_fetched); + CHECK_AND_ASSERT_EQ(blocks_fetched, top_height); + + alice_wlt->refresh(blocks_fetched); + CHECK_AND_ASSERT_EQ(blocks_fetched, top_height); + + //uint64_t balance_unlocked = 1, balance_awaiting_in = 1, balance_awaiting_out = 1, balance_mined = 1; + //uint64_t miner_initial_balance = miner_wlt->balance(balance_unlocked, balance_awaiting_in, balance_awaiting_out, balance_mined); + //CHECK_AND_ASSERT_EQ(miner_initial_balance, PREMINE_AMOUNT + CURRENCY_BLOCK_REWARD * top_height); + uint64_t miner_amount = PREMINE_AMOUNT + CURRENCY_BLOCK_REWARD * top_height; + uint64_t expected_unlocked = miner_amount - (CURRENCY_MINED_MONEY_UNLOCK_WINDOW - 1) * COIN; + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt, "miner", miner_amount, INVALID_BALANCE_VAL, expected_unlocked, 0, 0), false, ""); + + CHECK_AND_ASSERT_MES(miner_wlt->try_mint_pos(), false, "try_mint_pos failed"); + + CHECK_AND_ASSERT_EQ(c.get_top_block_height(), top_height + 1); + + miner_wlt->refresh(blocks_fetched); + CHECK_AND_ASSERT_EQ(blocks_fetched, 1); + + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt, "miner", miner_amount + COIN), false, ""); + + uint64_t amount_to_alice = MK_TEST_COINS(500); + miner_wlt->transfer(amount_to_alice, m_accounts[ALICE_ACC_IDX].get_public_address()); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "enexpected pool txs count: " << c.get_pool_transactions_count()); + CHECK_AND_ASSERT_MES(miner_wlt->try_mint_pos(), false, "try_mint_pos failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "enexpected pool txs count: " << c.get_pool_transactions_count()); + + miner_wlt->refresh(blocks_fetched); + CHECK_AND_ASSERT_EQ(blocks_fetched, 1); + + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt, "miner", miner_amount + 2 * COIN - amount_to_alice - TESTS_DEFAULT_FEE), false, ""); + + // Alice + + //alice_wlt->refresh(blocks_fetched); + //CHECK_AND_ASSERT_EQ(blocks_fetched, 2); + + //CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", amount_to_alice, 0, 0, 0, 0), false, ""); + + //r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + //CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + //alice_wlt->refresh(blocks_fetched); + //CHECK_AND_ASSERT_EQ(blocks_fetched, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + // + //CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", amount_to_alice, 0, amount_to_alice, 0, 0), false, ""); + + //std::this_thread::yield(); + //std::this_thread::sleep_for( std::chrono::milliseconds(1) ); + //CHECK_AND_ASSERT_MES(alice_wlt->try_mint_pos(), false, "try_mint_pos failed"); + + //alice_wlt->refresh(blocks_fetched); + //CHECK_AND_ASSERT_EQ(blocks_fetched, 1); + + //CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", amount_to_alice + CURRENCY_BLOCK_REWARD, 0, 0, 0, 0), false, ""); + + return true; +} diff --git a/tests/core_tests/wallet_rpc_tests.h b/tests/core_tests/wallet_rpc_tests.h index 0d939778..3793ad78 100644 --- a/tests/core_tests/wallet_rpc_tests.h +++ b/tests/core_tests/wallet_rpc_tests.h @@ -49,3 +49,9 @@ struct wallet_rpc_exchange_suite : public wallet_test bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; +struct wallet_true_rpc_pos_mining : public wallet_test +{ + wallet_true_rpc_pos_mining(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; diff --git a/tests/core_tests/wallet_tests.cpp b/tests/core_tests/wallet_tests.cpp index ef10bf61..cba1fadb 100644 --- a/tests/core_tests/wallet_tests.cpp +++ b/tests/core_tests/wallet_tests.cpp @@ -639,8 +639,19 @@ bool gen_wallet_mine_pos_block::c1(currency::core& c, size_t ev_index, const std block top_block = AUTO_VAL_INIT(top_block); bool r = c.get_blockchain_storage().get_top_block(top_block); CHECK_AND_ASSERT_MES(r && is_pos_block(top_block), false, "get_top_block failed or smth goes wrong"); - uint64_t top_block_reward = get_outs_money_amount(top_block.miner_tx); - CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", top_block_reward, top_block_reward - MK_TEST_COINS(2000), 0, 0, 0), false, ""); + + uint64_t top_block_reward = 0, expected_mined = 0; + if (c.get_blockchain_storage().is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) + { + top_block_reward = MK_TEST_COINS(2000) + CURRENCY_BLOCK_REWARD; + expected_mined = INVALID_BALANCE_VAL; // Don't check mined balace for post-HF4 due to a lack of mined balance correctness TODO -- sowle + } + else + { + top_block_reward = get_outs_money_amount(top_block.miner_tx); + expected_mined = top_block_reward - MK_TEST_COINS(2000); + } + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", top_block_reward, expected_mined, 0, 0, 0), false, ""); alice_wlt->reset_password(g_wallet_password); alice_wlt->store(g_wallet_filename); diff --git a/tests/core_tests/wallet_tests_basic.cpp b/tests/core_tests/wallet_tests_basic.cpp index 43707ac8..87066e8b 100644 --- a/tests/core_tests/wallet_tests_basic.cpp +++ b/tests/core_tests/wallet_tests_basic.cpp @@ -99,3 +99,8 @@ std::shared_ptr wallet_test::init_playtime_test_wallet(const std return init_playtime_test_wallet(events, c, m_accounts[account_index]); } +std::shared_ptr wallet_test::init_playtime_test_wallet_with_true_http_rpc(const std::vector& events, currency::core& c, size_t account_index) const +{ + CHECK_AND_ASSERT_THROW_MES(account_index < m_accounts.size(), "Invalid account index"); + return init_playtime_test_wallet_t(events, c, m_accounts[account_index], true); +} diff --git a/tests/core_tests/wallet_tests_basic.h b/tests/core_tests/wallet_tests_basic.h index bc45bcc8..6515b4a7 100644 --- a/tests/core_tests/wallet_tests_basic.h +++ b/tests/core_tests/wallet_tests_basic.h @@ -1,10 +1,15 @@ -// Copyright (c) 2014-2018 Zano Project +// Copyright (c) 2014-2024 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once #include "chaingen.h" +#include "currency_protocol/currency_protocol_handler.h" +#include "currency_core/currency_core.h" +#include "p2p/net_node.h" +#include "currency_core/bc_offers_service.h" +#include "rpc/core_rpc_server.h" struct wallet_test : virtual public test_chain_unit_enchanced { @@ -22,11 +27,30 @@ struct wallet_test : virtual public test_chain_unit_enchanced static std::string get_test_account_name_by_id(size_t acc_id); template - std::shared_ptr init_playtime_test_wallet_t(const std::vector& events, currency::core& c, const currency::account_base& acc) const + std::shared_ptr init_playtime_test_wallet_t(const std::vector& events, currency::core& c, const currency::account_base& acc, bool true_http_rpc = false) const { CHECK_AND_ASSERT_THROW_MES(events.size() > 0 && events[0].type() == typeid(currency::block), "Invalid events queue, can't find genesis block at the beginning"); crypto::hash genesis_hash = get_block_hash(boost::get(events[0])); + if (true_http_rpc) + { + m_core_proxy = std::make_shared(); + m_core_proxy->set_connectivity(100, 1); + CHECK_AND_ASSERT_THROW_MES(m_core_proxy->set_connection_addr("127.0.0.1:33777"), "set_connection_addr failed"); + if (!m_core_proxy->check_connection()) + { + // if there's not http rpc core server yet, create one + boost::program_options::options_description desc_options; + currency::core_rpc_server::init_options(desc_options); + boost::program_options::variables_map vm{}; + char* argv[] = {"--rpc-bind-ip=127.0.0.1", "--rpc-bind-port=33777"}; + boost::program_options::store(boost::program_options::parse_command_line(sizeof argv / sizeof argv[0], argv, desc_options), vm); + m_http_rpc_server = std::make_shared(c, vm); + } + m_core_proxy->set_connectivity(30000, 1); + CHECK_AND_ASSERT_THROW_MES(m_core_proxy->check_connection(), "m_core_proxy: no connection"); + } + std::shared_ptr w(new wallet_t); w->set_core_runtime_config(c.get_blockchain_storage().get_core_runtime_config()); w->assign_account(acc); @@ -64,12 +88,42 @@ protected: size_t account_index; }; + struct core_http_rpc_server_details + { + currency::t_currency_protocol_handler cph; + nodetool::node_server > p2p; + bc_services::bc_offers_service bos; + currency::core_rpc_server core_rpc_server; + + core_http_rpc_server_details(currency::core& c, const boost::program_options::variables_map& vm) + : cph(c, nullptr) + , p2p(cph) + , bos(nullptr) + , core_rpc_server(c, p2p, bos) + { + bos.set_disabled(true); + core_rpc_server.set_ignore_connectivity_status(true); + + bool r = core_rpc_server.init(vm); + CRYPTO_CHECK_AND_THROW_MES(r, "core_http_rpc_server_details: rpc server init failed"); + r = core_rpc_server.run(2, false); + CRYPTO_CHECK_AND_THROW_MES(r, "core_http_rpc_server_details: rpc server run failed"); + } + + ~core_http_rpc_server_details() + { + core_rpc_server.deinit(); + } + }; + std::shared_ptr init_playtime_test_wallet(const std::vector& events, currency::core& c, size_t account_index) const; std::shared_ptr init_playtime_test_wallet(const std::vector& events, currency::core& c, const currency::account_base& acc) const; + std::shared_ptr init_playtime_test_wallet_with_true_http_rpc(const std::vector& events, currency::core& c, size_t account_index) const; mutable std::vector m_accounts; mutable test_generator generator; - std::shared_ptr m_core_proxy; + mutable std::shared_ptr m_core_proxy; + mutable std::shared_ptr m_http_rpc_server; // "True" RPC via http on localhost }; diff --git a/tests/unit_tests/amounts_tests.cpp b/tests/unit_tests/amounts_tests.cpp index 300b92a7..08abdaff 100644 --- a/tests/unit_tests/amounts_tests.cpp +++ b/tests/unit_tests/amounts_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Zano Project +// Copyright (c) 2022-2024 Zano Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,23 +10,23 @@ using namespace currency; namespace { - void do_pos_test(uint64_t expected, const std::string& str) + void do_pos_test(uint64_t expected, const std::string& str, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT) { uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end()); - ASSERT_TRUE(parse_amount(val, number_str)); + ASSERT_TRUE(parse_amount(number_str, val, decimal_point)); ASSERT_EQ(expected, val); } - void do_neg_test(const std::string& str) + void do_neg_test(const std::string& str, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT) { uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end()); - ASSERT_FALSE(parse_amount(val, number_str)); + ASSERT_FALSE(parse_amount(number_str, val, decimal_point)); } } @@ -48,6 +48,23 @@ namespace do_neg_test(#str); \ } +#define TEST_pos_dp(expected, str, decimal_point) \ + TEST(parse_amount, handles_pos_ ## str ## _dp ## decimal_point) \ + { \ + do_pos_test(UINT64_C(expected), #str, decimal_point); \ + } + +#define TEST_neg_dp(str, decimal_point) \ + TEST(parse_amount, handles_neg_ ## str ## _dp ## decimal_point) \ + { \ + do_neg_test(#str, decimal_point); \ + } + +#define TEST_neg_n_dp(str, name, decimal_point) \ + TEST(parse_amount, handles_neg_ ## name ## _dp ## decimal_point) \ + { \ + do_neg_test(#str, decimal_point); \ + } TEST_pos(0, 0); TEST_pos(0, 00); @@ -96,6 +113,44 @@ TEST_pos(18446744073700000000, 18446744_0737000000000); TEST_pos(18446744073700000000, 18446744_07370000000000000000000); TEST_pos(18446744073709551615, 18446744_073709551615); +// non-standard decimal point +TEST_pos_dp(0, 0_0, 3); +TEST_pos_dp(0, 00_0, 3); +TEST_pos_dp(0, 00_00, 3); +TEST_pos_dp(0, 00000000_00, 3); +TEST_pos_dp(0, 00_000000000, 3); +TEST_pos_dp(0, 00_00000000000000000000000000000000, 3); + +TEST_pos_dp( 65535, 65535, 0); +TEST_pos_dp( 6553500, 65535, 2); +TEST_pos_dp( 65535000000, 65535, 6); +TEST_pos_dp( 18000000000000000000, 18, 18); +TEST_pos_dp( 1, 0_1, 1); +TEST_pos_dp( 10, 0_1, 2); +TEST_pos_dp( 100, 0_1, 3); +TEST_pos_dp( 10000000000000000000, 0_1, 20); +TEST_pos_dp( 1, 0_001, 3); +TEST_pos_dp( 123, 0_123, 3); +TEST_pos_dp( 1230, 0_123, 4); +TEST_pos_dp( 12300, 0_123, 5); +TEST_pos_dp( 123000, 0_123, 6); + +TEST_pos_dp(18446744073709551615, 18446744073709551615, 0); +TEST_pos_dp(18446744073709551615, 18446744073709551615_0, 0); + +TEST_pos_dp(18446744073709551615, 1844674407370955161_5, 1); +TEST_pos_dp(18446744073709551615, 1844674407370955161_50, 1); + +TEST_pos_dp(18446744073709551615, 18446744073709551_615, 3); +TEST_pos_dp(18446744073709551615, 18446744073709551_615000, 3); + +TEST_pos_dp(18446744073709551615, 1_8446744073709551615, 19); +TEST_pos_dp(18446744073709551615, 1_844674407370955161500, 19); + +TEST_pos_dp(18446744073709551615, 0_18446744073709551615, 20); +TEST_pos_dp(18446744073709551615, 0_1844674407370955161500, 20); + + // Invalid numbers TEST_neg_n(~, empty_string); TEST_neg_n(-0, minus_0); @@ -109,10 +164,17 @@ TEST_neg(0_0000000000001); TEST_neg(0_0000000000009); TEST_neg(18446744_0737000000001); +TEST_neg_dp(00_184467440737095516150001, 20); +TEST_neg_dp(00_184467440737095516151, 20); +TEST_neg_dp(1_2, 0); + // Overflow TEST_neg(184467440737_09551616); TEST_neg(184467440738); TEST_neg(18446744073709551616); +TEST_neg_dp(18446744073709551616, 0); +TEST_neg_dp(1844674407370955161_60, 1); +TEST_neg_dp(0_18446744073709551616, 20); // Two or more points TEST_neg(__); @@ -123,6 +185,51 @@ TEST_neg(0_0_); TEST_neg(_0_0); TEST_neg(0_0_0); +// moved from test_format_utils.cpp +TEST(validate_parse_amount_case, validate_parse_amount) +{ + uint64_t res = 0; + bool r = currency::parse_amount("0.0001", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000000); + + r = currency::parse_amount("100.0001", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000100000000); + + r = currency::parse_amount("000.0000", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 0); + + r = currency::parse_amount("0", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 0); + + + r = currency::parse_amount(" 100.0001 ", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000100000000); + + r = currency::parse_amount(" 100.0000 ", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000000000000); + + r = currency::parse_amount(" 100. 0000 ", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("100. 0000", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("100 . 0000", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("100.00 00", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("1 00.00 00", res); + ASSERT_FALSE(r); +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -201,3 +308,143 @@ TEST(decompose_amount_randomly, 1) foo(1000000000000, 2, 3); } } + +TEST(print_money_brief, 1) +{ + // decimal point 12 (default) + ASSERT_EQ(print_money_brief( 0), "0.0"); + ASSERT_EQ(print_money_brief( 1), "0.000000000001"); + ASSERT_EQ(print_money_brief( 1000000000000), "1.0"); + ASSERT_EQ(print_money_brief( 1900000000000), "1.9"); + ASSERT_EQ(print_money_brief( 1000000100000), "1.0000001"); + ASSERT_EQ(print_money_brief( 1000000000001), "1.000000000001"); + ASSERT_EQ(print_money_brief( 9999999999999), "9.999999999999"); + ASSERT_EQ(print_money_brief( 90009990009900), "90.0099900099"); + ASSERT_EQ(print_money_brief(10109010000000000000), "10109010.0"); + ASSERT_EQ(print_money_brief(10109010010000000000), "10109010.01"); + ASSERT_EQ(print_money_brief(18446744073709551610), "18446744.07370955161"); + ASSERT_EQ(print_money_brief(18446744073709551614), "18446744.073709551614"); + ASSERT_EQ(print_money_brief(18446744073709551615), "18446744.073709551615"); + + // decimal point 0 + ASSERT_EQ(print_money_brief( 0, 0), "0"); + ASSERT_EQ(print_money_brief( 1, 0), "1"); + ASSERT_EQ(print_money_brief( 1000000000000, 0), "1000000000000"); + ASSERT_EQ(print_money_brief( 1900000000000, 0), "1900000000000"); + ASSERT_EQ(print_money_brief( 1000000100000, 0), "1000000100000"); + ASSERT_EQ(print_money_brief( 1000000000001, 0), "1000000000001"); + ASSERT_EQ(print_money_brief( 9999999999999, 0), "9999999999999"); + ASSERT_EQ(print_money_brief( 90009990009900, 0), "90009990009900"); + ASSERT_EQ(print_money_brief(10109010000000000000, 0), "10109010000000000000"); + ASSERT_EQ(print_money_brief(10109010010000000000, 0), "10109010010000000000"); + ASSERT_EQ(print_money_brief(18446744073709551610, 0), "18446744073709551610"); + ASSERT_EQ(print_money_brief(18446744073709551614, 0), "18446744073709551614"); + ASSERT_EQ(print_money_brief(18446744073709551615, 0), "18446744073709551615"); + + // decimal point 1 + ASSERT_EQ(print_money_brief( 0, 1), "0.0"); + ASSERT_EQ(print_money_brief( 1, 1), "0.1"); + ASSERT_EQ(print_money_brief( 1000000000000, 1), "100000000000.0"); + ASSERT_EQ(print_money_brief( 1900000000000, 1), "190000000000.0"); + ASSERT_EQ(print_money_brief( 1000000100000, 1), "100000010000.0"); + ASSERT_EQ(print_money_brief( 1000000000001, 1), "100000000000.1"); + ASSERT_EQ(print_money_brief( 9999999999999, 1), "999999999999.9"); + ASSERT_EQ(print_money_brief( 90009990009900, 1), "9000999000990.0"); + ASSERT_EQ(print_money_brief(10109010000000000000, 1), "1010901000000000000.0"); + ASSERT_EQ(print_money_brief(10109010010000000000, 1), "1010901001000000000.0"); + ASSERT_EQ(print_money_brief(18446744073709551610, 1), "1844674407370955161.0"); + ASSERT_EQ(print_money_brief(18446744073709551614, 1), "1844674407370955161.4"); + ASSERT_EQ(print_money_brief(18446744073709551615, 1), "1844674407370955161.5"); + + // decimal point 2 + ASSERT_EQ(print_money_brief( 0, 2), "0.0"); + ASSERT_EQ(print_money_brief( 1, 2), "0.01"); + ASSERT_EQ(print_money_brief( 1000000000000, 2), "10000000000.0"); + ASSERT_EQ(print_money_brief( 1900000000000, 2), "19000000000.0"); + ASSERT_EQ(print_money_brief( 1000000100000, 2), "10000001000.0"); + ASSERT_EQ(print_money_brief( 1000000000001, 2), "10000000000.01"); + ASSERT_EQ(print_money_brief( 9999999999999, 2), "99999999999.99"); + ASSERT_EQ(print_money_brief( 90009990009900, 2), "900099900099.0"); + ASSERT_EQ(print_money_brief(10109010000000000000, 2), "101090100000000000.0"); + ASSERT_EQ(print_money_brief(10109010010000000000, 2), "101090100100000000.0"); + ASSERT_EQ(print_money_brief(18446744073709551610, 2), "184467440737095516.1"); + ASSERT_EQ(print_money_brief(18446744073709551614, 2), "184467440737095516.14"); + ASSERT_EQ(print_money_brief(18446744073709551615, 2), "184467440737095516.15"); + + // decimal point 3 + ASSERT_EQ(print_money_brief( 0, 3), "0.0"); + ASSERT_EQ(print_money_brief( 1, 3), "0.001"); + ASSERT_EQ(print_money_brief( 1000000000000, 3), "1000000000.0"); + ASSERT_EQ(print_money_brief( 1900000000000, 3), "1900000000.0"); + ASSERT_EQ(print_money_brief( 1000000100000, 3), "1000000100.0"); + ASSERT_EQ(print_money_brief( 1000000000001, 3), "1000000000.001"); + ASSERT_EQ(print_money_brief( 9999999999999, 3), "9999999999.999"); + ASSERT_EQ(print_money_brief( 90009990009900, 3), "90009990009.9"); + ASSERT_EQ(print_money_brief(10109010000000000000, 3), "10109010000000000.0"); + ASSERT_EQ(print_money_brief(10109010010000000000, 3), "10109010010000000.0"); + ASSERT_EQ(print_money_brief(18446744073709551610, 3), "18446744073709551.61"); + ASSERT_EQ(print_money_brief(18446744073709551614, 3), "18446744073709551.614"); + ASSERT_EQ(print_money_brief(18446744073709551615, 3), "18446744073709551.615"); + + // decimal point 18 + ASSERT_EQ(print_money_brief( 0, 18), "0.0"); + ASSERT_EQ(print_money_brief( 1, 18), "0.000000000000000001"); + ASSERT_EQ(print_money_brief( 1000000000000, 18), "0.000001"); + ASSERT_EQ(print_money_brief( 1900000000000, 18), "0.0000019"); + ASSERT_EQ(print_money_brief( 1000000100000, 18), "0.0000010000001"); + ASSERT_EQ(print_money_brief( 1000000000001, 18), "0.000001000000000001"); + ASSERT_EQ(print_money_brief( 9999999999999, 18), "0.000009999999999999"); + ASSERT_EQ(print_money_brief( 90009990009900, 18), "0.0000900099900099"); + ASSERT_EQ(print_money_brief(10109010000000000000, 18), "10.10901"); + ASSERT_EQ(print_money_brief(10109010010000000000, 18), "10.10901001"); + ASSERT_EQ(print_money_brief(18446744073709551610, 18), "18.44674407370955161"); + ASSERT_EQ(print_money_brief(18446744073709551614, 18), "18.446744073709551614"); + ASSERT_EQ(print_money_brief(18446744073709551615, 18), "18.446744073709551615"); + + // decimal point 19 + ASSERT_EQ(print_money_brief( 0, 19), "0.0"); + ASSERT_EQ(print_money_brief( 1, 19), "0.0000000000000000001"); + ASSERT_EQ(print_money_brief( 1000000000000, 19), "0.0000001"); + ASSERT_EQ(print_money_brief( 1900000000000, 19), "0.00000019"); + ASSERT_EQ(print_money_brief( 1000000100000, 19), "0.00000010000001"); + ASSERT_EQ(print_money_brief( 1000000000001, 19), "0.0000001000000000001"); + ASSERT_EQ(print_money_brief( 9999999999999, 19), "0.0000009999999999999"); + ASSERT_EQ(print_money_brief( 90009990009900, 19), "0.00000900099900099"); + ASSERT_EQ(print_money_brief(10109010000000000000, 19), "1.010901"); + ASSERT_EQ(print_money_brief(10109010010000000000, 19), "1.010901001"); + ASSERT_EQ(print_money_brief(18446744073709551610, 19), "1.844674407370955161"); + ASSERT_EQ(print_money_brief(18446744073709551614, 19), "1.8446744073709551614"); + ASSERT_EQ(print_money_brief(18446744073709551615, 19), "1.8446744073709551615"); + + // TODO: remove it after setting reasonable limit of 18 + // decimal point 20 + ASSERT_EQ(print_money_brief( 0, 20), "0.0"); + ASSERT_EQ(print_money_brief( 1, 20), "0.00000000000000000001"); + ASSERT_EQ(print_money_brief( 1000000000000, 20), "0.00000001"); + ASSERT_EQ(print_money_brief( 1900000000000, 20), "0.000000019"); + ASSERT_EQ(print_money_brief( 1000000100000, 20), "0.000000010000001"); + ASSERT_EQ(print_money_brief( 1000000000001, 20), "0.00000001000000000001"); + ASSERT_EQ(print_money_brief( 9999999999999, 20), "0.00000009999999999999"); + ASSERT_EQ(print_money_brief( 90009990009900, 20), "0.000000900099900099"); + ASSERT_EQ(print_money_brief(10109010000000000000, 20), "0.1010901"); + ASSERT_EQ(print_money_brief(10109010010000000000, 20), "0.1010901001"); + ASSERT_EQ(print_money_brief(18446744073709551610, 20), "0.1844674407370955161"); + ASSERT_EQ(print_money_brief(18446744073709551614, 20), "0.18446744073709551614"); + ASSERT_EQ(print_money_brief(18446744073709551615, 20), "0.18446744073709551615"); + + // TODO: remove it after setting reasonable limit of 18 + // decimal point 21 + ASSERT_EQ(print_money_brief( 0, 21), "0.0"); + ASSERT_EQ(print_money_brief( 1, 21), "0.000000000000000000001"); + ASSERT_EQ(print_money_brief( 1000000000000, 21), "0.000000001"); + ASSERT_EQ(print_money_brief( 1900000000000, 21), "0.0000000019"); + ASSERT_EQ(print_money_brief( 1000000100000, 21), "0.0000000010000001"); + ASSERT_EQ(print_money_brief( 1000000000001, 21), "0.000000001000000000001"); + ASSERT_EQ(print_money_brief( 9999999999999, 21), "0.000000009999999999999"); + ASSERT_EQ(print_money_brief( 90009990009900, 21), "0.0000000900099900099"); + ASSERT_EQ(print_money_brief(10109010000000000000, 21), "0.01010901"); + ASSERT_EQ(print_money_brief(10109010010000000000, 21), "0.01010901001"); + ASSERT_EQ(print_money_brief(18446744073709551610, 21), "0.01844674407370955161"); + ASSERT_EQ(print_money_brief(18446744073709551614, 21), "0.018446744073709551614"); + ASSERT_EQ(print_money_brief(18446744073709551615, 21), "0.018446744073709551615"); +} diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index 0e277222..f12c149f 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -125,48 +125,3 @@ void force_random(forced_to_pod_t& o) // } // // } - - -TEST(validate_parse_amount_case, validate_parse_amount) -{ - uint64_t res = 0; - bool r = currency::parse_amount(res, "0.0001"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000000); - - r = currency::parse_amount(res, "100.0001"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000100000000); - - r = currency::parse_amount(res, "000.0000"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 0); - - r = currency::parse_amount(res, "0"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 0); - - - r = currency::parse_amount(res, " 100.0001 "); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000100000000); - - r = currency::parse_amount(res, " 100.0000 "); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000000000000); - - r = currency::parse_amount(res, " 100. 0000 "); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "100. 0000"); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "100 . 0000"); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "100.00 00"); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "1 00.00 00"); - ASSERT_FALSE(r); -}