blockchain: fix timestamp/difficulty cache getting out of sync
The cache is discarded when a block is popped, but then gets rebuilt when the difficulty for next block is requested. While this is all properly locked, it does not take into account the delay caused by a database transaction being only committed (and thus its effects made visible to other threads) later on, which means another thread could request difficulty between the pop and the commit, which would end up using stale database view to build the cache, but that cache would not be invalidated again when the transaction gets committed, which would cause the cache to not match the new database data. To fix this, we now keep track of when the cache is invalidated so we can invalidate it again upon database transaction commit to ensure it gets calculated again with fresh data next time it is nedeed.
This commit is contained in:
parent
4be84b7753
commit
72655f78fc
|
@ -86,7 +86,7 @@ DISABLE_VS_WARNINGS(4267)
|
|||
|
||||
//------------------------------------------------------------------
|
||||
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_reset_timestamps_and_difficulties_height(true), 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),
|
||||
|
@ -427,6 +427,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
|
|||
if (num_popped_blocks > 0)
|
||||
{
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_reset_timestamps_and_difficulties_height = true;
|
||||
m_hardfork->reorganize_from_chain_height(get_current_blockchain_height());
|
||||
uint64_t top_block_height;
|
||||
crypto::hash top_block_hash = get_tail_id(top_block_height);
|
||||
|
@ -567,6 +568,7 @@ block Blockchain::pop_block_from_blockchain()
|
|||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_reset_timestamps_and_difficulties_height = true;
|
||||
|
||||
block popped_block;
|
||||
std::vector<transaction> popped_txs;
|
||||
|
@ -644,6 +646,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
|
|||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_reset_timestamps_and_difficulties_height = true;
|
||||
invalidate_block_template_cache();
|
||||
m_db->reset();
|
||||
m_db->drop_alt_blocks();
|
||||
|
@ -860,6 +863,8 @@ start:
|
|||
// pop the oldest one from the list. This only requires 1x read per height instead
|
||||
// of doing 735 (DIFFICULTY_BLOCKS_COUNT).
|
||||
bool check = false;
|
||||
if (m_reset_timestamps_and_difficulties_height)
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= difficulty_blocks_count)
|
||||
{
|
||||
uint64_t index = height - 1;
|
||||
|
@ -1006,6 +1011,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
|
|||
}
|
||||
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_reset_timestamps_and_difficulties_height = true;
|
||||
|
||||
// remove blocks from blockchain until we get back to where we should be.
|
||||
while (m_db->height() != rollback_height)
|
||||
|
@ -1042,6 +1048,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
|
|||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_reset_timestamps_and_difficulties_height = true;
|
||||
|
||||
// if empty alt chain passed (not sure how that could happen), return false
|
||||
CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
|
||||
|
@ -1755,6 +1762,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_reset_timestamps_and_difficulties_height = true;
|
||||
uint64_t block_height = get_block_height(b);
|
||||
if(0 == block_height)
|
||||
{
|
||||
|
@ -4454,7 +4462,14 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
|
|||
try
|
||||
{
|
||||
if (m_batch_success)
|
||||
{
|
||||
m_db->batch_stop();
|
||||
if (m_reset_timestamps_and_difficulties_height)
|
||||
{
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
m_reset_timestamps_and_difficulties_height = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_db->batch_abort();
|
||||
success = true;
|
||||
|
|
|
@ -1067,6 +1067,7 @@ namespace cryptonote
|
|||
std::vector<uint64_t> m_timestamps;
|
||||
std::vector<difficulty_type> m_difficulties;
|
||||
uint64_t m_timestamps_and_difficulties_height;
|
||||
bool m_reset_timestamps_and_difficulties_height;
|
||||
uint64_t m_long_term_block_weights_window;
|
||||
uint64_t m_long_term_effective_median_block_weight;
|
||||
mutable crypto::hash m_long_term_block_weights_cache_tip_hash;
|
||||
|
|
Loading…
Reference in New Issue