diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index f695cb659..f513651ed 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1038,6 +1038,16 @@ public: */ virtual difficulty_type get_block_difficulty(const uint64_t& height) const = 0; + /** + * @brief correct blocks cumulative difficulties that were incorrectly calculated due to the 'difficulty drift' bug + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param start_height the height where the drift starts + * @param new_cumulative_difficulties new cumulative difficulties to be stored + */ + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector& new_cumulative_difficulties) = 0; + /** * @brief fetch a block's already generated coins * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index dc29df8ea..6f47a8118 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -2779,6 +2779,44 @@ difficulty_type BlockchainLMDB::get_block_difficulty(const uint64_t& height) con return diff1 - diff2; } +void BlockchainLMDB::correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector& new_cumulative_difficulties) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + int result = 0; + block_wtxn_start(); + CURSOR(block_info) + + const uint64_t bc_height = height(); + if (start_height + new_cumulative_difficulties.size() != bc_height) + { + block_wtxn_abort(); + throw0(DB_ERROR("Incorrect new_cumulative_difficulties size")); + } + + for (uint64_t height = start_height; height < bc_height; ++height) + { + MDB_val_set(key, height); + result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &key, MDB_GET_BOTH); + if (result) + throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str())); + + mdb_block_info bi = *(mdb_block_info*)key.mv_data; + const difficulty_type d = new_cumulative_difficulties[height - start_height]; + bi.bi_diff_hi = ((d >> 64) & 0xffffffffffffffff).convert_to(); + bi.bi_diff_lo = (d & 0xffffffffffffffff).convert_to(); + + MDB_val_set(key2, height); + MDB_val_set(val, bi); + result = mdb_cursor_put(m_cur_block_info, &key2, &val, MDB_CURRENT); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to overwrite block info to db transaction: ", result).c_str())); + } + block_wtxn_stop(); +} + uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 2dd4e7107..5abb8014f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -229,6 +229,8 @@ public: virtual difficulty_type get_block_difficulty(const uint64_t& height) const; + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector& new_cumulative_difficulties); + virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const; virtual uint64_t get_block_long_term_weight(const uint64_t& height) const; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 24324bce3..92911d081 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -82,6 +82,7 @@ public: virtual std::vector get_block_weights(uint64_t start_height, size_t count) const override { return {}; } virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const override { return 10; } virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const override { return 0; } + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector& new_cumulative_difficulties) override {} virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const override { return 10000000000; } virtual uint64_t get_block_long_term_weight(const uint64_t& height) const override { return 128; } virtual std::vector get_long_term_block_weights(uint64_t start_height, size_t count) const override { return {}; } diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index c0b787e77..32e711bb6 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -72,7 +72,7 @@ namespace cryptonote { } //--------------------------------------------------------------------------- - bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) + bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str, const std::string& difficulty_str) { crypto::hash h = crypto::null_hash; bool r = epee::string_tools::hex_to_pod(hash_str, h); @@ -84,6 +84,23 @@ namespace cryptonote CHECK_AND_ASSERT_MES(h == m_points[height], false, "Checkpoint at given height already exists, and hash for new checkpoint was different!"); } m_points[height] = h; + if (!difficulty_str.empty()) + { + try + { + difficulty_type difficulty(difficulty_str); + if (m_difficulty_points.count(height)) + { + CHECK_AND_ASSERT_MES(difficulty == m_difficulty_points[height], false, "Difficulty checkpoint at given height already exists, and difficulty for new checkpoint was different!"); + } + m_difficulty_points[height] = difficulty; + } + catch (...) + { + LOG_ERROR("Failed to parse difficulty checkpoint: " << difficulty_str); + return false; + } + } return true; } //--------------------------------------------------------------------------- @@ -143,6 +160,11 @@ namespace cryptonote { return m_points; } + //--------------------------------------------------------------------------- + const std::map& checkpoints::get_difficulty_points() const + { + return m_difficulty_points; + } bool checkpoints::check_for_conflicts(const checkpoints& other) const { @@ -166,67 +188,16 @@ namespace cryptonote { return true; } - ADD_CHECKPOINT(1, "97f4ce4d7879b3bea54dcec738cd2ebb7952b4e9bb9743262310cd5fec749340"); - ADD_CHECKPOINT(10, "305472c87ff86d8afb3ec42634828462b0ed3d929fc05fa1ae668c3bee04837a"); - ADD_CHECKPOINT(100, "a92b9deae26e19322041cbc2f850fa905748ae1e5bf69b35ca90b247c5cbfc04"); - ADD_CHECKPOINT(1000, "62921e13030b29264439cafaf8320cf8aa039ee6ba7ba29c72f11b50a079269a"); - ADD_CHECKPOINT(2000, "b3e1d73e3d0243239481aa76cb075cf2428556f5dc4f2e30428ea2ba36693e97"); - ADD_CHECKPOINT(3000, "83a6e1ab394e80b8442b7b70b0e4c3a9fa0143e0ca51a33e829537ef5dd1bf13"); - ADD_CHECKPOINT(4000, "7c70722d8cb8106b4bec67e1790614cc6e98db7afd0843b96cdff6960a0e0073"); - ADD_CHECKPOINT(5000, "331ee74008e174e5fd1956f64c52793961b321a1366f7c6f7d324e8265df34f6"); - ADD_CHECKPOINT(6969, "aa7b66e8c461065139b55c29538a39c33ceda93e587f84d490ed573d80511c87"); //Hard fork to v8 - ADD_CHECKPOINT(7000, "2711bd33b107f744ad8bf98c1acefa18658780079496bd2f3a36f2e20b261f8e"); - ADD_CHECKPOINT(7500, "5975967c4624f13f058acafe7adf9355e03e8e802eeadc84ccb22ea588bc0762"); - ADD_CHECKPOINT(7900, "d9bc18cb35feb6b26bc5a19bbdbf7c852d9cc02883acb5bbce2e87d8b2c86069"); - ADD_CHECKPOINT(10000, "bc5bfbf1b26c8f976d1d792ece4c6a7e93064bec62b72f1d5beae74c3f273b3b"); - ADD_CHECKPOINT(20000, "52cc7edcb49eb02f28a653b824089a726f4050eb210263ee6f4180d388a1e5cc"); - ADD_CHECKPOINT(30000, "d22fde5dd240ade16d3250eb0aa5d1c16dc7cb51c20484e05eb274911032b3fa"); - ADD_CHECKPOINT(40000, "aee0d642322542ba069cb1c58ab2acd3560f108d4682c3dc3cb15a54d442d91f"); - ADD_CHECKPOINT(50000, "5286ac2a0f39b3aefcba363cd71f2760bd1e0d763cbc81026ebdc3f80a86541f"); - ADD_CHECKPOINT(53666, "3f43f56f66ef0c43cf2fd14d0d28fa2aae0ef8f40716773511345750770f1255"); //Hard fork to v9 - ADD_CHECKPOINT(54500, "8ed3078b389c2b44add007803d741b58d3fbed2e1ba4139bda702152d8773c9b"); - ADD_CHECKPOINT(55000, "4b662ceccefc3247edb4d654dd610b8fb496e85b88a5de43cc2bdd28171b15ff"); - ADD_CHECKPOINT(57000, "08a79f09f12bb5d230b63963356a760d51618e526cfc636047a6f3798217c177"); - ADD_CHECKPOINT(59000, "180b51ee2c5fbcd4362eb7a29df9422481310dd77d10bccdf8930724c31e007e"); - ADD_CHECKPOINT(59900, "18cc0653ef39cb304c68045dba5eb6b885f936281cd939dea04d0e6c9cd4ae2e"); - ADD_CHECKPOINT(60000, "0f02aa57a63f79f63dafed9063abe228a37cb19f00430dc3168b8a8f4ae8016c"); - ADD_CHECKPOINT(61000, "509aca8c54eb5fe44623768757b6e890ae39d512478c75f614cbff3d91809350"); - ADD_CHECKPOINT(62000, "7fe91ad256c08dbd961e04738968be22fb481093fbfa7959bde7796ccceba0e2"); - ADD_CHECKPOINT(62150, "1a7c75f8ebeda0e20eb5877181eafd7db0fc887e3fed43e0b27ab2e7bccafd10"); - ADD_CHECKPOINT(62269, "4969555d60742afb93925fd96d83ac28f45e6e3c0e583c9fb3c92d9b2100d38f"); - ADD_CHECKPOINT(62405, "4d0ae890cf9f875f231c7069508ad28dc429d14814b52db114dfab7519a27584"); - ADD_CHECKPOINT(62419, "bd8bf5ac4c4fb07ab4d0d492bd1699def5c095ab6943ad3b63a89d1d8b1ce748"); - ADD_CHECKPOINT(62425, "41a922dba6f3906871b2ccaf31ec9c91033470c503959093dae796deda8940ea"); - ADD_CHECKPOINT(62479, "a2e8ff4205ba2980eb70921b0b21b5fc656ee273664ea94b860c68ca069b60dd"); - ADD_CHECKPOINT(62503, "25fa115962988b4b8f8cfd22744a3e653b22ead8c8468e64caf334fc75a97d08"); - ADD_CHECKPOINT(62550, "bde522a8a81c392c98c979434aa1dd9d20b4ca52230ba6ae0362872757808a48"); - ADD_CHECKPOINT(62629, "8368e1ce1d421f1fc969364558433e2b2363d0ffcb5f2d946633095e3e6734f5"); - ADD_CHECKPOINT(62720, "f871cddd75951e2fe24c282d2bd28396fc922ea519b354ace992a0162cb333ff"); - ADD_CHECKPOINT(62733, "8331dbeeaf23173d2235a062373a437befadb6492cceb7640127bf18653a9e61"); - ADD_CHECKPOINT(62877, "62d44adc05d7d4fd9d15239c5575612207beab0bcf2da49158bf89e365441ca1"); - ADD_CHECKPOINT(63469, "4e33a9343fc5b86661ec0affaeb5b5a065290602c02d817337e4a979fe5747d8"); //Hard fork to v10 - ADD_CHECKPOINT(69800, "5c65428a664738bc083d1ccd6a1b5ff4305f98e7633f44033816801429b33ce1"); - ADD_CHECKPOINT(75000, "e93492f79b5344e7edb31537ee65b3e908bf71110cff8188c0c62fefc015d342"); - ADD_CHECKPOINT(79500, "9bbfd6f2257ce9084de30179944b7695c9b918c9c03a8a63306ab6c5828ff857"); - ADD_CHECKPOINT(80920, "8fca818344f97ea3912557cbd8be659cf6a5bc1203514c27338e234251d72dfb"); - ADD_CHECKPOINT(81769, "41db9fef8d0ccfa78b570ee9525d4f55de77b510c3ae4b08a1d51b9aec9ade1d"); //Hard fork to v11 - ADD_CHECKPOINT(82069, "fdea800d23d0b2eea19dec8af31e453e883e8315c97e25c8bb3e88ca164f8369"); //Hard fork to v12 - ADD_CHECKPOINT(85000, "31d62ab75470b15aedee6674b78767b53f10951786e991c26035743c267b247a"); - ADD_CHECKPOINT(87000, "a788e5a7233ca2198ad6446ddc454b05d578e72253ed2bbca969527230f6eec2"); - ADD_CHECKPOINT(88200, "50bb43d5d563524d6b9f308a2483b80934bab2ab5250757558318834476f1cfb"); - ADD_CHECKPOINT(100000, "0c1f3bec32fe4ac9bd4b6ce1f4dfc52824f0947d756c9a1a453252c9423071f5"); - ADD_CHECKPOINT(111450, "d6eadc95607765b36afd8b9148eac20eb101632021348cd34371fc1d8b67f6b6"); - ADD_CHECKPOINT(114969, "b48245956b87f243048fd61021f4b3e5443e57eee7ff8ba4762d18926e80b80c"); //Hard fork to v13 - ADD_CHECKPOINT(114980, "3a96963b93154889bd7d59c8a60cf8005e864b930627616e51a4ad11cd9a3d50"); - ADD_CHECKPOINT(115257, "338e056551087fe23d6c4b4280244bc5362b004716d85ec799a775f190f9fea9"); //Hard fork to v14 - ADD_CHECKPOINT(118500, "2ef1cd0c68f1b8e1acf384109431b6377dbdbd6705964be17b7358c47ea07447"); - ADD_CHECKPOINT(157400, "44445d1fcc845b4d6f8e7730c50af64c09031003d584cdeaa04d6523e0acc049"); - ADD_CHECKPOINT(160777, "9496690579af21f38f00e67e11c2e85a15912fe4f412aad33d1162be1579e755"); //Hard fork to v15 - ADD_CHECKPOINT(194444, "0aa7ea6ade2ee8f5a525a079c53888fac415826ee8d1e8c92caa52629773db35"); - ADD_CHECKPOINT(200500, "1e5c7af11e19a94f334576d79fe0179ff493ce378701f3f810b674db2760c228"); - ADD_CHECKPOINT(211300, "f712b6dc0dfe896d18c5ca9097144d05ef8810b11277663638c0963d96ea172c"); - ADD_CHECKPOINT(223800, "878d805ce24368a48c4bd36283f3c53510e86b09511ec6770fbaca8f1fd3c55b"); - + ADD_CHECKPOINT2(1, "97f4ce4d7879b3bea54dcec738cd2ebb7952b4e9bb9743262310cd5fec749340", "0x2"); + ADD_CHECKPOINT2(6969, "aa7b66e8c461065139b55c29538a39c33ceda93e587f84d490ed573d80511c87", "0x118eef693fd"); //Hard fork to v8 + ADD_CHECKPOINT2(53666, "3f43f56f66ef0c43cf2fd14d0d28fa2aae0ef8f40716773511345750770f1255", "0xb677d6405ae"); //Hard fork to v9 + ADD_CHECKPOINT2(63469, "4e33a9343fc5b86661ec0affaeb5b5a065290602c02d817337e4a979fe5747d8", "0xe7cd9819062"); //Hard fork to v10 + ADD_CHECKPOINT2(81769, "41db9fef8d0ccfa78b570ee9525d4f55de77b510c3ae4b08a1d51b9aec9ade1d", "0x150066455b88"); //Hard fork to v11 + ADD_CHECKPOINT2(82069, "fdea800d23d0b2eea19dec8af31e453e883e8315c97e25c8bb3e88ca164f8369", "0x15079b5fdaa8"); //Hard fork to v12 + ADD_CHECKPOINT2(114969, "b48245956b87f243048fd61021f4b3e5443e57eee7ff8ba4762d18926e80b80c", "0x1ca552b3ec68"); //Hard fork to v13 + ADD_CHECKPOINT2(115257, "338e056551087fe23d6c4b4280244bc5362b004716d85ec799a775f190f9fea9", "0x1cb25f5d4628"); //Hard fork to v14 + ADD_CHECKPOINT2(160777, "9496690579af21f38f00e67e11c2e85a15912fe4f412aad33d1162be1579e755", "0x5376eaa196a8"); //Hard fork to v15 + ADD_CHECKPOINT2(230200, "0ebb018c452fa84997f573937db67ef3946a857ee57cd40fa51ffe368896c666", "0xbcc1409c7268"); return true; } diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h index cb6e93972..029c50548 100644 --- a/src/checkpoints/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -33,8 +33,10 @@ #include "misc_log_ex.h" #include "crypto/hash.h" #include "cryptonote_config.h" +#include "cryptonote_basic/difficulty.h" #define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false); +#define ADD_CHECKPOINT2(h, hash, difficulty) CHECK_AND_ASSERT(add_checkpoint(h, hash, difficulty), false); #define JSON_HASH_FILE_NAME "checkpoints.json" @@ -61,12 +63,13 @@ namespace cryptonote * * @param height the height of the block the checkpoint is for * @param hash_str the hash of the block, as a string + * @param difficulty_str the cumulative difficulty of the block, as a string (optional) * * @return false if parsing the hash fails, or if the height is a duplicate * AND the existing checkpoint hash does not match the new one, * otherwise returns true */ - bool add_checkpoint(uint64_t height, const std::string& hash_str); + bool add_checkpoint(uint64_t height, const std::string& hash_str, const std::string& difficulty_str = ""); /** * @brief checks if there is a checkpoint in the future @@ -133,6 +136,13 @@ namespace cryptonote */ const std::map& get_points() const; + /** + * @brief gets the difficulty checkpoints container + * + * @return a const reference to the difficulty checkpoints container + */ + const std::map& get_difficulty_points() const; + /** * @brief checks if our checkpoints container conflicts with another * @@ -187,6 +197,7 @@ namespace cryptonote private: std::map m_points; //!< the checkpoints container + std::map m_difficulty_points; //!< the difficulty checkpoints container }; } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8bbde6fd2..2b3e2b71f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #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(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()) @@ -984,6 +994,111 @@ start: return diff; } //------------------------------------------------------------------ +std::pair Blockchain::check_difficulty_checkpoints() const +{ + uint64_t res = 0; + for (const std::pair& 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 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 timestamps; + std::vector 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 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::max(), "Difficulty overflow!"); + difficulty_type recalculated_cum_diff = recalculated_cum_diff_256.convert_to(); + + 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 Blockchain::get_last_block_timestamps(unsigned int blocks) const { CRITICAL_REGION_LOCAL(m_blockchain_lock); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 966e98f7e..fb7e5c4f8 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -309,6 +309,22 @@ namespace cryptonote */ difficulty_type get_difficulty_for_next_block(); + /** + * @brief check currently stored difficulties against difficulty checkpoints + * + * @return {flag, height} flag: true if all difficulty checkpoints pass, height: the last checkpoint height before the difficulty drift bug starts + */ + std::pair check_difficulty_checkpoints() const; + + /** + * @brief recalculate difficulties for blocks after the last difficulty checkpoints to circumvent the annoying 'difficulty drift' bug + * + * @param start_height: if omitted, starts recalculation from the last difficulty checkpoint + * + * @return number of blocks whose difficulties got corrected + */ + size_t recalculate_difficulties(boost::optional start_height = boost::none); + /** * @brief adds a block to the blockchain * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 12e46571a..4b8d6cbcc 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1726,6 +1726,7 @@ namespace cryptonote 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_blockchain_pruning_interval.do_call(boost::bind(&core::update_blockchain_pruning, this)); + m_diff_recalc_interval.do_call(boost::bind(&core::recalculate_difficulties, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; @@ -1964,6 +1965,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::recalculate_difficulties() + { + m_blockchain_storage.recalculate_difficulties(); + return true; + } + //----------------------------------------------------------------------------------------------- void core::flush_bad_txs_cache() { bad_semantics_txes_lock.lock(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 472204119..6a9ffda92 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1028,6 +1028,13 @@ namespace cryptonote */ bool check_block_rate(); + /** + * @brief recalculate difficulties after the last difficulty checklpoint to circumvent the annoying 'difficulty drift' bug + * + * @return true + */ + bool recalculate_difficulties(); + 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 @@ -1053,6 +1060,7 @@ namespace cryptonote 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 epee::math_helper::once_a_time_seconds<60*60*5, true> m_blockchain_pruning_interval; //!< interval for incremental blockchain pruning + epee::math_helper::once_a_time_seconds<60*60*24*7, false> m_diff_recalc_interval; //!< interval for recalculating difficulties std::atomic m_starter_message_showed; //!< has the "daemon will sync now" message been shown?