diff --git a/src/common/error_codes.h b/src/common/error_codes.h index 313a9aad..87b7fa8c 100644 --- a/src/common/error_codes.h +++ b/src/common/error_codes.h @@ -12,6 +12,7 @@ #define API_RETURN_CODE_INTERNAL_ERROR "INTERNAL_ERROR" #define API_RETURN_CODE_NOT_ENOUGH_MONEY "NOT_ENOUGH_MONEY" #define API_RETURN_CODE_NOT_ENOUGH_OUTPUTS_FOR_MIXING "NOT_ENOUGH_OUTPUTS_FOR_MIXING" +#define API_RETURN_CODE_NOT_ENOUGH_OUTPUTS_FOR_OPERATION "NOT_ENOUGH_OUTPUTS_FOR_OPERATION" #define API_RETURN_CODE_INTERNAL_ERROR_QUE_FULL "INTERNAL_ERROR_QUE_FULL" #define API_RETURN_CODE_BAD_ARG "BAD_ARG" #define API_RETURN_CODE_BAD_ARG_EMPTY_DESTINATIONS "BAD_ARG_EMPTY_DESTINATIONS" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 05fe16f5..07deff48 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -51,6 +51,9 @@ using namespace currency; #define MINIMUM_REQUIRED_WALLET_FREE_SPACE_BYTES (100*1024*1024) // 100 MB +#define WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX 10 // TODO @#@# change to default decoy set number +#define WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX 100 // TODO: @#@# consider descreasing to mimic normal tx + #undef LOG_DEFAULT_CHANNEL #define LOG_DEFAULT_CHANNEL "wallet" ENABLE_CHANNEL_BY_DEFAULT("wallet") @@ -69,7 +72,8 @@ namespace tools , m_watch_only(false) , m_last_pow_block_h(0) , m_minimum_height(WALLET_MINIMUM_HEIGHT_UNSET_CONST) - , m_pos_mint_packing_size(WALLET_DEFAULT_POS_MINT_PACKING_SIZE) + , m_min_utxo_count_for_defragmentation_tx(WALLET_MIN_UTXO_COUNT_FOR_DEFRAGMENTATION_TX) + , m_decoys_count_for_defragmentation_tx(WALLET_DEFAULT_DECOYS_COUNT_FOR_DEFRAGMENTATION_TX) , m_current_wallet_file_size(0) , m_use_deffered_global_outputs(false) #ifdef DISABLE_TOR @@ -203,9 +207,9 @@ bool wallet2::set_core_proxy(const std::shared_ptr& proxy) return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::set_pos_mint_packing_size(uint64_t new_size) +void wallet2::set_pos_min_utxo_count_for_defragmentation_tx(uint64_t new_size) { - m_pos_mint_packing_size = new_size; + m_min_utxo_count_for_defragmentation_tx = new_size; } //---------------------------------------------------------------------------------------------------- std::shared_ptr wallet2::get_core_proxy() @@ -3363,29 +3367,11 @@ void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) con incoming_transfers = m_transfers; } //---------------------------------------------------------------------------------------------------- -bool wallet2::generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number) +bool wallet2::generate_utxo_defragmentation_transaction_if_needed(currency::transaction& tx) { - prepare_free_transfers_cache(0); - auto it = m_found_free_amounts[currency::native_coin_asset_id].find(CURRENCY_BLOCK_REWARD); - if (it == m_found_free_amounts[currency::native_coin_asset_id].end() || it->second.size() <= m_pos_mint_packing_size) - return false; - - //let's check if we have at least WALLET_POS_MINT_PACKING_SIZE transactions which is ready to go - size_t count = 0; - for (auto it_ind = it->second.begin(); it_ind != it->second.end() && count <= m_pos_mint_packing_size; it_ind++) - { - if (is_transfer_ready_to_go(m_transfers[*it_ind], fake_outputs_number)) - ++count; - } - if (count <= m_pos_mint_packing_size) - return false; construct_tx_param ctp = get_default_construct_tx_param(); - currency::tx_destination_entry de = AUTO_VAL_INIT(de); - de.addr.push_back(m_account.get_public_address()); - de.amount = m_pos_mint_packing_size*CURRENCY_BLOCK_REWARD; - ctp.dsts.push_back(de); - ctp.perform_packing = true; - + ctp.create_utxo_defragmentation_tx = true; + TRY_ENTRY(); transfer(ctp, tx, false, nullptr); CATCH_ENTRY2(false); @@ -3682,7 +3668,7 @@ bool enum_container(iterator_t it_begin, iterator_t it_end, callback_t cb) return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::is_consolidating_transaction(const wallet_public::wallet_transfer_info& wti) +bool wallet2::is_defragmentation_transaction(const wallet_public::wallet_transfer_info& wti) { if (!wti.is_income) { @@ -3705,7 +3691,7 @@ void wallet2::get_recent_transfers_history(std::vectorcall_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!"); @@ -5376,33 +5362,26 @@ bool wallet2::decrypt_buffer(const std::string& buff, std::string& res_buff) return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector& sources, std::vector& selected_indicies, uint64_t& found_money) +bool wallet2::prepare_tx_sources_for_defragmentation_tx(std::vector& sources, std::vector& selected_indicies, uint64_t& found_money) { - prepare_free_transfers_cache(fake_outputs_count); + //prepare_free_transfers_cache(fake_outputs_count); + //free_amounts_cache_type& free_amounts_for_native_coin = m_found_free_amounts[currency::native_coin_asset_id]; - free_amounts_cache_type& free_amounts_for_native_coin = m_found_free_amounts[currency::native_coin_asset_id]; - - auto it = free_amounts_for_native_coin.find(CURRENCY_BLOCK_REWARD); - if (it == free_amounts_for_native_coin.end() || it->second.size() < m_pos_mint_packing_size) - return false; - - for (auto set_it = it->second.begin(); set_it != it->second.end() && selected_indicies.size() <= m_pos_mint_packing_size; ) + for (size_t i = 0, size = m_transfers.size(); i < size && selected_indicies.size() < m_min_utxo_count_for_defragmentation_tx; ++i) { - if (is_transfer_ready_to_go(m_transfers[*set_it], fake_outputs_count)) - { - found_money += it->first; - selected_indicies.push_back(*set_it); - WLT_LOG_L2("Selected index: " << *set_it << ", transfer_details: " << ENDL << epee::serialization::store_t_to_json(m_transfers[*set_it])); - - it->second.erase(set_it++); - } - else - set_it++; - } - if (!it->second.size()) - free_amounts_for_native_coin.erase(it); + const auto& td = m_transfers[i]; + if (!td.is_native_coin() || td.m_amount > CURRENCY_BLOCK_REWARD) + continue; - return prepare_tx_sources(fake_outputs_count, sources, selected_indicies); + if (is_transfer_ready_to_go(td, m_decoys_count_for_defragmentation_tx)) + { + found_money += td.m_amount; + selected_indicies.push_back(i); + WLT_LOG_L2("prepare_tx_sources_for_defragmentation_tx: selected index: " << i << ", transfer_details: " << ENDL << epee::serialization::store_t_to_json(m_transfers[i])); + } + } + + return prepare_tx_sources(m_decoys_count_for_defragmentation_tx, sources, selected_indicies); } //---------------------------------------------------------------------------------------------------- bool wallet2::prepare_tx_sources(assets_selection_context& needed_money_map, size_t fake_outputs_count, uint64_t dust_threshold, std::vector& sources, std::vector& selected_indicies) @@ -6448,9 +6427,10 @@ void wallet2::prepare_transaction(construct_tx_param& ctp, currency::finalize_tx //uint64_t found_money = 0; TIME_MEASURE_START_MS(prepare_tx_sources_time); - if (ctp.perform_packing) + if (ctp.create_utxo_defragmentation_tx) { - prepare_tx_sources_for_packing(WALLET_DEFAULT_POS_MINT_PACKING_SIZE, 0, ftp.sources, ftp.selected_transfers, needed_money_map[currency::native_coin_asset_id].found_amount); + bool r = prepare_tx_sources_for_defragmentation_tx(ftp.sources, ftp.selected_transfers, needed_money_map[currency::native_coin_asset_id].found_amount); + WLT_THROW_IF_FALSE_WITH_CODE(r, "utxo defragmentation tx was not prepared (not an error)", API_RETURN_CODE_NOT_ENOUGH_OUTPUTS_FOR_OPERATION); } else if (ctp.htlc_tx_id != currency::null_hash) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 6e834fba..22a686c8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -50,8 +50,6 @@ #define WALLET_DEFAULT_TX_SPENDABLE_AGE 10 #define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1 -#define WALLET_DEFAULT_POS_MINT_PACKING_SIZE 100 - #define WALLET_TRANSFER_DETAIL_FLAG_SPENT uint32_t(1 << 0) #define WALLET_TRANSFER_DETAIL_FLAG_BLOCKED uint32_t(1 << 1) #define WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION uint32_t(1 << 2) @@ -299,7 +297,7 @@ namespace tools currency::account_public_address crypt_address; uint8_t tx_outs_attr = 0; bool shuffle = false; - bool perform_packing = false; + bool create_utxo_defragmentation_tx = false; bool need_at_least_1_zc = false; }; @@ -574,7 +572,7 @@ namespace tools const currency::account_base& get_account() const { return m_account; } void get_recent_transfers_history(std::vector& trs, size_t offset, size_t count, uint64_t& total, uint64_t& last_item_index, bool exclude_mining_txs = false, bool start_from_end = true); - bool is_consolidating_transaction(const wallet_public::wallet_transfer_info& wti); + bool is_defragmentation_transaction(const wallet_public::wallet_transfer_info& wti); uint64_t get_recent_transfers_total_count(); uint64_t get_transfer_entries_count(); void get_unconfirmed_transfers(std::vector& trs, bool exclude_mining_txs = false); @@ -609,7 +607,7 @@ namespace tools void deploy_new_asset(const currency::asset_descriptor_base& asset_info, const std::vector& destinations, currency::transaction& result_tx, crypto::public_key& new_asset_id); bool set_core_proxy(const std::shared_ptr& proxy); - void set_pos_mint_packing_size(uint64_t new_size); + void set_pos_min_utxo_count_for_defragmentation_tx(uint64_t new_size); void set_minimum_height(uint64_t h); std::shared_ptr get_core_proxy(); uint64_t balance() const; @@ -1042,7 +1040,7 @@ private: bool prepare_tx_sources(size_t fake_outputs_count, std::vector& sources, const std::vector& selected_indicies); bool prepare_tx_sources(crypto::hash multisig_id, std::vector& sources, uint64_t& found_money); bool prepare_tx_sources_htlc(crypto::hash htlc_tx_id, const std::string& origin, std::vector& sources, uint64_t& found_money); - bool prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector& sources, std::vector& selected_indicies, uint64_t& found_money); + bool prepare_tx_sources_for_defragmentation_tx(std::vector& sources, std::vector& selected_indicies, uint64_t& found_money); void prefetch_global_indicies_if_needed(const std::vector& selected_indicies); assets_selection_context get_needed_money(uint64_t fee, const std::vector& dsts); void prepare_tx_destinations(const assets_selection_context& needed_money_map, @@ -1110,7 +1108,7 @@ private: void exception_handler(); void exception_handler() const; uint64_t get_minimum_allowed_fee_for_contract(const crypto::hash& ms_id); - bool generate_packing_transaction_if_needed(currency::transaction& tx, uint64_t fake_outputs_number); + bool generate_utxo_defragmentation_transaction_if_needed(currency::transaction& tx); bool store_unsigned_tx_to_file_and_reserve_transfers(const currency::finalize_tx_param& ftp, const std::string& filename, std::string* p_unsigned_tx_blob_str = nullptr); void check_and_throw_if_self_directed_tx_with_payment_id_requested(const construct_tx_param& ctp); void push_new_block_id(const crypto::hash& id, uint64_t height); @@ -1146,7 +1144,8 @@ private: std::atomic m_last_bc_timestamp; bool m_do_rise_transfer; - uint64_t m_pos_mint_packing_size; + uint64_t m_min_utxo_count_for_defragmentation_tx; + size_t m_decoys_count_for_defragmentation_tx; transfer_container m_transfers; multisig_transfer_container m_multisig_transfers; diff --git a/tests/core_tests/pos_validation.cpp b/tests/core_tests/pos_validation.cpp index 941c7b4d..03434c15 100644 --- a/tests/core_tests/pos_validation.cpp +++ b/tests/core_tests/pos_validation.cpp @@ -1085,7 +1085,7 @@ bool pos_minting_tx_packing::c1(currency::core& c, size_t ev_index, const std::v m_alice_start_amount + CURRENCY_BLOCK_REWARD * m_pos_mint_packing_size // unlocked ), false, ""); - alice_wlt->set_pos_mint_packing_size(m_pos_mint_packing_size); + alice_wlt->set_pos_min_utxo_count_for_defragmentation_tx(m_pos_mint_packing_size); // no coinbase tx outputs should be packed r = alice_wlt->try_mint_pos(); diff --git a/tests/core_tests/wallet_tests.cpp b/tests/core_tests/wallet_tests.cpp index a342303b..4937774a 100644 --- a/tests/core_tests/wallet_tests.cpp +++ b/tests/core_tests/wallet_tests.cpp @@ -3402,7 +3402,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde miner_wlt->refresh(blocks_fetched, received_money, atomic_false); CHECK_AND_ASSERT_MES(blocks_fetched == CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5, false, "Incorrect numbers of blocks fetched"); - miner_wlt->set_pos_mint_packing_size(4); + miner_wlt->set_pos_min_utxo_count_for_defragmentation_tx(4); CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner_wlt", m_premine_amount + m_mined_amount, uint64_max, uint64_max, 0, 0), false, ""); miner_wlt->try_mint_pos();