aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2019-03-04 12:09:12 +0200
committerRiccardo Spagni <ric@spagni.net>2019-03-04 12:09:13 +0200
commit55305559c1ce10ec5d5d5a1c5d31e5056b8a9dd6 (patch)
treefeb811a9f75c43e49e8dd3fa24492a8b96770976 /src/cryptonote_core
parentMerge pull request #5122 (diff)
parentArticMine's new block weight algorithm (diff)
downloadmonero-55305559c1ce10ec5d5d5a1c5d31e5056b8a9dd6.tar.xz
Merge pull request #5124
b8787f43 ArticMine's new block weight algorithm (moneromooo-monero)
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp139
-rw-r--r--src/cryptonote_core/blockchain.h18
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp3
-rw-r--r--src/cryptonote_core/cryptonote_core.h1
4 files changed, 137 insertions, 24 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 09b67a64b..42003921a 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -166,6 +166,8 @@ static const struct {
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
+ m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
+ m_long_term_effective_median_block_weight(0),
m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1),
m_btc_valid(false)
@@ -500,7 +502,11 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
}
- update_next_cumulative_weight_limit();
+ if (test_options && test_options->long_term_block_weight_window)
+ m_long_term_block_weights_window = test_options->long_term_block_weight_window;
+
+ if (!update_next_cumulative_weight_limit())
+ return false;
return true;
}
//------------------------------------------------------------------
@@ -685,7 +691,7 @@ block Blockchain::pop_block_from_blockchain()
m_blocks_txs_check.clear();
m_check_txin_table.clear();
- update_next_cumulative_weight_limit();
+ CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit");
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
invalidate_block_template_cache();
@@ -704,7 +710,8 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
block_verification_context bvc = boost::value_initialized<block_verification_context>();
add_new_block(b, bvc);
- update_next_cumulative_weight_limit();
+ if (!update_next_cumulative_weight_limit())
+ return false;
return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
}
//------------------------------------------------------------------
@@ -1202,7 +1209,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
}
}
- std::vector<size_t> last_blocks_weights;
+ std::vector<uint64_t> last_blocks_weights;
get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version))
{
@@ -1237,7 +1244,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
}
//------------------------------------------------------------------
// get the block weights of the last <count> blocks, and return by reference <sz>.
-void Blockchain::get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const
+void Blockchain::get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -3083,6 +3090,7 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b
bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
{
const uint8_t version = get_current_hard_fork_version();
+ const uint64_t blockchain_height = m_db->height();
uint64_t median = 0;
uint64_t already_generated_coins = 0;
@@ -3090,7 +3098,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
if (version >= HF_VERSION_DYNAMIC_FEE)
{
median = m_current_block_cumul_weight_limit / 2;
- already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
+ already_generated_coins = blockchain_height ? m_db->get_block_already_generated_coins(blockchain_height - 1) : 0;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false;
}
@@ -3098,7 +3106,8 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
uint64_t needed_fee;
if (version >= HF_VERSION_PER_BYTE_FEE)
{
- uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, median, version);
+ const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT;
+ uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version);
MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee");
needed_fee = tx_weight * fee_per_byte;
// quantize fee up to 8 decimals
@@ -3135,6 +3144,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{
const uint8_t version = get_current_hard_fork_version();
+ const uint64_t db_height = m_db->height();
if (version < HF_VERSION_DYNAMIC_FEE)
return FEE_PER_KB;
@@ -3143,7 +3153,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
const uint64_t min_block_weight = get_min_block_weight(version);
- std::vector<size_t> weights;
+ std::vector<uint64_t> weights;
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
weights.reserve(grace_blocks);
for (size_t i = 0; i < grace_blocks; ++i)
@@ -3153,7 +3163,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
if(median <= min_block_weight)
median = min_block_weight;
- uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
+ 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(median, 1, already_generated_coins, base_reward, version))
{
@@ -3161,7 +3171,8 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
base_reward = BLOCK_REWARD_OVERESTIMATE;
}
- uint64_t fee = get_dynamic_base_fee(base_reward, median, version);
+ const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT;
+ uint64_t fee = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version);
const bool per_byte = version < HF_VERSION_PER_BYTE_FEE;
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB"));
return fee;
@@ -3658,7 +3669,8 @@ leave:
{
try
{
- new_height = m_db->add_block(bl, block_weight, cumulative_difficulty, already_generated_coins, txs);
+ uint64_t long_term_block_weight = get_next_long_term_block_weight(block_weight);
+ new_height = m_db->add_block(bl, block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs);
}
catch (const KEY_IMAGE_EXISTS& e)
{
@@ -3684,7 +3696,12 @@ leave:
TIME_MEASURE_FINISH(addblock);
// do this after updating the hard fork state since the weight limit may change due to fork
- update_next_cumulative_weight_limit();
+ if (!update_next_cumulative_weight_limit())
+ {
+ MERROR("Failed to update next cumulative weight limit");
+ pop_block_from_blockchain();
+ return false;
+ }
MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms");
if(m_show_time_stats)
@@ -3740,20 +3757,100 @@ bool Blockchain::check_blockchain_pruning()
return m_db->check_pruning();
}
//------------------------------------------------------------------
-bool Blockchain::update_next_cumulative_weight_limit()
+uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) const
{
- uint64_t full_reward_zone = get_min_block_weight(get_current_hard_fork_version());
+ PERF_TIMER(get_next_long_term_block_weight);
+
+ const uint64_t db_height = m_db->height();
+ const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
+
+ const uint8_t hf_version = get_current_hard_fork_version();
+ if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
+ return block_weight;
+
+ std::vector<uint64_t> weights;
+ weights.resize(nblocks);
+ for (uint64_t h = 0; h < nblocks; ++h)
+ weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h);
+ uint64_t long_term_median = epee::misc_utils::median(weights);
+ 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 long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
+
+ return long_term_block_weight;
+}
+//------------------------------------------------------------------
+bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight)
+{
+ PERF_TIMER(update_next_cumulative_weight_limit);
LOG_PRINT_L3("Blockchain::" << __func__);
- std::vector<size_t> weights;
- get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
- uint64_t median = epee::misc_utils::median(weights);
- m_current_block_cumul_weight_median = median;
- if(median <= full_reward_zone)
- median = full_reward_zone;
+ // when we reach this, the last hf version is not yet written to the db
+ const uint64_t db_height = m_db->height();
+ const uint8_t hf_version = get_current_hard_fork_version();
+ uint64_t full_reward_zone = get_min_block_weight(hf_version);
+ uint64_t long_term_block_weight;
+
+ if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
+ {
+ std::vector<uint64_t> weights;
+ get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
+ m_current_block_cumul_weight_median = epee::misc_utils::median(weights);
+ long_term_block_weight = weights.back();
+ }
+ else
+ {
+ const uint64_t block_weight = m_db->get_block_weight(db_height - 1);
+
+ std::vector<uint64_t> weights, new_weights;
+ uint64_t long_term_median;
+ if (db_height == 1)
+ {
+ long_term_median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5;
+ }
+ else
+ {
+ uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
+ if (nblocks == db_height)
+ --nblocks;
+ weights.resize(nblocks);
+ for (uint64_t h = 0; h < nblocks; ++h)
+ weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h - 1);
+ new_weights = weights;
+ long_term_median = epee::misc_utils::median(weights);
+ }
+
+ 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;
+ long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
+
+ if (new_weights.empty())
+ new_weights.resize(1);
+ new_weights[0] = long_term_block_weight;
+ long_term_median = epee::misc_utils::median(new_weights);
+ m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
+ short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5;
+
+ weights.clear();
+ 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);
+
+ m_current_block_cumul_weight_median = effective_median_block_weight;
+ }
+
+ if (m_current_block_cumul_weight_median <= full_reward_zone)
+ m_current_block_cumul_weight_median = full_reward_zone;
+
+ m_current_block_cumul_weight_limit = m_current_block_cumul_weight_median * 2;
+
+ if (long_term_effective_median_block_weight)
+ *long_term_effective_median_block_weight = m_long_term_effective_median_block_weight;
- m_current_block_cumul_weight_limit = median*2;
return true;
}
//------------------------------------------------------------------
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index c742bec91..92aef1278 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -37,6 +37,7 @@
#include <boost/multi_index/global_fun.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
+#include <boost/circular_buffer.hpp>
#include <atomic>
#include <functional>
#include <unordered_map>
@@ -631,6 +632,13 @@ namespace cryptonote
uint64_t get_current_cumulative_block_weight_limit() const;
/**
+ * @brief gets the long term block weight for a new block
+ *
+ * @return the long term block weight
+ */
+ uint64_t get_next_long_term_block_weight(uint64_t block_weight) const;
+
+ /**
* @brief gets the block weight median based on recent blocks (same window as for the limit)
*
* @return the median
@@ -994,7 +1002,9 @@ namespace cryptonote
*/
void pop_blocks(uint64_t nblocks);
+#ifndef IN_UNIT_TESTS
private:
+#endif
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
@@ -1047,6 +1057,8 @@ namespace cryptonote
std::vector<uint64_t> m_timestamps;
std::vector<difficulty_type> m_difficulties;
uint64_t m_timestamps_and_difficulties_height;
+ uint64_t m_long_term_block_weights_window;
+ uint64_t m_long_term_effective_median_block_weight;
epee::critical_section m_difficulty_lock;
crypto::hash m_difficulty_for_next_block_top_hash;
@@ -1280,7 +1292,7 @@ namespace cryptonote
* @param sz return-by-reference the list of weights
* @param count the number of blocks to get weights for
*/
- void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const;
+ void get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const;
/**
* @brief checks if a transaction is unlocked (its outputs spendable)
@@ -1379,9 +1391,11 @@ namespace cryptonote
/**
* @brief calculate the block weight limit for the next block to be added
*
+ * @param long_term_effective_median_block_weight optionally return that value
+ *
* @return true
*/
- bool update_next_cumulative_weight_limit();
+ bool update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight = NULL);
void return_tx_to_pool(std::vector<transaction> &txs);
/**
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 1513bb750..bc01e2435 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -602,7 +602,8 @@ namespace cryptonote
const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)};
const cryptonote::test_options regtest_test_options = {
- regtest_hard_forks
+ regtest_hard_forks,
+ 0
};
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty, get_checkpoints);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 4810fc891..308924bb9 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -54,6 +54,7 @@ namespace cryptonote
{
struct test_options {
const std::pair<uint8_t, uint64_t> *hard_forks;
+ const size_t long_term_block_weight_window;
};
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir;