blockchain: remove buggy long term block weight cache

It seems to be buggy on reorgs, and prevents the use of
a blockchain with two nodes.
We'll speed this up again if/when the need arises.
This commit is contained in:
moneromooo-monero 2019-02-19 11:00:27 +00:00 committed by wowario
parent 0622741c03
commit b117840c81
No known key found for this signature in database
GPG Key ID: 24DCBE762DE9C111
3 changed files with 39 additions and 79 deletions

View File

@ -167,8 +167,8 @@ static const struct {
Blockchain::Blockchain(tx_memory_pool& tx_pool) : 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_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_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(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
m_long_term_block_weights_height(0), m_long_term_effective_median_block_weight(0),
m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1), m_difficulty_for_next_block(1),
m_btc_valid(false) m_btc_valid(false)
@ -495,17 +495,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
} }
if (test_options && test_options->long_term_block_weight_window) if (test_options && test_options->long_term_block_weight_window)
m_long_term_block_weights.set_capacity(test_options->long_term_block_weight_window); m_long_term_block_weights_window = test_options->long_term_block_weight_window;
// fill up the long term block weights to reach the required amount
const uint64_t db_height = m_db->height();
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights.capacity(), db_height);
while (m_long_term_block_weights.size() < nblocks)
{
uint64_t weight = m_db->get_block_long_term_weight(db_height - 1 - m_long_term_block_weights.size());
m_long_term_block_weights.push_front(weight);
}
m_long_term_block_weights_height = db_height;
if (!update_next_cumulative_weight_limit()) if (!update_next_cumulative_weight_limit())
return false; return false;
@ -625,8 +615,6 @@ block Blockchain::pop_block_from_blockchain()
// make sure the hard fork object updates its current version // make sure the hard fork object updates its current version
m_hardfork->on_block_popped(1); m_hardfork->on_block_popped(1);
pop_from_long_term_block_weights();
// return transactions from popped block to the tx_pool // return transactions from popped block to the tx_pool
for (transaction& tx : popped_txs) for (transaction& tx : popped_txs)
{ {
@ -672,8 +660,6 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
m_timestamps_and_difficulties_height = 0; m_timestamps_and_difficulties_height = 0;
m_alternative_chains.clear(); m_alternative_chains.clear();
invalidate_block_template_cache(); invalidate_block_template_cache();
m_long_term_block_weights.clear();
m_long_term_block_weights_height = 0;
m_db->reset(); m_db->reset();
m_hardfork->init(); m_hardfork->init();
@ -2994,6 +2980,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 bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
{ {
const uint8_t version = get_current_hard_fork_version(); const uint8_t version = get_current_hard_fork_version();
const uint64_t blockchain_height = m_db->height();
uint64_t median = 0; uint64_t median = 0;
uint64_t already_generated_coins = 0; uint64_t already_generated_coins = 0;
@ -3001,7 +2988,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
if (version >= HF_VERSION_DYNAMIC_FEE) if (version >= HF_VERSION_DYNAMIC_FEE)
{ {
median = m_current_block_cumul_weight_limit / 2; 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)) if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false; return false;
} }
@ -3010,7 +2997,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
if (version >= HF_VERSION_PER_BYTE_FEE) if (version >= HF_VERSION_PER_BYTE_FEE)
{ {
const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; 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_block_weights.back() : median, version); 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"); MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee");
needed_fee = tx_weight * fee_per_byte; needed_fee = tx_weight * fee_per_byte;
// quantize fee up to 8 decimals // quantize fee up to 8 decimals
@ -3047,6 +3034,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 uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{ {
const uint8_t version = get_current_hard_fork_version(); const uint8_t version = get_current_hard_fork_version();
const uint64_t db_height = m_db->height();
if (version < HF_VERSION_DYNAMIC_FEE) if (version < HF_VERSION_DYNAMIC_FEE)
return FEE_PER_KB; return FEE_PER_KB;
@ -3065,7 +3053,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
if(median <= min_block_weight) if(median <= min_block_weight)
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; uint64_t base_reward;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
{ {
@ -3074,7 +3062,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
} }
const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; 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_block_weights.back() : median, version); 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; const bool per_byte = version < HF_VERSION_PER_BYTE_FEE;
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB")); MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB"));
return fee; return fee;
@ -3630,31 +3618,21 @@ leave:
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
void Blockchain::pop_from_long_term_block_weights()
{
m_long_term_block_weights.pop_back();
--m_long_term_block_weights_height;
if (m_long_term_block_weights_height + 1 > m_long_term_block_weights.capacity())
{
uint64_t block_height = m_long_term_block_weights_height - m_long_term_block_weights.capacity() + 1;
uint64_t weight = m_db->get_block_long_term_weight(block_height);
m_long_term_block_weights.push_front(weight);
}
}
//------------------------------------------------------------------
uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) const uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) const
{ {
PERF_TIMER(get_next_long_term_block_weight); PERF_TIMER(get_next_long_term_block_weight);
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights.capacity(), m_db->height()); const uint64_t db_height = m_db->height();
CHECK_AND_ASSERT_MES(m_long_term_block_weights.size() == nblocks, 0, const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
"m_long_term_block_weights is not the expected size");
const uint8_t hf_version = get_current_hard_fork_version(); const uint8_t hf_version = get_current_hard_fork_version();
if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
return block_weight; return block_weight;
std::vector<uint64_t> weights(m_long_term_block_weights.begin(), m_long_term_block_weights.end()); 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_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 long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
@ -3670,25 +3648,12 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
uint64_t local_long_term_effective_median_block_weight;
if (!long_term_effective_median_block_weight)
long_term_effective_median_block_weight = &local_long_term_effective_median_block_weight;
// when we reach this, the last hf version is not yet written to the db // when we reach this, the last hf version is not yet written to the db
const uint64_t db_height = m_db->height(); const uint64_t db_height = m_db->height();
const uint8_t hf_version = (m_long_term_block_weights_height + 1 < m_db->height()) ? m_db->get_hard_fork_version(db_height - 1) : get_current_hard_fork_version(); const uint8_t hf_version = get_current_hard_fork_version();
uint64_t full_reward_zone = get_min_block_weight(hf_version); uint64_t full_reward_zone = get_min_block_weight(hf_version);
uint64_t long_term_block_weight; uint64_t long_term_block_weight;
// recalc, or reorg ? pop the last one(s) and recompute
uint64_t pop = 0;
if (db_height && db_height <= m_long_term_block_weights_height)
{
pop = m_long_term_block_weights_height - db_height + 1;
while (pop--)
pop_from_long_term_block_weights();
}
if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
{ {
std::vector<size_t> weights; std::vector<size_t> weights;
@ -3700,40 +3665,42 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
{ {
const uint64_t block_weight = m_db->get_block_weight(db_height - 1); const uint64_t block_weight = m_db->get_block_weight(db_height - 1);
std::vector<uint64_t> weights(m_long_term_block_weights.begin(), m_long_term_block_weights.end()); std::vector<uint64_t> weights;
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
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);
std::vector<uint64_t> new_weights = weights;
uint64_t long_term_median = epee::misc_utils::median(weights); uint64_t long_term_median = epee::misc_utils::median(weights);
*long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); 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 = *long_term_effective_median_block_weight + *long_term_effective_median_block_weight * 2 / 5; 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); long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
weights = std::vector<uint64_t>(m_long_term_block_weights.begin(), m_long_term_block_weights.end()); if (new_weights.empty())
if (weights.empty()) new_weights.resize(1);
weights.resize(1); new_weights[0] = long_term_block_weight;
weights[0] = long_term_block_weight; long_term_median = epee::misc_utils::median(new_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);
*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;
short_term_constraint = *long_term_effective_median_block_weight + *long_term_effective_median_block_weight * 2 / 5;
weights.clear(); weights.clear();
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
uint64_t short_term_median = epee::misc_utils::median(weights); 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 * *long_term_effective_median_block_weight); 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; m_current_block_cumul_weight_median = effective_median_block_weight;
} }
m_long_term_block_weights.push_back(long_term_block_weight);
m_long_term_block_weights_height = db_height;
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights.capacity(), db_height);
CHECK_AND_ASSERT_MES(m_long_term_block_weights.size() == nblocks, false, "Bad m_long_term_block_weights size");
if (m_current_block_cumul_weight_median <= full_reward_zone) if (m_current_block_cumul_weight_median <= full_reward_zone)
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; 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;
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------

View File

@ -1027,8 +1027,8 @@ namespace cryptonote
std::vector<uint64_t> m_timestamps; std::vector<uint64_t> m_timestamps;
std::vector<difficulty_type> m_difficulties; std::vector<difficulty_type> m_difficulties;
uint64_t m_timestamps_and_difficulties_height; uint64_t m_timestamps_and_difficulties_height;
boost::circular_buffer<uint64_t> m_long_term_block_weights; uint64_t m_long_term_block_weights_window;
uint64_t m_long_term_block_weights_height; uint64_t m_long_term_effective_median_block_weight;
epee::critical_section m_difficulty_lock; epee::critical_section m_difficulty_lock;
crypto::hash m_difficulty_for_next_block_top_hash; crypto::hash m_difficulty_for_next_block_top_hash;
@ -1419,12 +1419,5 @@ namespace cryptonote
* At some point, may be used to push an update to miners * At some point, may be used to push an update to miners
*/ */
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie); void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie);
/**
* @brief pops an entry from long term block weights
*
* another is added at the other end if necessary
*/
void pop_from_long_term_block_weights();
}; };
} // namespace cryptonote } // namespace cryptonote

View File

@ -229,7 +229,7 @@ TEST(long_term_block_weight, pop_invariant_max)
for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h)
{ {
size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit();
uint64_t ltw = bc->get_next_long_term_block_weight(w); uint64_t ltw = bc->get_next_long_term_block_weight(w);
bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {});
ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
@ -277,7 +277,7 @@ TEST(long_term_block_weight, pop_invariant_random)
for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h)
{ {
size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit();
uint64_t ltw = bc->get_next_long_term_block_weight(w); uint64_t ltw = bc->get_next_long_term_block_weight(w);
bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {});
ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
@ -287,7 +287,7 @@ TEST(long_term_block_weight, pop_invariant_random)
{ {
// pop some blocks, then add some more // pop some blocks, then add some more
int remove = 1 + (n * 17) % 8; int remove = 1 + (n * 17) % 8;
int add = (n * 23) % 12; int add = (n * 23) % 123;
// save long term block weights we're about to remove // save long term block weights we're about to remove
uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1; uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1;