From 82dbba10d467e28e56929e2e7f3b1f04d4635da4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 27 Oct 2016 23:43:44 +0100 Subject: core: dynamic fee algorithm from ArticMine The fee will vary based on the base reward and the current block size limit: fee = (R/R0) * (M0/M) * F0 R: base reward R0: reference base reward (10 monero) M: block size limit M0: minimum block size limit (60000) F0: 0.002 monero Starts applying at v4 --- src/cryptonote_config.h | 4 +++ src/cryptonote_core/blockchain.cpp | 52 ++++++++++++++++++++++++++++++++++++++ src/cryptonote_core/blockchain.h | 27 ++++++++++++++++++++ src/cryptonote_core/tx_pool.cpp | 6 +---- 4 files changed, 84 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 66084da3c..175a5d26e 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -64,6 +64,8 @@ #define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10) #define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) +#define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9) +#define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12) #define ORPHANED_BLOCKS_MAX_COUNT 100 @@ -122,6 +124,8 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 +#define HF_VERSION_DYNAMIC_FEE 4 + // New constants are intended to go here namespace config { diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 9ea023a4c..c92ee6c9b 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -46,6 +46,7 @@ #include "misc_language.h" #include "profile_tools.h" #include "file_io_utils.h" +#include "common/int-util.h" #include "common/boost_serialization_helper.h" #include "warnings.h" #include "crypto/hash.h" @@ -2708,6 +2709,57 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const result = crypto::check_ring_signature(tx_prefix_hash, key_image, p_output_keys, sig.data()) ? 1 : 0; } +//------------------------------------------------------------------ +uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size) +{ + if (median_block_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2) + median_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + + uint64_t unscaled_fee_per_kb = (DYNAMIC_FEE_PER_KB_BASE_FEE * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / median_block_size); + uint64_t hi, lo = mul128(unscaled_fee_per_kb, block_reward, &hi); + static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); + static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); + // divide in two steps, since the divisor must be 32 bits, but DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD isn't + div128_32(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000, &hi, &lo); + div128_32(hi, lo, 1000000, &hi, &lo); + assert(hi == 0); + + return lo; +} + +//------------------------------------------------------------------ +bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const +{ + const uint8_t version = get_current_hard_fork_version(); + + uint64_t fee_per_kb; + if (version < HF_VERSION_DYNAMIC_FEE) + { + fee_per_kb = FEE_PER_KB; + } + else + { + uint64_t median = m_current_block_cumul_sz_limit / 2; + uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; + uint64_t base_reward; + if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) + return false; + fee_per_kb = get_dynamic_per_kb_fee(base_reward, median); + } + LOG_PRINT_L2("Using " << print_money(fee) << "/kB fee"); + + uint64_t needed_fee = blob_size / 1024; + needed_fee += (blob_size % 1024) ? 1 : 0; + needed_fee *= fee_per_kb; + + if (fee < needed_fee) + { + LOG_PRINT_L1("transaction fee is not enough: " << print_money(fee) << ", minimum fee: " << print_money(needed_fee)); + return false; + } + return true; +} + //------------------------------------------------------------------ // This function checks to see if a tx is unlocked. unlock_time is either // a block index or a unix time. diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 262c2952b..3311798f0 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -512,6 +512,33 @@ namespace cryptonote */ bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); + /** + * @brief get dynamic per kB fee for a given block size + * + * The dynamic fee is based on the block size in a past window, and + * the current block reward. It is expressed by kB. + * + * @param block_reward the current block reward + * @param median_block_size the median blob's size in the past window + * + * @return the per kB fee + */ + static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size); + + /** + * @brief validate a transaction's fee + * + * This function validates the fee is enough for the transaction. + * This is based on the size of the transaction blob, and, after a + * height threshold, on the average size of transaction in a past window + * + * @param blob_size the transaction blob's size + * @param fee the fee + * + * @return true if the fee is enough, false otherwise + */ + bool check_fee(size_t blob_size, uint64_t fee) const; + /** * @brief check that a transaction's outputs conform to current standards * diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index dba05a539..e72a592ca 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -133,12 +133,8 @@ namespace cryptonote fee = tx.rct_signatures.txnFee; } - uint64_t needed_fee = blob_size / 1024; - needed_fee += (blob_size % 1024) ? 1 : 0; - needed_fee *= FEE_PER_KB; - if (!kept_by_block && fee < needed_fee) + if (!kept_by_block && !m_blockchain.check_fee(blob_size, fee)) { - LOG_PRINT_L1("transaction fee is not enough: " << print_money(fee) << ", minimum fee: " << print_money(needed_fee)); tvc.m_verifivation_failed = true; tvc.m_fee_too_low = true; return false; -- cgit v1.2.3