1
0
Fork 0
forked from lthn/blockchain

wallet: packing/consolidating tx that automatically aggregates small UTXO in PoS blocks was ranamed to defragmentation tx to avoid confusion + re-implemented to reflect post-HF4 changes

This commit is contained in:
sowle 2023-06-09 01:10:16 +02:00
parent 6aba2d44b6
commit 13e8d0dfe3
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
5 changed files with 46 additions and 66 deletions

View file

@ -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"

View file

@ -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<i_core_proxy>& 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<i_core_proxy> 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::vector<wallet_public::wallet_tra
if (exclude_mining_txs)
{
if (currency::is_coinbase(wti.tx) || is_consolidating_transaction(wti))
if (currency::is_coinbase(wti.tx) || is_defragmentation_transaction(wti))
return true;
}
trs.push_back(wti);
@ -4228,12 +4214,12 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco
tmpl_req.pe.tx_out_index = td.m_internal_output_index;
tmpl_req.pe.wallet_index = cxt.index;
//generate packing tx
transaction pack_tx = AUTO_VAL_INIT(pack_tx);
if (generate_packing_transaction_if_needed(pack_tx, 0))
// generate UTXO Defragmentation Transaction - to reduce the UTXO set size
transaction udtx{};
if (generate_utxo_defragmentation_transaction_if_needed(udtx))
{
tx_to_blob(pack_tx, tmpl_req.explicit_transaction);
WLT_LOG_GREEN("Packing inputs: " << pack_tx.vin.size() << " inputs consolidated in tx " << get_transaction_hash(pack_tx), LOG_LEVEL_0);
tx_to_blob(udtx, tmpl_req.explicit_transaction);
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!");
@ -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<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money)
bool wallet2::prepare_tx_sources_for_defragmentation_tx(std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& 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<currency::tx_source_entry>& sources, std::vector<uint64_t>& 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)
{

View file

@ -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<wallet_public::wallet_transfer_info>& 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<wallet_public::wallet_transfer_info>& 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<currency::tx_destination_entry>& destinations, currency::transaction& result_tx, crypto::public_key& new_asset_id);
bool set_core_proxy(const std::shared_ptr<i_core_proxy>& 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<i_core_proxy> get_core_proxy();
uint64_t balance() const;
@ -1042,7 +1040,7 @@ private:
bool prepare_tx_sources(size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, const std::vector<uint64_t>& selected_indicies);
bool prepare_tx_sources(crypto::hash multisig_id, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
bool prepare_tx_sources_htlc(crypto::hash htlc_tx_id, const std::string& origin, std::vector<currency::tx_source_entry>& sources, uint64_t& found_money);
bool prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake_outputs_count, std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
bool prepare_tx_sources_for_defragmentation_tx(std::vector<currency::tx_source_entry>& sources, std::vector<uint64_t>& selected_indicies, uint64_t& found_money);
void prefetch_global_indicies_if_needed(const std::vector<uint64_t>& selected_indicies);
assets_selection_context get_needed_money(uint64_t fee, const std::vector<currency::tx_destination_entry>& 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<uint64_t> 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;

View file

@ -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();

View file

@ -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();