Fee changes from ArticMine
https://github.com/ArticMine/Monero-Documents/blob/master/MoneroScaling2021-02.pdf with a change to use 1.7 instead of 2.0 for the max long term increase rate
This commit is contained in:
parent
9f786f0550
commit
b030f20751
@ -1064,6 +1064,69 @@ namespace cryptonote
|
||||
return s;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
uint64_t round_money_up(uint64_t amount, unsigned significant_digits)
|
||||
{
|
||||
// round monetary amount up with the requested amount of significant digits
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(significant_digits > 0, "significant_digits must not be 0");
|
||||
static_assert(sizeof(unsigned long long) == sizeof(uint64_t), "Unexpected unsigned long long size");
|
||||
|
||||
// we don't need speed, so we do it via text, as it's easier to get right
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)amount);
|
||||
const size_t len = strlen(buf);
|
||||
if (len > significant_digits)
|
||||
{
|
||||
bool bump = false;
|
||||
char *ptr;
|
||||
for (ptr = buf + significant_digits; *ptr; ++ptr)
|
||||
{
|
||||
// bump digits by one if the following digits past significant digits were to be 5 or more
|
||||
if (*ptr != '0')
|
||||
{
|
||||
bump = true;
|
||||
*ptr = '0';
|
||||
}
|
||||
}
|
||||
ptr = buf + significant_digits;
|
||||
while (bump && ptr > buf)
|
||||
{
|
||||
--ptr;
|
||||
// bumping a nine overflows
|
||||
if (*ptr == '9')
|
||||
*ptr = '0';
|
||||
else
|
||||
{
|
||||
// bumping another digit means we can stop bumping (no carry)
|
||||
++*ptr;
|
||||
bump = false;
|
||||
}
|
||||
}
|
||||
if (bump)
|
||||
{
|
||||
// carry reached the highest digit, we need to add a 1 in front
|
||||
size_t offset = strlen(buf);
|
||||
for (size_t i = offset + 1; i > 0; --i)
|
||||
buf[i] = buf[i - 1];
|
||||
buf[0] = '1';
|
||||
}
|
||||
}
|
||||
char *end = NULL;
|
||||
errno = 0;
|
||||
const unsigned long long ull = strtoull(buf, &end, 10);
|
||||
CHECK_AND_ASSERT_THROW_MES(ull != ULONG_MAX || errno == 0, "Failed to parse rounded amount: " << buf);
|
||||
CHECK_AND_ASSERT_THROW_MES(ull != 0 || amount == 0, "Overflow in rounding");
|
||||
return ull;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
std::string round_money_up(const std::string &s, unsigned significant_digits)
|
||||
{
|
||||
uint64_t amount;
|
||||
CHECK_AND_ASSERT_THROW_MES(parse_amount(amount, s), "Failed to parse amount: " << s);
|
||||
amount = round_money_up(amount, significant_digits);
|
||||
return print_money(amount);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
crypto::hash get_blob_hash(const blobdata& blob)
|
||||
{
|
||||
crypto::hash h = null_hash;
|
||||
|
@ -144,6 +144,8 @@ namespace cryptonote
|
||||
std::string get_unit(unsigned int decimal_point = -1);
|
||||
std::string print_money(uint64_t amount, unsigned int decimal_point = -1);
|
||||
std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point = -1);
|
||||
uint64_t round_money_up(uint64_t amount, unsigned significant_digits);
|
||||
std::string round_money_up(const std::string &amount, unsigned significant_digits);
|
||||
//---------------------------------------------------------------
|
||||
template<class t_object>
|
||||
bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob)
|
||||
|
@ -183,8 +183,10 @@
|
||||
#define HF_VERSION_CLSAG 13
|
||||
#define HF_VERSION_DETERMINISTIC_UNLOCK_TIME 13
|
||||
#define HF_VERSION_BULLETPROOF_PLUS 15
|
||||
#define HF_VERSION_2021_SCALING 15
|
||||
|
||||
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
|
||||
#define CRYPTONOTE_SCALING_2021_FEE_ROUNDING_PLACES 2
|
||||
|
||||
#define HASH_OF_HASHES_STEP 512
|
||||
|
||||
|
@ -3710,11 +3710,24 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b
|
||||
if (version >= HF_VERSION_PER_BYTE_FEE)
|
||||
{
|
||||
lo = mul128(block_reward, DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT, &hi);
|
||||
div128_64(hi, lo, min_block_weight, &hi, &lo, NULL, NULL);
|
||||
div128_64(hi, lo, median_block_weight, &hi, &lo, NULL, NULL);
|
||||
assert(hi == 0);
|
||||
lo /= 5;
|
||||
return lo;
|
||||
if (version >= HF_VERSION_2021_SCALING)
|
||||
{
|
||||
// min_fee_per_byte = round_up( 0.95 * block_reward * ref_weight / (fee_median^2) )
|
||||
// note: since hardfork HF_VERSION_2021_SCALING, fee_median (a.k.a. median_block_weight) equals effective long term median
|
||||
div128_64(hi, lo, median_block_weight, &hi, &lo, NULL, NULL);
|
||||
assert(hi == 0);
|
||||
lo -= lo / 20;
|
||||
return lo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// min_fee_per_byte = 0.2 * block_reward * ref_weight / (min_penalty_free_zone * fee_median)
|
||||
div128_64(hi, lo, min_block_weight, &hi, &lo, NULL, NULL);
|
||||
assert(hi == 0);
|
||||
lo /= 5;
|
||||
return lo;
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t fee_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE;
|
||||
@ -3786,6 +3799,81 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, uint64_t base_reward, uint64_t Mnw, uint64_t Mlw, std::vector<uint64_t> &fees) const
|
||||
{
|
||||
// variable names and calculations as per https://github.com/ArticMine/Monero-Documents/blob/master/MoneroScaling2021-02.pdf
|
||||
// from (earlier than) this fork, the base fee is per byte
|
||||
const uint64_t Mfw = std::min(Mnw, Mlw);
|
||||
|
||||
// 3 kB divided by something ? It's going to be either 0 or *very* quantized, so fold it into integer steps below
|
||||
//const uint64_t Brlw = DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT / Mfw;
|
||||
|
||||
// constant.... equal to 0, unless floating point, so fold it into integer steps below
|
||||
//const uint64_t Br = DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5
|
||||
|
||||
//const uint64_t Fl = base_reward * Brlw / Mfw; fold Brlw from above
|
||||
const uint64_t Fl = base_reward * /*Brlw*/ DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT / (Mfw * Mfw);
|
||||
|
||||
// fold Fl into this for better precision (and to match the test cases in the PDF)
|
||||
// const uint64_t Fn = 4 * Fl;
|
||||
const uint64_t Fn = 4 * base_reward * /*Brlw*/ DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT / (Mfw * Mfw);
|
||||
|
||||
// const uint64_t Fm = 16 * base_reward * Br / Mfw; fold Br from above
|
||||
const uint64_t Fm = 16 * base_reward * DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT / (CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 * Mfw);
|
||||
|
||||
// const uint64_t Fp = 2 * base_reward / Mnw;
|
||||
|
||||
// fold Br from above, move 4Fm in the max to decrease quantization effect
|
||||
//const uint64_t Fh = 4 * Fm * std::max<uint64_t>(1, Mfw / (32 * DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT * Mnw / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5));
|
||||
const uint64_t Fh = std::max<uint64_t>(4 * Fm, 4 * Fm * Mfw / (32 * DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT * Mnw / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5));
|
||||
|
||||
fees.resize(4);
|
||||
fees[0] = cryptonote::round_money_up(Fl, CRYPTONOTE_SCALING_2021_FEE_ROUNDING_PLACES);
|
||||
fees[1] = cryptonote::round_money_up(Fn, CRYPTONOTE_SCALING_2021_FEE_ROUNDING_PLACES);
|
||||
fees[2] = cryptonote::round_money_up(Fm, CRYPTONOTE_SCALING_2021_FEE_ROUNDING_PLACES);
|
||||
fees[3] = cryptonote::round_money_up(Fh, CRYPTONOTE_SCALING_2021_FEE_ROUNDING_PLACES);
|
||||
}
|
||||
|
||||
void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees) const
|
||||
{
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const uint64_t db_height = m_db->height();
|
||||
|
||||
// we want Mlw = median of max((min(Mbw, 1.7 * Ml), Zm), Ml / 1.7)
|
||||
// Mbw: block weight for the last 99990 blocks, 0 for the next 10
|
||||
// Ml: penalty free zone (dynamic), aka long_term_median, aka median of max((min(Mb, 1.7 * Ml), Zm), Ml / 1.7)
|
||||
// Zm: 300000 (minimum penalty free zone)
|
||||
//
|
||||
// So we copy the current rolling median state, add 10 (grace_blocks) zeroes to it, and get back Mlw
|
||||
|
||||
epee::misc_utils::rolling_median_t<uint64_t> rm = m_long_term_block_weights_cache_rolling_median;
|
||||
for (size_t i = 0; i < grace_blocks; ++i)
|
||||
rm.insert(0);
|
||||
const uint64_t Mlw_penalty_free_zone_for_wallet = std::max<uint64_t>(rm.median(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5);
|
||||
|
||||
// Msw: median over [100 - grace blocks] past + [grace blocks] future blocks
|
||||
CHECK_AND_ASSERT_THROW_MES(grace_blocks <= 100, "Grace blocks invalid In 2021 fee scaling estimate.");
|
||||
std::vector<uint64_t> weights;
|
||||
get_last_n_blocks_weights(weights, 100 - grace_blocks);
|
||||
weights.reserve(100);
|
||||
for (size_t i = 0; i < grace_blocks; ++i)
|
||||
weights.push_back(0);
|
||||
const uint64_t Msw_effective_short_term_median = std::max(epee::misc_utils::median(weights), Mlw_penalty_free_zone_for_wallet);
|
||||
|
||||
const uint64_t Mnw = std::min(Msw_effective_short_term_median, 50 * Mlw_penalty_free_zone_for_wallet);
|
||||
|
||||
uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0;
|
||||
uint64_t base_reward;
|
||||
if (!get_block_reward(m_current_block_cumul_weight_limit / 2, 1, already_generated_coins, base_reward, version))
|
||||
{
|
||||
MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound");
|
||||
base_reward = BLOCK_REWARD_OVERESTIMATE;
|
||||
}
|
||||
|
||||
get_dynamic_base_fee_estimate_2021_scaling(grace_blocks, base_reward, Mnw, Mlw_penalty_free_zone_for_wallet, fees);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
|
||||
{
|
||||
@ -3798,6 +3886,13 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
|
||||
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
|
||||
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
|
||||
|
||||
if (version >= HF_VERSION_2021_SCALING)
|
||||
{
|
||||
std::vector<uint64_t> fees;
|
||||
get_dynamic_base_fee_estimate_2021_scaling(grace_blocks, fees);
|
||||
return fees[0];
|
||||
}
|
||||
|
||||
const uint64_t min_block_weight = get_min_block_weight(version);
|
||||
std::vector<uint64_t> weights;
|
||||
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
|
||||
@ -4472,6 +4567,7 @@ bool Blockchain::check_blockchain_pruning()
|
||||
return m_db->check_pruning();
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// returns min(Mb, 1.7*Ml) as per https://github.com/ArticMine/Monero-Documents/blob/master/MoneroScaling2021-02.pdf from HF_VERSION_LONG_TERM_BLOCK_WEIGHT
|
||||
uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) const
|
||||
{
|
||||
PERF_TIMER(get_next_long_term_block_weight);
|
||||
@ -4486,7 +4582,18 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
|
||||
uint64_t long_term_median = get_long_term_block_weight_median(db_height - nblocks, nblocks);
|
||||
uint64_t long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
|
||||
|
||||
uint64_t short_term_constraint = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 2 / 5;
|
||||
uint64_t short_term_constraint;
|
||||
if (hf_version >= HF_VERSION_2021_SCALING)
|
||||
{
|
||||
// long_term_block_weight = block_weight bounded to range [long-term-median/1.7, long-term-median*1.7]
|
||||
block_weight = std::max<uint64_t>(block_weight, long_term_effective_median_block_weight * 10 / 17);
|
||||
short_term_constraint = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 7 / 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
// long_term_block_weight = block_weight bounded to range [0, long-term-median*1.4]
|
||||
short_term_constraint = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 2 / 5;
|
||||
}
|
||||
uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
|
||||
|
||||
return long_term_block_weight;
|
||||
@ -4528,7 +4635,11 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
|
||||
|
||||
m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
|
||||
|
||||
uint64_t short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5;
|
||||
uint64_t short_term_constraint = m_long_term_effective_median_block_weight;
|
||||
if (hf_version >= HF_VERSION_2021_SCALING)
|
||||
short_term_constraint += m_long_term_effective_median_block_weight * 7 / 10;
|
||||
else
|
||||
short_term_constraint += m_long_term_effective_median_block_weight * 2 / 5;
|
||||
uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
|
||||
|
||||
if (db_height == 1)
|
||||
@ -4547,7 +4658,19 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
|
||||
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
|
||||
uint64_t short_term_median = epee::misc_utils::median(weights);
|
||||
uint64_t effective_median_block_weight = std::min<uint64_t>(std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, short_term_median), CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * m_long_term_effective_median_block_weight);
|
||||
uint64_t effective_median_block_weight;
|
||||
if (hf_version >= HF_VERSION_2021_SCALING)
|
||||
{
|
||||
// effective median = short_term_median bounded to range [long_term_median, 50*long_term_median], but it can't be smaller than the
|
||||
// minimum penalty free zone (a.k.a. 'full reward zone')
|
||||
effective_median_block_weight = std::min<uint64_t>(std::max<uint64_t>(m_long_term_effective_median_block_weight, short_term_median), CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * m_long_term_effective_median_block_weight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// effective median = short_term_median bounded to range [0, 50*long_term_median], but it can't be smaller than the
|
||||
// minimum penalty free zone (a.k.a. 'full reward zone')
|
||||
effective_median_block_weight = std::min<uint64_t>(std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, short_term_median), CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * m_long_term_effective_median_block_weight);
|
||||
}
|
||||
|
||||
m_current_block_cumul_weight_median = effective_median_block_weight;
|
||||
}
|
||||
|
@ -648,6 +648,22 @@ namespace cryptonote
|
||||
* @return the fee estimate
|
||||
*/
|
||||
uint64_t get_dynamic_base_fee_estimate(uint64_t grace_blocks) const;
|
||||
void get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, uint64_t base_reward, uint64_t Mnw, uint64_t Mlw, std::vector<uint64_t> &fees) const;
|
||||
|
||||
/**
|
||||
* @brief get four levels of dynamic per byte fee estimate for the next few blocks
|
||||
*
|
||||
* The dynamic fee is based on the block weight in a past window, and
|
||||
* the current block reward. It is expressed per byte, and is based on
|
||||
* https://github.com/ArticMine/Monero-Documents/blob/master/MoneroScaling2021-02.pdf
|
||||
* This function calculates an estimate for a dynamic fee which will be
|
||||
* valid for the next grace_blocks
|
||||
*
|
||||
* @param grace_blocks number of blocks we want the fee to be valid for
|
||||
*
|
||||
* @return the fee estimates (4 of them)
|
||||
*/
|
||||
void get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees) const;
|
||||
|
||||
/**
|
||||
* @brief validate a transaction's fee
|
||||
|
@ -2890,7 +2890,17 @@ namespace cryptonote
|
||||
return r;
|
||||
|
||||
CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE);
|
||||
res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks);
|
||||
|
||||
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
|
||||
if (version >= HF_VERSION_2021_SCALING)
|
||||
{
|
||||
m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.grace_blocks, res.fees);
|
||||
res.fee = res.fees[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks);
|
||||
}
|
||||
res.quantization_mask = Blockchain::get_fee_quantization_mask();
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
|
@ -2191,11 +2191,13 @@ namespace cryptonote
|
||||
{
|
||||
uint64_t fee;
|
||||
uint64_t quantization_mask;
|
||||
std::vector<uint64_t> fees;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_PARENT(rpc_access_response_base)
|
||||
KV_SERIALIZE(fee)
|
||||
KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1)
|
||||
KV_SERIALIZE(fees)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
|
@ -1744,7 +1744,6 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::str
|
||||
m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0),
|
||||
m_wallet->use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, 0),
|
||||
m_wallet->get_base_fee(),
|
||||
m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))),
|
||||
m_wallet->get_fee_quantization_mask());
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@ void NodeRPCProxy::invalidate()
|
||||
m_dynamic_base_fee_estimate = 0;
|
||||
m_dynamic_base_fee_estimate_cached_height = 0;
|
||||
m_dynamic_base_fee_estimate_grace_blocks = 0;
|
||||
m_dynamic_base_fee_estimate_vector.clear();
|
||||
m_fee_quantization_mask = 1;
|
||||
m_rpc_version = 0;
|
||||
m_target_height = 0;
|
||||
@ -210,7 +211,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee)
|
||||
boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees)
|
||||
{
|
||||
uint64_t height;
|
||||
|
||||
@ -238,13 +239,24 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_
|
||||
m_dynamic_base_fee_estimate = resp_t.fee;
|
||||
m_dynamic_base_fee_estimate_cached_height = height;
|
||||
m_dynamic_base_fee_estimate_grace_blocks = grace_blocks;
|
||||
m_dynamic_base_fee_estimate_vector = !resp_t.fees.empty() ? std::move(resp_t.fees) : std::vector<uint64_t>{m_dynamic_base_fee_estimate};
|
||||
m_fee_quantization_mask = resp_t.quantization_mask;
|
||||
}
|
||||
|
||||
fee = m_dynamic_base_fee_estimate;
|
||||
fees = m_dynamic_base_fee_estimate_vector;
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee)
|
||||
{
|
||||
std::vector<uint64_t> fees;
|
||||
auto res = get_dynamic_base_fee_estimate_2021_scaling(grace_blocks, fees);
|
||||
if (res)
|
||||
return res;
|
||||
fee = fees[0];
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask)
|
||||
{
|
||||
uint64_t height;
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
boost::optional<std::string> get_adjusted_time(uint64_t &adjusted_time);
|
||||
boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height);
|
||||
boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee);
|
||||
boost::optional<std::string> get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees);
|
||||
boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask);
|
||||
boost::optional<std::string> get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie);
|
||||
|
||||
@ -85,6 +86,7 @@ private:
|
||||
uint64_t m_dynamic_base_fee_estimate;
|
||||
uint64_t m_dynamic_base_fee_estimate_cached_height;
|
||||
uint64_t m_dynamic_base_fee_estimate_grace_blocks;
|
||||
std::vector<uint64_t> m_dynamic_base_fee_estimate_vector;
|
||||
uint64_t m_fee_quantization_mask;
|
||||
uint64_t m_adjusted_time;
|
||||
uint32_t m_rpc_version;
|
||||
|
@ -290,15 +290,15 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
|
||||
mms_file = file_path + ".mms";
|
||||
}
|
||||
|
||||
uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
|
||||
uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes)
|
||||
{
|
||||
uint64_t kB = (bytes + 1023) / 1024;
|
||||
return kB * fee_per_kb * fee_multiplier;
|
||||
return kB * fee_per_kb;
|
||||
}
|
||||
|
||||
uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
|
||||
uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_quantization_mask)
|
||||
{
|
||||
uint64_t fee = weight * base_fee * fee_multiplier;
|
||||
uint64_t fee = weight * base_fee;
|
||||
fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
|
||||
return fee;
|
||||
}
|
||||
@ -878,12 +878,12 @@ uint8_t get_clsag_fork()
|
||||
return HF_VERSION_CLSAG;
|
||||
}
|
||||
|
||||
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
|
||||
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_quantization_mask)
|
||||
{
|
||||
if (use_per_byte_fee)
|
||||
return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
|
||||
return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_quantization_mask);
|
||||
else
|
||||
return calculate_fee(base_fee, blob_size, fee_multiplier);
|
||||
return calculate_fee(base_fee, blob_size);
|
||||
}
|
||||
|
||||
bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
|
||||
@ -7232,17 +7232,17 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
|
||||
return sign_multisig_tx_to_file(exported_txs, filename, txids);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
|
||||
uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_quantization_mask) const
|
||||
{
|
||||
if (use_per_byte_fee)
|
||||
{
|
||||
const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
|
||||
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_quantization_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
|
||||
return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
|
||||
return calculate_fee(base_fee, estimated_tx_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7315,6 +7315,40 @@ uint64_t wallet2::get_base_fee()
|
||||
return get_dynamic_base_fee_estimate();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::get_base_fee(uint32_t 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;
|
||||
|
||||
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);
|
||||
if (result)
|
||||
{
|
||||
MERROR("Failed to determine base fee, using default");
|
||||
return FEE_PER_BYTE;
|
||||
}
|
||||
if (priority >= fees.size())
|
||||
{
|
||||
MERROR("Failed to determine base fee for priority " << priority << ", using default");
|
||||
return FEE_PER_BYTE;
|
||||
}
|
||||
return fees[priority];
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(priority);
|
||||
return base_fee * fee_multiplier;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::get_fee_quantization_mask()
|
||||
{
|
||||
if(m_light_wallet)
|
||||
@ -7389,9 +7423,8 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
|
||||
{
|
||||
// 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();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(1);
|
||||
const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
|
||||
const uint64_t base_fee = get_base_fee(1);
|
||||
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)
|
||||
{
|
||||
@ -9665,8 +9698,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
bulletproof_plus ? 4 : 3
|
||||
};
|
||||
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||
const uint64_t base_fee = get_base_fee(priority);
|
||||
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
|
||||
|
||||
// throw if attempting a transaction with no destinations
|
||||
@ -9698,7 +9730,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
// early out if we know we can't make it anyway
|
||||
// we could also check for being within FEE_PER_KB, but if the fee calculation
|
||||
// ever changes, this might be missed, so let this go through
|
||||
const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus));
|
||||
const uint64_t min_fee = (base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus));
|
||||
uint64_t balance_subtotal = 0;
|
||||
uint64_t unlocked_balance_subtotal = 0;
|
||||
for (uint32_t index_minor : subaddr_indices)
|
||||
@ -9720,7 +9752,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
|
||||
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
|
||||
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
|
||||
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
|
||||
// gather all dust and non-dust outputs belonging to specified subaddresses
|
||||
size_t num_nondust_outputs = 0;
|
||||
@ -9814,7 +9846,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
{
|
||||
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
|
||||
// will get us a known fee.
|
||||
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_quantization_mask);
|
||||
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
|
||||
if (!preferred_inputs.empty())
|
||||
{
|
||||
@ -9994,7 +10026,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
pending_tx test_ptx;
|
||||
|
||||
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_quantization_mask);
|
||||
|
||||
auto try_carving_from_partial_payment = [&](uint64_t needed_fee, uint64_t available_for_fee)
|
||||
{
|
||||
@ -10048,7 +10080,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
|
||||
LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
|
||||
print_money(needed_fee) << " needed)");
|
||||
@ -10073,7 +10105,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
|
||||
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
|
||||
}
|
||||
@ -10256,13 +10288,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
|
||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||
const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
|
||||
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||
const uint64_t base_fee = get_base_fee(priority);
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
|
||||
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
|
||||
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
|
||||
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, false) == 0, error::wallet_internal_error, "No unlocked balance in the specified account");
|
||||
|
||||
@ -10371,8 +10402,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
rct::RangeProofPaddedBulletproof,
|
||||
bulletproof_plus ? 4 : 3
|
||||
};
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||
const uint64_t base_fee = get_base_fee(priority);
|
||||
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
|
||||
|
||||
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
|
||||
@ -10399,11 +10429,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
|
||||
{
|
||||
const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus);
|
||||
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
|
||||
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_quantization_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
|
||||
fee_dust_threshold = base_fee * (upper_transaction_weight_limit + 1023) / 1024;
|
||||
}
|
||||
|
||||
size_t idx =
|
||||
@ -10437,7 +10467,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
pending_tx test_ptx;
|
||||
|
||||
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_quantization_mask);
|
||||
|
||||
// add N - 1 outputs for correct initial fee estimation
|
||||
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
|
||||
@ -10452,7 +10482,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
|
||||
for (auto &dt: test_ptx.dests)
|
||||
available_for_fee += dt.amount;
|
||||
@ -10489,7 +10519,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
|
||||
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
|
||||
} while (needed_fee > test_ptx.fee);
|
||||
@ -10816,7 +10846,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();
|
||||
const uint64_t base_fee = get_base_fee(1);
|
||||
|
||||
// may throw
|
||||
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
|
||||
|
@ -1427,8 +1427,9 @@ private:
|
||||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
|
||||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
|
||||
|
||||
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
|
||||
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_quantization_mask) const;
|
||||
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
|
||||
uint64_t get_base_fee(uint32_t priority);
|
||||
uint64_t get_base_fee();
|
||||
uint64_t get_fee_quantization_mask();
|
||||
uint64_t get_min_ring_size();
|
||||
|
@ -81,10 +81,10 @@ class BlockchainTest():
|
||||
assert ok
|
||||
|
||||
res = daemon.get_fee_estimate()
|
||||
assert res.fee == 234562
|
||||
assert res.fee == 1200000
|
||||
assert res.quantization_mask == 10000
|
||||
res = daemon.get_fee_estimate(10)
|
||||
assert res.fee <= 234562
|
||||
assert res.fee <= 1200000
|
||||
|
||||
# generate blocks
|
||||
res_generateblocks = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks)
|
||||
@ -243,10 +243,10 @@ class BlockchainTest():
|
||||
assert res.histogram[i].recent_instances == 0
|
||||
|
||||
res = daemon.get_fee_estimate()
|
||||
assert res.fee == 234560
|
||||
assert res.fee == 1200000
|
||||
assert res.quantization_mask == 10000
|
||||
res = daemon.get_fee_estimate(10)
|
||||
assert res.fee <= 234560
|
||||
assert res.fee <= 1200000
|
||||
|
||||
def _test_alt_chains(self):
|
||||
print('Testing alt chains')
|
||||
|
@ -77,6 +77,7 @@ set(unit_tests_sources
|
||||
pruning.cpp
|
||||
random.cpp
|
||||
rolling_median.cpp
|
||||
scaling_2021.cpp
|
||||
serialization.cpp
|
||||
sha256.cpp
|
||||
slow_memmem.cpp
|
||||
|
187
tests/unit_tests/scaling_2021.cpp
Normal file
187
tests/unit_tests/scaling_2021.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright (c) 2019-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// References:
|
||||
// - https://github.com/ArticMine/Monero-Documents/blob/master/MoneroScaling2021.pdf
|
||||
// - https://github.com/monero-project/research-lab/issues/70
|
||||
|
||||
#define IN_UNIT_TESTS
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "cryptonote_core/tx_pool.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "blockchain_db/testdb.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class TestDB: public cryptonote::BaseTestDB
|
||||
{
|
||||
public:
|
||||
TestDB() { m_open = true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define PREFIX_WINDOW(hf_version,window) \
|
||||
std::unique_ptr<cryptonote::Blockchain> bc; \
|
||||
cryptonote::tx_memory_pool txpool(*bc); \
|
||||
bc.reset(new cryptonote::Blockchain(txpool)); \
|
||||
struct get_test_options { \
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[3]; \
|
||||
const cryptonote::test_options test_options = { \
|
||||
hard_forks, \
|
||||
window, \
|
||||
}; \
|
||||
get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)1), std::make_pair((uint8_t)0, (uint64_t)0)} {} \
|
||||
} opts; \
|
||||
cryptonote::Blockchain *blockchain = bc.get(); \
|
||||
bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \
|
||||
ASSERT_TRUE(r)
|
||||
|
||||
#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW)
|
||||
|
||||
TEST(fee_2021_scaling, relay_fee_cases_from_pdf)
|
||||
{
|
||||
PREFIX_WINDOW(HF_VERSION_2021_SCALING, CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE);
|
||||
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(1200000000000, 300000, HF_VERSION_2021_SCALING-1), 8000);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(1200000000000, 300000, HF_VERSION_2021_SCALING), 38000);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(1200000000000, 1425000, HF_VERSION_2021_SCALING-1), 1684 /*1680*/);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(1200000000000, 1425000, HF_VERSION_2021_SCALING), 1684 /*1680*/);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(1200000000000, 1500000, HF_VERSION_2021_SCALING-1), 1600);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(1200000000000, 1500000, HF_VERSION_2021_SCALING), 1520);
|
||||
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(600000000000, 300000, HF_VERSION_2021_SCALING-1), 4000);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(600000000000, 300000, HF_VERSION_2021_SCALING), 19000);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(600000000000, 1425000, HF_VERSION_2021_SCALING-1), 842 /*840*/);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(600000000000, 1425000, HF_VERSION_2021_SCALING), 842 /*840*/);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(600000000000, 1500000, HF_VERSION_2021_SCALING-1), 800);
|
||||
ASSERT_EQ(bc->get_dynamic_base_fee(600000000000, 1500000, HF_VERSION_2021_SCALING), 760);
|
||||
}
|
||||
|
||||
TEST(fee_2021_scaling, wallet_fee_cases_from_pdf)
|
||||
{
|
||||
PREFIX_WINDOW(HF_VERSION_2021_SCALING, CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE);
|
||||
std::vector<uint64_t> fees;
|
||||
|
||||
fees.clear();
|
||||
bc->get_dynamic_base_fee_estimate_2021_scaling(10, 600000000000, 300000, 300000, fees);
|
||||
ASSERT_EQ(fees.size(), 4);
|
||||
ASSERT_EQ(fees[0], 20000);
|
||||
ASSERT_EQ(fees[1], 80000);
|
||||
ASSERT_EQ(fees[2], 320000);
|
||||
ASSERT_EQ(fees[3], 4000000);
|
||||
|
||||
fees.clear();
|
||||
bc->get_dynamic_base_fee_estimate_2021_scaling(10, 600000000000, 15000000, 300000, fees);
|
||||
ASSERT_EQ(fees.size(), 4);
|
||||
ASSERT_EQ(fees[0], 20000);
|
||||
ASSERT_EQ(fees[1], 80000);
|
||||
ASSERT_EQ(fees[2], 320000);
|
||||
ASSERT_EQ(fees[3], 1300000);
|
||||
|
||||
fees.clear();
|
||||
bc->get_dynamic_base_fee_estimate_2021_scaling(10, 600000000000, 1425000, 1425000, fees);
|
||||
ASSERT_EQ(fees.size(), 4);
|
||||
ASSERT_EQ(fees[0], 890);
|
||||
ASSERT_EQ(fees[1], 3600);
|
||||
ASSERT_EQ(fees[2], 68000);
|
||||
ASSERT_EQ(fees[3], 850000 /* 842000 */);
|
||||
|
||||
fees.clear();
|
||||
bc->get_dynamic_base_fee_estimate_2021_scaling(10, 600000000000, 1500000, 1500000, fees);
|
||||
ASSERT_EQ(fees.size(), 4);
|
||||
ASSERT_EQ(fees[0], 800);
|
||||
ASSERT_EQ(fees[1], 3200);
|
||||
ASSERT_EQ(fees[2], 64000);
|
||||
ASSERT_EQ(fees[3], 800000);
|
||||
|
||||
fees.clear();
|
||||
bc->get_dynamic_base_fee_estimate_2021_scaling(10, 600000000000, 75000000, 1500000, fees);
|
||||
ASSERT_EQ(fees.size(), 4);
|
||||
ASSERT_EQ(fees[0], 800);
|
||||
ASSERT_EQ(fees[1], 3200);
|
||||
ASSERT_EQ(fees[2], 64000);
|
||||
ASSERT_EQ(fees[3], 260000);
|
||||
}
|
||||
|
||||
TEST(fee_2021_scaling, rounding)
|
||||
{
|
||||
ASSERT_EQ(cryptonote::round_money_up("27810", 3), "27900.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("37.94", 3), "38.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("0.5555", 3), "0.556000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("0.002342", 3), "0.002350000000");
|
||||
|
||||
ASSERT_EQ(cryptonote::round_money_up("27810", 2), "28000.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("37.94", 2), "38.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("0.5555", 2), "0.560000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("0.002342", 2), "0.002400000000");
|
||||
|
||||
ASSERT_EQ(cryptonote::round_money_up("0", 8), "0.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("0.0", 8), "0.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("50.0", 8), "50.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("0.002342", 8), "0.002342000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("0.002342", 1), "0.003000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("12345", 8), "12345.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("45678", 1), "50000.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.234", 1), "2.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.0000001", 4), "1.001000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.0020001", 4), "1.003000000000");
|
||||
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 1), "2.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 2), "2.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 3), "2.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 4), "2.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 5), "2.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 6), "2.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 7), "1.999999000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 8), "1.999999000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("1.999999", 9), "1.999999000000");
|
||||
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 1), "3.000000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 2), "2.100000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 3), "2.010000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 4), "2.001000000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 5), "2.000100000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 6), "2.000010000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 7), "2.000001000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 8), "2.000001000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 9), "2.000001000000");
|
||||
ASSERT_EQ(cryptonote::round_money_up("2.000001", 4000), "2.000001000000");
|
||||
|
||||
ASSERT_EQ(cryptonote::round_money_up("999", 2), "1000.000000000000");
|
||||
|
||||
ASSERT_THROW(cryptonote::round_money_up("1.23", 0), std::runtime_error);
|
||||
ASSERT_THROW(cryptonote::round_money_up("18446744.073709551615", 1), std::runtime_error);
|
||||
ASSERT_THROW(cryptonote::round_money_up("18446744.073709551615", 2), std::runtime_error);
|
||||
ASSERT_THROW(cryptonote::round_money_up("18446744.073709551615", 12), std::runtime_error);
|
||||
ASSERT_THROW(cryptonote::round_money_up("18446744.073709551615", 19), std::runtime_error);
|
||||
ASSERT_EQ(cryptonote::round_money_up("18446744.073709551615", 20), "18446744.073709551615");
|
||||
}
|
Loading…
Reference in New Issue
Block a user