mirror of
https://github.com/monero-project/monero.git
synced 2025-08-24 02:05:15 -04:00
wallet: change sweep_dust to sweep_unmixable
With the change in mixin rules for v2, the "annoying" outputs are slightly changed. There is high correlation between dust and unmixable, but no equivalence.
This commit is contained in:
parent
600a3cf0c0
commit
12146daeed
6 changed files with 129 additions and 30 deletions
|
@ -2508,7 +2508,7 @@ uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
|
||||
void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
|
||||
|
@ -2518,6 +2518,19 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
|
|||
// throw if there are none
|
||||
uint64_t money = 0;
|
||||
std::list<transfer_container::iterator> selected_transfers;
|
||||
#if 1
|
||||
for (size_t n = 0; n < outs.size(); ++n)
|
||||
{
|
||||
const transfer_details& td = m_transfers[outs[n]];
|
||||
if (!td.m_spent)
|
||||
{
|
||||
selected_transfers.push_back (m_transfers.begin() + outs[n]);
|
||||
money += td.amount();
|
||||
if (selected_transfers.size() >= num_outputs)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (transfer_container::iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
|
||||
{
|
||||
const transfer_details& td = *i;
|
||||
|
@ -2529,6 +2542,7 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
|
|||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// we don't allow no output to self, easier, but one may want to burn the dust if = fee
|
||||
THROW_WALLET_EXCEPTION_IF(money <= needed_fee, error::not_enough_money, money, needed_fee, needed_fee);
|
||||
|
@ -2622,8 +2636,8 @@ bool wallet2::use_fork_rules(uint8_t version)
|
|||
r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
|
||||
m_daemon_rpc_mutex.unlock();
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to connect to daemon");
|
||||
CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
|
||||
CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, false, "Failed to get hard fork status");
|
||||
CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
|
||||
CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, false, "Failed to get hard fork status");
|
||||
|
||||
bool close_enough = res.height >= resp_t.result.earliest_height - 10; // start using the rules a bit beforehand
|
||||
if (close_enough)
|
||||
|
@ -2641,20 +2655,84 @@ uint64_t wallet2::get_upper_tranaction_size_limit()
|
|||
return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
|
||||
std::vector<size_t> wallet2::select_available_outputs(std::function<bool(const transfer_details &td)> f)
|
||||
{
|
||||
std::vector<size_t> outputs;
|
||||
size_t n = 0;
|
||||
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
|
||||
{
|
||||
if (i->m_spent)
|
||||
continue;
|
||||
if (!is_transfer_unlocked(*i))
|
||||
continue;
|
||||
if (f(*i))
|
||||
outputs.push_back(n);
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
|
||||
{
|
||||
std::set<uint64_t> set;
|
||||
for (const auto &td: m_transfers)
|
||||
{
|
||||
if (!td.m_spent)
|
||||
set.insert(td.amount());
|
||||
}
|
||||
std::vector<uint64_t> vector;
|
||||
vector.reserve(set.size());
|
||||
for (const auto &i: set)
|
||||
{
|
||||
vector.push_back(i);
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> wallet2::select_available_unmixable_outputs()
|
||||
{
|
||||
// request all outputs with at least 3 instances, so we can use mixin 2 with
|
||||
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t);
|
||||
epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
|
||||
m_daemon_rpc_mutex.lock();
|
||||
req_t.jsonrpc = "2.0";
|
||||
req_t.id = epee::serialization::storage_entry(0);
|
||||
req_t.method = "get_output_histogram";
|
||||
req_t.params.amounts = get_unspent_amounts_vector();
|
||||
req_t.params.min_count = 3;
|
||||
req_t.params.max_count = 0;
|
||||
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
|
||||
m_daemon_rpc_mutex.unlock();
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_unmixable_outputs");
|
||||
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
|
||||
THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status);
|
||||
|
||||
std::set<uint64_t> mixable;
|
||||
for (const auto &i: resp_t.result.histogram)
|
||||
{
|
||||
mixable.insert(i.amount);
|
||||
}
|
||||
|
||||
return select_available_outputs([mixable](const transfer_details &td) {
|
||||
const uint64_t amount = td.amount();
|
||||
if (mixable.find(amount) == mixable.end())
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
|
||||
{
|
||||
// From hard fork 1, we don't consider small amounts to be dust anymore
|
||||
const bool hf1_rules = use_fork_rules(2); // first hard fork has version 2
|
||||
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
|
||||
|
||||
size_t num_dust_outputs = 0;
|
||||
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
|
||||
// may throw
|
||||
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
|
||||
size_t num_dust_outputs = unmixable_outputs.size();
|
||||
|
||||
if (num_dust_outputs == 0)
|
||||
{
|
||||
const transfer_details& td = *i;
|
||||
if (!td.m_spent && (td.amount() < dust_policy.dust_threshold || !is_valid_decomposed_amount(td.amount())) && is_transfer_unlocked(td))
|
||||
{
|
||||
num_dust_outputs++;
|
||||
}
|
||||
return std::vector<wallet2::pending_tx>();
|
||||
}
|
||||
|
||||
// failsafe split attempt counter
|
||||
|
@ -2679,13 +2757,13 @@ std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
|
|||
uint64_t needed_fee = 0;
|
||||
do
|
||||
{
|
||||
transfer_dust(num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(ptx.tx);
|
||||
needed_fee = calculate_fee(txBlob);
|
||||
|
||||
// reroll the tx with the actual amount minus the fee
|
||||
// if there's not enough for the fee, it'll throw
|
||||
transfer_dust(num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
txBlob = t_serializable_object_to_blob(ptx.tx);
|
||||
needed_fee = calculate_fee(txBlob);
|
||||
} while (ptx.fee < needed_fee);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue