wallet: Improve Fee Priority Code for Clarity

This commit is contained in:
tzadiko
2025-03-28 20:31:46 -05:00
parent f90a267fa3
commit 2f2a8c46bb
10 changed files with 304 additions and 101 deletions
+65 -52
View File
@@ -40,6 +40,7 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
@@ -1198,7 +1199,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_print_ring_members(false),
m_store_tx_info(true),
m_default_mixin(0),
m_default_priority(0),
m_default_priority(fee_priority::Default),
m_refresh_type(RefreshOptimizeCoinbase),
m_auto_refresh(true),
m_first_refresh_done(false),
@@ -4606,7 +4607,7 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const crypt
value2.SetUint(m_default_mixin);
json.AddMember("default_mixin", value2, json.GetAllocator());
value2.SetUint(m_default_priority);
value2.SetUint(boost::numeric_cast<unsigned>(fee_priority_utilities::as_integral(m_default_priority)));
json.AddMember("default_priority", value2, json.GetAllocator());
value2.SetInt(m_auto_refresh ? 1 :0);
@@ -4938,7 +4939,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_print_ring_members = false;
m_store_tx_info = true;
m_default_mixin = 0;
m_default_priority = 0;
m_default_priority = fee_priority::Default;
m_auto_refresh = true;
m_refresh_type = RefreshType::RefreshDefault;
m_refresh_from_block_height = 0;
@@ -5071,15 +5072,15 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0);
if (field_default_priority_found)
{
m_default_priority = field_default_priority;
m_default_priority = fee_priority_utilities::from_integral(boost::numeric_cast<uint32_t>(field_default_priority));
}
else
{
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_fee_multiplier, unsigned int, Uint, false, 0);
if (field_default_fee_multiplier_found)
m_default_priority = field_default_fee_multiplier;
m_default_priority = fee_priority_utilities::from_integral(boost::numeric_cast<uint32_t>(field_default_fee_multiplier));
else
m_default_priority = 0;
m_default_priority = fee_priority::Default;
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false, true);
m_auto_refresh = field_auto_refresh;
@@ -8470,42 +8471,45 @@ uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs
}
}
uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm)
uint64_t wallet2::get_fee_multiplier(fee_priority priority, fee_algorithm fee_algorithm)
{
static const struct
struct fee_step
{
size_t count;
uint64_t multipliers[4];
}
multipliers[] =
{
{ 3, {1, 2, 3} },
{ 3, {1, 20, 166} },
{ 4, {1, 4, 20, 166} },
{ 4, {1, 5, 25, 1000} },
fee_priority maximum_priority; // Determines the maximum priority available for said fee algorithm.
uint64_t fee_multipliers[4]; // Determines the fee multiplier applied at various priorities for said fee algorithm.
};
if (fee_algorithm == -1)
static constexpr fee_step fee_steps[] =
{
{ fee_priority::Elevated, {1, 2, 3} },
{ fee_priority::Elevated, {1, 20, 166} },
{ fee_priority::Priority, {1, 4, 20, 166} },
{ fee_priority::Priority, {1, 5, 25, 1000} },
};
if (fee_algorithm == fee_algorithm::Unset)
fee_algorithm = get_fee_algorithm();
// 0 -> default (here, x1 till fee algorithm 2, x4 from it)
if (priority == 0)
if (priority == fee_priority::Default)
priority = m_default_priority;
if (priority == 0)
if (priority == fee_priority::Default)
{
if (fee_algorithm >= 2)
priority = 2;
if (fee_algorithm >= fee_algorithm::HardforkV5)
priority = fee_priority::Normal;
else
priority = 1;
priority = fee_priority::Unimportant;
}
THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 3, error::invalid_priority);
const auto fee_algorithm_index = fee_algorithm_utilities::as_integral(fee_algorithm);
THROW_WALLET_EXCEPTION_IF(fee_algorithm_index < 0 || fee_algorithm_index > static_cast<int>(std::size(fee_steps)), error::invalid_priority);
// 1 to 3/4 are allowed as priorities
const uint32_t max_priority = multipliers[fee_algorithm].count;
if (priority >= 1 && priority <= max_priority)
const fee_priority max_priority = fee_steps[fee_algorithm_index].maximum_priority;
if (priority >= fee_priority::Unimportant && priority <= max_priority)
{
return multipliers[fee_algorithm].multipliers[priority-1];
const fee_priority adjusted_priority = fee_priority_utilities::decrease(priority);
return fee_steps[fee_algorithm_index].fee_multipliers[fee_priority_utilities::as_integral(adjusted_priority)];
}
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
@@ -8533,16 +8537,18 @@ uint64_t wallet2::get_base_fee()
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_base_fee(uint32_t priority)
{
return get_base_fee(fee_priority_utilities::from_integral(priority));
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_base_fee(fee_priority priority)
{
const bool use_2021_scaling = use_fork_rules(HF_VERSION_2021_SCALING, -30 * 1);
if (use_2021_scaling)
{
// clamp and map to 0..3 indices, mapping 0 (default, but should not end up here) to 0, and 1..4 to 0..3
if (priority == 0)
priority = 1;
else if (priority > 4)
priority = 4;
--priority;
priority = fee_priority_utilities::clamp_modified(priority);
priority = fee_priority_utilities::decrease(priority);
std::vector<uint64_t> fees;
boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate_2021_scaling(FEE_ESTIMATE_GRACE_BLOCKS, fees);
@@ -8551,12 +8557,14 @@ uint64_t wallet2::get_base_fee(uint32_t priority)
MERROR("Failed to determine base fee, using default");
return FEE_PER_BYTE;
}
if (priority >= fees.size())
const auto priority_index = fee_priority_utilities::as_integral(priority);
if (priority_index >= fees.size())
{
MERROR("Failed to determine base fee for priority " << priority << ", using default");
MERROR("Failed to determine base fee for priority " << priority_index << ", using default");
return FEE_PER_BYTE;
}
return fees[priority];
return fees[priority_index];
}
else
{
@@ -8579,16 +8587,16 @@ uint64_t wallet2::get_fee_quantization_mask()
return fee_quantization_mask;
}
//----------------------------------------------------------------------------------------------------
int wallet2::get_fee_algorithm()
fee_algorithm wallet2::get_fee_algorithm()
{
// changes at v3, v5, v8
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0))
return 3;
return fee_algorithm::HardforkV8;
if (use_fork_rules(5, 0))
return 2;
return fee_algorithm::HardforkV5;
if (use_fork_rules(3, -30 * 14))
return 1;
return 0;
return fee_algorithm::HardforkV3;
return fee_algorithm::PreHardforkV3;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_min_ring_size()
@@ -8632,15 +8640,20 @@ uint64_t wallet2::adjust_mixin(uint64_t mixin)
return mixin;
}
//----------------------------------------------------------------------------------------------------
uint32_t wallet2::adjust_priority(uint32_t priority)
fee_priority wallet2::adjust_priority(uint32_t priority)
{
if (priority == 0 && m_default_priority == 0 && auto_low_priority())
return adjust_priority(fee_priority_utilities::from_integral(priority));
}
//----------------------------------------------------------------------------------------------------
fee_priority wallet2::adjust_priority(fee_priority priority)
{
if (priority == fee_priority::Default && m_default_priority == fee_priority::Default && auto_low_priority())
{
try
{
// check if there's a backlog in the tx pool
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const uint64_t base_fee = get_base_fee(1);
const uint64_t base_fee = get_base_fee(fee_priority::Unimportant);
const double fee_level = base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
if (blocks.size() != 1)
@@ -8651,7 +8664,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
else if (blocks[0].first > 0)
{
MINFO("We don't use the low priority because there's a backlog in the tx pool.");
return 2;
return fee_priority::Normal;
}
// get the current full reward zone
@@ -8696,10 +8709,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
if (P > 80)
{
MINFO("We don't use the low priority because recent blocks are quite full.");
return 2;
return fee_priority::Normal;
}
MINFO("We'll use the low priority because probably it's safe to do so.");
return 1;
return fee_priority::Unimportant;
}
catch (const std::exception &e)
{
@@ -10420,7 +10433,7 @@ static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &tr
// This system allows for sending (almost) the entire balance, since it does
// not generate spurious change in all txes, thus decreasing the instantaneous
// usable balance.
std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs)
std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, fee_priority priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs)
{
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
@@ -11188,7 +11201,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c
return true;
}
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, fee_priority priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@@ -11261,7 +11274,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, priority, extra);
}
std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra)
std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, fee_priority priority, const std::vector<uint8_t>& extra)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@@ -11282,7 +11295,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, priority, extra);
}
std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra)
std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, fee_priority priority, const std::vector<uint8_t>& extra)
{
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
@@ -11751,7 +11764,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
const uint64_t base_fee = get_base_fee(1);
const uint64_t base_fee = get_base_fee(fee_priority::Unimportant);
// may throw
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
@@ -11772,7 +11785,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
unmixable_transfer_outputs.push_back(n);
}
return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 1 /*priority */, std::vector<uint8_t>());
return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, fee_priority::Unimportant, std::vector<uint8_t>());
}
//----------------------------------------------------------------------------------------------------
void wallet2::discard_unmixable_outputs()