blockchain: detect and log bad difficulty calculations
This commit is contained in:
parent
309211fd6a
commit
bb4d95c98f
|
@ -812,12 +812,20 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph
|
||||||
// less blocks than desired if there aren't enough.
|
// less blocks than desired if there aren't enough.
|
||||||
difficulty_type Blockchain::get_difficulty_for_next_block()
|
difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||||
{
|
{
|
||||||
|
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
bool print = false;
|
||||||
|
|
||||||
|
int done = 0;
|
||||||
|
ss << "get_difficulty_for_next_block: height " << m_db->height() << std::endl;
|
||||||
if (m_fixed_difficulty)
|
if (m_fixed_difficulty)
|
||||||
{
|
{
|
||||||
return m_db->height() ? m_fixed_difficulty : 1;
|
return m_db->height() ? m_fixed_difficulty : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
start:
|
||||||
|
difficulty_type D = 0;
|
||||||
|
|
||||||
crypto::hash top_hash = get_tail_id();
|
crypto::hash top_hash = get_tail_id();
|
||||||
{
|
{
|
||||||
|
@ -826,21 +834,30 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||||
// something a bit out of date, but that's fine since anything which
|
// something a bit out of date, but that's fine since anything which
|
||||||
// requires the blockchain lock will have acquired it in the first place,
|
// requires the blockchain lock will have acquired it in the first place,
|
||||||
// and it will be unlocked only when called from the getinfo RPC
|
// and it will be unlocked only when called from the getinfo RPC
|
||||||
|
ss << "Locked, tail id " << top_hash << ", cached is " << m_difficulty_for_next_block_top_hash << std::endl;
|
||||||
if (top_hash == m_difficulty_for_next_block_top_hash)
|
if (top_hash == m_difficulty_for_next_block_top_hash)
|
||||||
return m_difficulty_for_next_block;
|
{
|
||||||
|
ss << "Same, using cached diff " << m_difficulty_for_next_block << std::endl;
|
||||||
|
D = m_difficulty_for_next_block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||||
std::vector<uint64_t> timestamps;
|
std::vector<uint64_t> timestamps;
|
||||||
std::vector<difficulty_type> difficulties;
|
std::vector<difficulty_type> difficulties;
|
||||||
uint64_t height;
|
uint64_t height;
|
||||||
top_hash = get_tail_id(height); // get it again now that we have the lock
|
auto new_top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||||
++height; // top block height to blockchain height
|
++height;
|
||||||
|
if (!(new_top_hash == top_hash)) D=0;
|
||||||
|
ss << "Re-locked, height " << height << ", tail id " << new_top_hash << (new_top_hash == top_hash ? "" : " (different)") << std::endl;
|
||||||
|
top_hash = new_top_hash;
|
||||||
|
|
||||||
// ND: Speedup
|
// ND: Speedup
|
||||||
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
|
// 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty,
|
||||||
// then when the next block difficulty is queried, push the latest height data and
|
// then when the next block difficulty is queried, push the latest height data and
|
||||||
// pop the oldest one from the list. This only requires 1x read per height instead
|
// pop the oldest one from the list. This only requires 1x read per height instead
|
||||||
// of doing 735 (DIFFICULTY_BLOCKS_COUNT).
|
// of doing 735 (DIFFICULTY_BLOCKS_COUNT).
|
||||||
|
bool check = false;
|
||||||
if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT)
|
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;
|
uint64_t index = height - 1;
|
||||||
|
@ -855,8 +872,12 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||||
m_timestamps_and_difficulties_height = height;
|
m_timestamps_and_difficulties_height = height;
|
||||||
timestamps = m_timestamps;
|
timestamps = m_timestamps;
|
||||||
difficulties = m_difficulties;
|
difficulties = m_difficulties;
|
||||||
|
check = true;
|
||||||
}
|
}
|
||||||
else
|
//else
|
||||||
|
std::vector<uint64_t> timestamps_from_cache = timestamps;
|
||||||
|
std::vector<difficulty_type> difficulties_from_cache = difficulties;
|
||||||
|
|
||||||
{
|
{
|
||||||
uint64_t offset = height - std::min <uint64_t> (height, static_cast<uint64_t>(DIFFICULTY_BLOCKS_COUNT));
|
uint64_t offset = height - std::min <uint64_t> (height, static_cast<uint64_t>(DIFFICULTY_BLOCKS_COUNT));
|
||||||
if (offset == 0)
|
if (offset == 0)
|
||||||
|
@ -869,22 +890,68 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
||||||
timestamps.reserve(height - offset);
|
timestamps.reserve(height - offset);
|
||||||
difficulties.reserve(height - offset);
|
difficulties.reserve(height - offset);
|
||||||
}
|
}
|
||||||
|
ss << "Looking up " << (height - offset) << " from " << offset << std::endl;
|
||||||
for (; offset < height; offset++)
|
for (; offset < height; offset++)
|
||||||
{
|
{
|
||||||
timestamps.push_back(m_db->get_block_timestamp(offset));
|
timestamps.push_back(m_db->get_block_timestamp(offset));
|
||||||
difficulties.push_back(m_db->get_block_cumulative_difficulty(offset));
|
difficulties.push_back(m_db->get_block_cumulative_difficulty(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (check) if (timestamps != timestamps_from_cache || difficulties !=difficulties_from_cache)
|
||||||
|
{
|
||||||
|
ss << "Inconsistency XXX:" << std::endl;
|
||||||
|
ss << "top hash: "<<top_hash << std::endl;
|
||||||
|
ss << "timestamps: " << timestamps_from_cache.size() << " from cache, but " << timestamps.size() << " without" << std::endl;
|
||||||
|
ss << "difficulties: " << difficulties_from_cache.size() << " from cache, but " << difficulties.size() << " without" << std::endl;
|
||||||
|
ss << "timestamps_from_cache:" << std::endl; for (const auto &v :timestamps_from_cache) ss << " " << v << std::endl;
|
||||||
|
ss << "timestamps:" << std::endl; for (const auto &v :timestamps) ss << " " << v << std::endl;
|
||||||
|
ss << "difficulties_from_cache:" << std::endl; for (const auto &v :difficulties_from_cache) ss << " " << v << std::endl;
|
||||||
|
ss << "difficulties:" << std::endl; for (const auto &v :difficulties) ss << " " << v << std::endl;
|
||||||
|
|
||||||
|
uint64_t dbh = m_db->height();
|
||||||
|
uint64_t sh = dbh < 10000 ? 0 : dbh - 10000;
|
||||||
|
ss << "History from -10k at :" << dbh << ", from " << sh << std::endl;
|
||||||
|
for (uint64_t h = sh; h < dbh; ++h)
|
||||||
|
{
|
||||||
|
uint64_t ts = m_db->get_block_timestamp(h);
|
||||||
|
difficulty_type d = m_db->get_block_cumulative_difficulty(h);
|
||||||
|
ss << " " << h << " " << ts << " " << d << std::endl;
|
||||||
|
}
|
||||||
|
print = true;
|
||||||
|
}
|
||||||
m_timestamps_and_difficulties_height = height;
|
m_timestamps_and_difficulties_height = height;
|
||||||
m_timestamps = timestamps;
|
m_timestamps = timestamps;
|
||||||
m_difficulties = difficulties;
|
m_difficulties = difficulties;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t target = get_difficulty_target();
|
size_t target = get_difficulty_target();
|
||||||
difficulty_type diff = next_difficulty(timestamps, difficulties, target);
|
difficulty_type diff = next_difficulty(timestamps, difficulties, target);
|
||||||
|
|
||||||
CRITICAL_REGION_LOCAL1(m_difficulty_lock);
|
CRITICAL_REGION_LOCAL1(m_difficulty_lock);
|
||||||
m_difficulty_for_next_block_top_hash = top_hash;
|
m_difficulty_for_next_block_top_hash = top_hash;
|
||||||
m_difficulty_for_next_block = diff;
|
m_difficulty_for_next_block = diff;
|
||||||
|
if (D && D != diff)
|
||||||
|
{
|
||||||
|
ss << "XXX Mismatch at " << height << "/" << top_hash << "/" << get_tail_id() << ": cached " << D << ", real " << diff << std::endl;
|
||||||
|
print = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
++done;
|
||||||
|
if (done == 1 && D && D != diff)
|
||||||
|
{
|
||||||
|
print = true;
|
||||||
|
ss << "Might be a race. Let's see what happens if we try again..." << std::endl;
|
||||||
|
epee::misc_utils::sleep_no_w(100);
|
||||||
|
goto start;
|
||||||
|
}
|
||||||
|
ss << "Diff for " << top_hash << ": " << diff << std::endl;
|
||||||
|
if (print)
|
||||||
|
{
|
||||||
|
MGINFO("START DUMP");
|
||||||
|
MGINFO(ss.str());
|
||||||
|
MGINFO("END DUMP");
|
||||||
|
MGINFO("Please send moneromooo on Freenode the contents of this log, from a couple dozen lines before START DUMP to END DUMP");
|
||||||
|
}
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue