blockchain: detect and log bad difficulty calculations
This commit is contained in:
parent
77a008f714
commit
5741b4d74d
|
@ -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.
|
||||
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)
|
||||
{
|
||||
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();
|
||||
{
|
||||
|
@ -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
|
||||
// requires the blockchain lock will have acquired it in the first place,
|
||||
// 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)
|
||||
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);
|
||||
std::vector<uint64_t> timestamps;
|
||||
std::vector<difficulty_type> difficulties;
|
||||
uint64_t height;
|
||||
top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||
++height; // top block height to blockchain height
|
||||
auto new_top_hash = get_tail_id(height); // get it again now that we have the lock
|
||||
++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
|
||||
// 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
|
||||
// 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_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT)
|
||||
{
|
||||
uint64_t index = height - 1;
|
||||
|
@ -855,8 +872,12 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
|||
m_timestamps_and_difficulties_height = height;
|
||||
timestamps = m_timestamps;
|
||||
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));
|
||||
if (offset == 0)
|
||||
|
@ -869,22 +890,68 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
|
|||
timestamps.reserve(height - offset);
|
||||
difficulties.reserve(height - offset);
|
||||
}
|
||||
ss << "Looking up " << (height - offset) << " from " << offset << std::endl;
|
||||
for (; offset < height; offset++)
|
||||
{
|
||||
timestamps.push_back(m_db->get_block_timestamp(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 = timestamps;
|
||||
m_difficulties = difficulties;
|
||||
}
|
||||
|
||||
size_t target = get_difficulty_target();
|
||||
difficulty_type diff = next_difficulty(timestamps, difficulties, target);
|
||||
|
||||
CRITICAL_REGION_LOCAL1(m_difficulty_lock);
|
||||
m_difficulty_for_next_block_top_hash = top_hash;
|
||||
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;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue