mirror of
https://github.com/monero-project/monero.git
synced 2025-08-09 18:12:20 -04:00
Merge pull request #6534
7bd66b01b
daemon: guard against rare 'difficulty drift' bug with checkpoints and recalculation (stoffu)
This commit is contained in:
commit
36d50d93f2
10 changed files with 280 additions and 48 deletions
|
@ -32,6 +32,7 @@
|
|||
#include <cstdio>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
|
@ -440,6 +441,15 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
|
|||
m_long_term_block_weights_cache_rolling_median = epee::misc_utils::rolling_median_t<uint64_t>(m_long_term_block_weights_window);
|
||||
}
|
||||
|
||||
bool difficulty_ok;
|
||||
uint64_t difficulty_recalc_height;
|
||||
std::tie(difficulty_ok, difficulty_recalc_height) = check_difficulty_checkpoints();
|
||||
if (!difficulty_ok)
|
||||
{
|
||||
MERROR("Difficulty drift detected!");
|
||||
recalculate_difficulties(difficulty_recalc_height);
|
||||
}
|
||||
|
||||
{
|
||||
db_txn_guard txn_guard(m_db, m_db->is_read_only());
|
||||
if (!update_next_cumulative_weight_limit())
|
||||
|
@ -960,6 +970,111 @@ start:
|
|||
return diff;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
std::pair<bool, uint64_t> Blockchain::check_difficulty_checkpoints() const
|
||||
{
|
||||
uint64_t res = 0;
|
||||
for (const std::pair<uint64_t, difficulty_type>& i : m_checkpoints.get_difficulty_points())
|
||||
{
|
||||
if (i.first >= m_db->height())
|
||||
break;
|
||||
if (m_db->get_block_cumulative_difficulty(i.first) != i.second)
|
||||
return {false, res};
|
||||
res = i.first;
|
||||
}
|
||||
return {true, res};
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_height_opt)
|
||||
{
|
||||
if (m_fixed_difficulty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
const uint64_t start_height = start_height_opt ? *start_height_opt : check_difficulty_checkpoints().second;
|
||||
const uint64_t top_height = m_db->height() - 1;
|
||||
MGINFO("Recalculating difficulties from height " << start_height << " to height " << top_height);
|
||||
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> difficulties;
|
||||
timestamps.reserve(DIFFICULTY_BLOCKS_COUNT + 1);
|
||||
difficulties.reserve(DIFFICULTY_BLOCKS_COUNT + 1);
|
||||
if (start_height > 1)
|
||||
{
|
||||
for (uint64_t i = 0; i < DIFFICULTY_BLOCKS_COUNT; ++i)
|
||||
{
|
||||
uint64_t height = start_height - 1 - i;
|
||||
if (height == 0)
|
||||
break;
|
||||
timestamps.insert(timestamps.begin(), m_db->get_block_timestamp(height));
|
||||
difficulties.insert(difficulties.begin(), m_db->get_block_cumulative_difficulty(height));
|
||||
}
|
||||
}
|
||||
difficulty_type last_cum_diff = start_height <= 1 ? start_height : difficulties.back();
|
||||
uint64_t drift_start_height = 0;
|
||||
std::vector<difficulty_type> new_cumulative_difficulties;
|
||||
for (uint64_t height = start_height; height <= top_height; ++height)
|
||||
{
|
||||
size_t target = get_ideal_hard_fork_version(height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
|
||||
difficulty_type recalculated_diff = next_difficulty(timestamps, difficulties, target);
|
||||
|
||||
boost::multiprecision::uint256_t recalculated_cum_diff_256 = boost::multiprecision::uint256_t(recalculated_diff) + last_cum_diff;
|
||||
CHECK_AND_ASSERT_THROW_MES(recalculated_cum_diff_256 <= std::numeric_limits<difficulty_type>::max(), "Difficulty overflow!");
|
||||
difficulty_type recalculated_cum_diff = recalculated_cum_diff_256.convert_to<difficulty_type>();
|
||||
|
||||
if (drift_start_height == 0)
|
||||
{
|
||||
difficulty_type existing_cum_diff = m_db->get_block_cumulative_difficulty(height);
|
||||
if (recalculated_cum_diff != existing_cum_diff)
|
||||
{
|
||||
drift_start_height = height;
|
||||
new_cumulative_difficulties.reserve(top_height + 1 - height);
|
||||
LOG_ERROR("Difficulty drift found at height:" << height << ", hash:" << m_db->get_block_hash_from_height(height) << ", existing:" << existing_cum_diff << ", recalculated:" << recalculated_cum_diff);
|
||||
}
|
||||
}
|
||||
if (drift_start_height > 0)
|
||||
{
|
||||
new_cumulative_difficulties.push_back(recalculated_cum_diff);
|
||||
if (height % 100000 == 0)
|
||||
LOG_ERROR(boost::format("%llu / %llu (%.1f%%)") % height % top_height % (100 * (height - drift_start_height) / float(top_height - drift_start_height)));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
timestamps.push_back(m_db->get_block_timestamp(height));
|
||||
difficulties.push_back(recalculated_cum_diff);
|
||||
}
|
||||
if (timestamps.size() > DIFFICULTY_BLOCKS_COUNT)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(timestamps.size() == DIFFICULTY_BLOCKS_COUNT + 1, "Wrong timestamps size: " << timestamps.size());
|
||||
timestamps.erase(timestamps.begin());
|
||||
difficulties.erase(difficulties.begin());
|
||||
}
|
||||
last_cum_diff = recalculated_cum_diff;
|
||||
}
|
||||
|
||||
if (drift_start_height > 0)
|
||||
{
|
||||
LOG_ERROR("Writing to the DB...");
|
||||
try
|
||||
{
|
||||
m_db->correct_block_cumulative_difficulties(drift_start_height, new_cumulative_difficulties);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("Error correcting cumulative difficulties from height " << drift_start_height << ", what = " << e.what());
|
||||
}
|
||||
LOG_ERROR("Corrected difficulties for " << new_cumulative_difficulties.size() << " blocks");
|
||||
// clear cache
|
||||
m_difficulty_for_next_block_top_hash = crypto::null_hash;
|
||||
m_timestamps_and_difficulties_height = 0;
|
||||
}
|
||||
|
||||
return new_cumulative_difficulties.size();
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue