cryptonote_core: warn when the block rate deviates from expectations

The warning threshold is set to allow a false positive every
ten days on average.
This commit is contained in:
moneromooo-monero 2018-09-20 13:31:45 +00:00
parent bad2c7cf31
commit 341b3931ed
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
4 changed files with 69 additions and 0 deletions

View file

@ -880,6 +880,15 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
return diff; return diff;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const
{
std::vector<time_t> timestamps(blocks);
uint64_t height = m_db->height();
while (blocks--)
timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1);
return timestamps;
}
//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the // This function removes blocks from the blockchain until it gets to the
// position where the blockchain switch started and then re-adds the blocks // position where the blockchain switch started and then re-adds the blocks
// that had been removed. // that had been removed.

View file

@ -943,6 +943,11 @@ namespace cryptonote
*/ */
void on_new_tx_from_block(const cryptonote::transaction &tx); void on_new_tx_from_block(const cryptonote::transaction &tx);
/**
* @brief returns the timestamps of the last N blocks
*/
std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const;
private: private:
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage

View file

@ -1471,6 +1471,7 @@ namespace cryptonote
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this));
m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this));
m_miner.on_idle(); m_miner.on_idle();
m_mempool.on_idle(); m_mempool.on_idle();
return true; return true;
@ -1655,6 +1656,52 @@ namespace cryptonote
return true; return true;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
double factorial(unsigned int n)
{
if (n <= 1)
return 1.0;
double f = n;
while (n-- > 1)
f *= n;
return f;
}
//-----------------------------------------------------------------------------------------------
static double probability(unsigned int blocks, unsigned int expected)
{
// https://www.umass.edu/wsp/resources/poisson/#computing
return pow(expected, blocks) / (factorial(blocks) * exp(expected));
}
//-----------------------------------------------------------------------------------------------
bool core::check_block_rate()
{
if (m_offline || m_target_blockchain_height > get_current_blockchain_height())
{
MDEBUG("Not checking block rate, offline or syncing");
return true;
}
static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days
const time_t now = time(NULL);
const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60);
static const unsigned int seconds[] = { 5400, 1800, 600 };
for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n)
{
unsigned int b = 0;
for (time_t ts: timestamps) b += ts >= now - seconds[n];
const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2);
MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")");
if (p < threshold)
{
MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
break; // no need to look further
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
void core::set_target_blockchain_height(uint64_t target_blockchain_height) void core::set_target_blockchain_height(uint64_t target_blockchain_height)
{ {
m_target_blockchain_height = target_blockchain_height; m_target_blockchain_height = target_blockchain_height;

View file

@ -935,6 +935,13 @@ namespace cryptonote
*/ */
bool check_disk_space(); bool check_disk_space();
/**
* @brief checks block rate, and warns if it's too slow
*
* @return true on success, false otherwise
*/
bool check_block_rate();
bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
@ -959,6 +966,7 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space
epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown? std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?