diff --git a/src/CryptoNoteConfig.h b/src/CryptoNoteConfig.h index 94ee5c69..818df936 100644 --- a/src/CryptoNoteConfig.h +++ b/src/CryptoNoteConfig.h @@ -141,6 +141,7 @@ namespace cn const char P2P_NET_DATA_FILENAME[] = "p2pstate.bin"; const char CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME[] = "blockchainindices.dat"; const char MINER_CONFIG_FILE_NAME[] = "miner_conf.json"; + const char CRYPTONOTE_CHECKPOINT_FILENAME[] = "checkpoint.dat"; } // namespace parameters @@ -191,6 +192,7 @@ namespace cn // This defines the minimum P2P version required for lite blocks propogation const uint8_t P2P_LITE_BLOCKS_PROPOGATION_VERSION = 3; + const uint8_t P2P_CHECKPOINT_LIST_VERSION = 3; const size_t P2P_LOCAL_WHITE_PEERLIST_LIMIT = 1000; const size_t P2P_LOCAL_GRAY_PEERLIST_LIMIT = 5000; @@ -230,6 +232,9 @@ namespace cn __attribute__((unused)) #endif + const char DNS_CHECKPOINT_DOMAIN[] = "checkpoints.conceal.id"; + const char TESTNET_DNS_CHECKPOINT_DOMAIN[] = "testpoints.conceal.gq"; + // Blockchain Checkpoints: // {, ""}, const std::initializer_list diff --git a/src/CryptoNoteCore/Blockchain.cpp b/src/CryptoNoteCore/Blockchain.cpp index c208715f..86550d67 100644 --- a/src/CryptoNoteCore/Blockchain.cpp +++ b/src/CryptoNoteCore/Blockchain.cpp @@ -355,7 +355,6 @@ namespace cn m_blockchainIndexesEnabled(blockchainIndexesEnabled), m_blockchainAutosaveEnabled(blockchainAutosaveEnabled), logger(logger, "Blockchain") - { } @@ -461,7 +460,6 @@ namespace cn bool Blockchain::init(const std::string &config_folder, bool load_existing, bool testnet) { m_testnet = testnet; - m_checkpoints.set_testnet(testnet); std::lock_guard lk(m_blockchain_lock); if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) { @@ -471,6 +469,9 @@ namespace cn m_config_folder = config_folder; + m_checkpoints.init_targets(testnet, appendPath(config_folder, m_currency.checkpointFileName())); + m_checkpoints.load_checkpoints_from_file(); + if (!m_blocks.open(appendPath(config_folder, m_currency.blocksFileName()), appendPath(config_folder, m_currency.blockIndexesFileName()), 1024)) { return false; @@ -593,25 +594,35 @@ namespace cn bool Blockchain::checkCheckpoints(uint32_t &lastValidCheckpointHeight) { - std::vector checkpointHeights = m_checkpoints.getCheckpointHeights(); - for (const auto &checkpointHeight : checkpointHeights) + bool rv = true; + std::vector> checkpointHeights = m_checkpoints.get_checkpoint_targets(); + lastValidCheckpointHeight = 0; + + /* Hashes are in reverse order so if everything is ok we will only do one iteration */ + for (const auto &check : checkpointHeights) { - if (m_blocks.size() <= checkpointHeight) - { - return true; - } + uint32_t height = check.first; + if (m_blocks.size() <= height) + continue; - if (m_checkpoints.check_block(checkpointHeight, getBlockIdByHeight(checkpointHeight))) + crypto::Hash hv = m_blockIndex.getHashOfIds(0, height); + if (hv == check.second) { - lastValidCheckpointHeight = checkpointHeight; + lastValidCheckpointHeight = height; + break; } else - { - return false; - } + rv = false; } - logger(INFO, BRIGHT_WHITE) << "Checkpoints passed"; - return true; + + if(rv) + { + logger(INFO, BRIGHT_WHITE) << "Checkpoints passed"; + if(m_checkpoints.get_points_height() < lastValidCheckpointHeight) + m_checkpoints.set_checkpoint_list(m_blockIndex.getBlockIds(0, lastValidCheckpointHeight)); + } + + return rv; } void Blockchain::rebuildCache() @@ -1403,6 +1414,29 @@ namespace cn return true; } + bool Blockchain::is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const { + if (0 == block_height) + return false; + + uint32_t lowest_height = blockchain_height - cn::parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + + if (blockchain_height < cn::parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) + { + lowest_height = 0; + } + + if (block_height < lowest_height && !m_checkpoints.is_in_checkpoint_zone(block_height)) + { + logger(logging::DEBUGGING, logging::WHITE) + << "<< Checkpoints.cpp << " + << "Reorganization depth too deep : " << (blockchain_height - block_height) << ". Block Rejected"; + return false; + } + + uint32_t checkpoint_height = m_checkpoints.get_greatest_target_height(); + return checkpoint_height < block_height; + } + bool Blockchain::handle_alternative_block(const Block &b, const crypto::Hash &id, block_verification_context &bvc, bool sendNewAlternativeBlockMessage) { std::lock_guard lk(m_blockchain_lock); @@ -1416,10 +1450,8 @@ namespace cn return false; } - /* in the absence of a better solution, we fetch checkpoints from dns records */ - m_checkpoints.load_checkpoints_from_dns(); - if (!m_checkpoints.is_alternative_block_allowed(getCurrentBlockchainHeight(), block_height)) + if (!is_alternative_block_allowed(getCurrentBlockchainHeight(), block_height)) { logger(DEBUGGING) << "Block with id: " << id << std::endl << " can't be accepted for alternative chain, block height: " << block_height << std::endl @@ -1514,8 +1546,8 @@ namespace cn bei.bl = b; bei.height = alt_chain.size() ? it_prev->second.height + 1 : mainPrevHeight + 1; - bool is_a_checkpoint; - if (!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) + auto checkpoint_status = m_checkpoints.check_checkpoint(bei.height, id); + if ( checkpoint_status == CheckpointList::is_in_zone_failed ) { logger(ERROR, BRIGHT_RED) << "Checkpoint validaton failure"; @@ -1581,7 +1613,7 @@ namespace cn alt_chain.push_back(i_res.first->first); - if (is_a_checkpoint) + if ( checkpoint_status == CheckpointList::is_checkpointed ) { //do reorganize! logger(INFO, BRIGHT_GREEN) << "###### REORGANIZE on height: " << m_alternative_chains[alt_chain.front()].height << " of " << m_blocks.size() - 1 << ", checkpoint is found in alternative chain on height " << bei.height; @@ -2481,15 +2513,13 @@ namespace cn auto longhashTimeStart = std::chrono::steady_clock::now(); crypto::Hash proof_of_work = NULL_HASH; - if (m_checkpoints.is_in_checkpoint_zone(getCurrentBlockchainHeight())) + auto checkpoint_status = m_checkpoints.check_checkpoint(getCurrentBlockchainHeight(), blockHash); + if (checkpoint_status == CheckpointList::is_in_zone_failed ) { - if (!m_checkpoints.check_block(getCurrentBlockchainHeight(), blockHash)) - { - bvc.m_verification_failed = true; - return false; - } + bvc.m_verification_failed = true; + return false; } - else + else if(checkpoint_status == CheckpointList::is_out_of_zone ) { if (!m_currency.checkProofOfWork(m_cn_context, blockData, currentDifficulty, proof_of_work)) { diff --git a/src/CryptoNoteCore/Blockchain.h b/src/CryptoNoteCore/Blockchain.h index 7235aeae..db0f848b 100644 --- a/src/CryptoNoteCore/Blockchain.h +++ b/src/CryptoNoteCore/Blockchain.h @@ -15,7 +15,7 @@ #include "Common/ObserverManager.h" #include "Common/Util.h" #include "CryptoNoteCore/BlockIndex.h" -#include "CryptoNoteCore/Checkpoints.h" +#include "CryptoNoteCore/CheckpointList.h" #include "CryptoNoteCore/Currency.h" #include "CryptoNoteCore/DepositIndex.h" #include "CryptoNoteCore/IBlockchainStorageObserver.h" @@ -68,7 +68,6 @@ namespace cn bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t &height); std::vector getBlockIds(uint32_t startHeight, uint32_t maxCount); - void setCheckpoints(Checkpoints &&chk_pts) { m_checkpoints = std::move(chk_pts); } bool getBlocks(uint32_t start_offset, uint32_t count, std::list &blocks, std::list &txs); bool getBlocks(uint32_t start_offset, uint32_t count, std::list &blocks); bool getAlternativeBlocks(std::list &blocks); @@ -285,7 +284,7 @@ namespace cn outputs_container m_outputs; std::string m_config_folder; - Checkpoints m_checkpoints; + CheckpointList m_checkpoints; std::atomic m_is_in_checkpoint_zone; using Blocks = SwappedVector; @@ -320,6 +319,7 @@ namespace cn bool switch_to_alternative_blockchain(const std::list &alt_chain, bool discard_disconnected_chain); + bool is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const; bool handle_alternative_block(const Block &b, const crypto::Hash &id, block_verification_context &bvc, bool sendNewAlternativeBlockMessage = true); difficulty_type get_next_difficulty_for_alternative_chain(const std::list &alt_chain, const BlockEntry &bei); void pushToDepositIndex(const BlockEntry &block, uint64_t interest); diff --git a/src/CryptoNoteCore/CheckpointList.h b/src/CryptoNoteCore/CheckpointList.h new file mode 100644 index 00000000..10b5e944 --- /dev/null +++ b/src/CryptoNoteCore/CheckpointList.h @@ -0,0 +1,94 @@ +// Copyright (c) 2011-2017 The Cryptonote developers +// Copyright (c) 2017-2018 The Circle Foundation & Conceal Devs +// Copyright (c) 2018-2023 Conceal Network & Conceal Devs +// +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include +#include + +#include "CryptoNoteBasicImpl.h" +#include + +namespace cn +{ + class CheckpointList + { + public: + explicit CheckpointList(logging::ILogger& log) : logger(log, "checkpoint_list") {} + + void init_targets(bool is_testnet, const std::string& save_file); + + bool add_checkpoint_list(const std::vector& points); + bool set_checkpoint_list(std::vector&& points); + bool load_checkpoints_from_file(); + + uint32_t get_points_height() const + { + return m_points.size()-1; + } + + uint32_t get_greatest_target_height() const + { + return m_targets.rbegin()->first - 1; + } + + bool is_ready() const + { + return m_points.size() > 0; + } + + bool is_in_checkpoint_zone(uint32_t height) const + { + return m_points.size() < height; + } + + enum check_rt + { + is_out_of_zone, + is_in_zone_failed, + is_checkpointed + }; + + check_rt check_checkpoint(uint32_t height, const crypto::Hash& hv) const + { + if(m_points.size() >= height) + return is_out_of_zone; + if(m_points[height] == hv) + return is_checkpointed; + else + return is_in_zone_failed; + } + + std::vector> get_checkpoint_targets() + { + std::vector> rv; + rv.reserve(m_targets.size()); + for(auto it = m_targets.rbegin(); it != m_targets.rend(); ++it) + rv.emplace_back(it->first-1, it->second); + return rv; + } + + private: + bool m_testnet; + logging::LoggerRef logger; + std::string m_save_file; + + std::map m_targets; /*NB uint32_t is size not height */ + std::unordered_set m_valid_point_sizes; + std::vector m_points; + + bool save_checkpoints(); + bool add_checkpoint_target(uint32_t height, const std::string &hash_str); + bool is_fsize_valid(uint32_t fsize) + { + if(fsize % sizeof(crypto::Hash) != 0) + return false; + fsize /= sizeof(crypto::Hash); + + return m_valid_point_sizes.find(fsize) != m_valid_point_sizes.end(); + } + }; +} diff --git a/src/CryptoNoteCore/Checkpoints.cpp b/src/CryptoNoteCore/Checkpoints.cpp deleted file mode 100644 index a75d6dce..00000000 --- a/src/CryptoNoteCore/Checkpoints.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) 2011-2017 The Cryptonote developers -// Copyright (c) 2017-2018 The Circle Foundation & Conceal Devs -// Copyright (c) 2018-2023 Conceal Network & Conceal Devs -// -// Copyright (c) 2016-2019, The Karbo developers - -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Checkpoints.h" -#include "../CryptoNoteConfig.h" -#include "Common/StringTools.h" -#include "Common/DnsTools.h" - -using namespace logging; - -namespace cn { -//--------------------------------------------------------------------------- -Checkpoints::Checkpoints(logging::ILogger &log) : logger(log, "checkpoints") {} -//--------------------------------------------------------------------------- -bool Checkpoints::add_checkpoint(uint32_t height, const std::string &hash_str) { - crypto::Hash h = NULL_HASH; - - if (!common::podFromHex(hash_str, h)) { - logger(ERROR) << "<< Checkpoints.cpp << " << "Incorrect hash in checkpoints"; - return false; - } - - if (!(0 == m_points.count(height))) { - logger(DEBUGGING) << "Checkpoint already exists for height " << height; - return false; - } - - m_points[height] = h; - - return true; -} -//--------------------------------------------------------------------------- -bool Checkpoints::is_in_checkpoint_zone(uint32_t height) const { - return !m_points.empty() && (height <= (--m_points.end())->first); -} -//--------------------------------------------------------------------------- -bool Checkpoints::check_block(uint32_t height, const crypto::Hash &h, bool &is_a_checkpoint) const { - auto it = m_points.find(height); - is_a_checkpoint = it != m_points.end(); - if (!is_a_checkpoint) - return true; - - if (it->second == h) { - return true; - } else { - logger(logging::ERROR) << "<< Checkpoints.cpp << " << "Checkpoint failed for height " << height - << ". Expected hash: " << it->second - << ", Fetched hash: " << h; - return false; - } -} -//--------------------------------------------------------------------------- -bool Checkpoints::check_block(uint32_t height, const crypto::Hash &h) const { - bool ignored; - return check_block(height, h, ignored); -} -//--------------------------------------------------------------------------- -bool Checkpoints::is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const { - if (0 == block_height) - return false; - - uint32_t lowest_height = blockchain_height - cn::parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; - - if (blockchain_height < cn::parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) - { - lowest_height = 0; - } - - if (block_height < lowest_height && !is_in_checkpoint_zone(block_height)) - { - logger(logging::DEBUGGING, logging::WHITE) - << "<< Checkpoints.cpp << " - << "Reorganization depth too deep : " << (blockchain_height - block_height) << ". Block Rejected"; - return false; - } - - auto it = m_points.upper_bound(blockchain_height); - if (it == m_points.begin()) - return true; - - --it; - uint32_t checkpoint_height = it->first; - return checkpoint_height < block_height; -} - -//--------------------------------------------------------------------------- - -std::vector Checkpoints::getCheckpointHeights() const { - std::vector checkpointHeights; - checkpointHeights.reserve(m_points.size()); - for (const auto& it : m_points) { - checkpointHeights.push_back(it.first); - } - - return checkpointHeights; -} - -bool Checkpoints::load_checkpoints_from_dns() -{ - std::string domain("checkpoints.conceal.id"); - if (m_testnet) - { - domain = "testpoints.conceal.gq"; - } - std::vectorrecords; - - logger(logging::DEBUGGING) << "<< Checkpoints.cpp << " << "Fetching DNS checkpoint records from " << domain; - - if (!common::fetch_dns_txt(domain, records)) { - logger(logging::DEBUGGING) << "<< Checkpoints.cpp << " << "Failed to lookup DNS checkpoint records from " << domain; - } - - for (const auto& record : records) { - uint32_t height; - crypto::Hash hash = NULL_HASH; - std::stringstream ss; - size_t del = record.find_first_of(':'); - std::string height_str = record.substr(0, del), hash_str = record.substr(del + 1, 64); - ss.str(height_str); - ss >> height; - char c; - if (del == std::string::npos) continue; - if ((ss.fail() || ss.get(c)) || !common::podFromHex(hash_str, hash)) { - logger(logging::INFO) << "<< Checkpoints.cpp << " << "Failed to parse DNS checkpoint record: " << record; - continue; - } - - if (!(0 == m_points.count(height))) { - logger(DEBUGGING) << "<< Checkpoints.cpp << " << "Checkpoint already exists for height: " << height << ". Ignoring DNS checkpoint."; - } else { - add_checkpoint(height, hash_str); - logger(DEBUGGING) << "<< Checkpoints.cpp << " << "Added DNS checkpoint: " << height_str << ":" << hash_str; - } - } - - return true; -} - -bool Checkpoints::load_checkpoints() -{ - if (m_testnet) - { - for (const auto &cp : cn::TESTNET_CHECKPOINTS) - { - add_checkpoint(cp.height, cp.blockId); - } - } - else - { - for (const auto &cp : cn::CHECKPOINTS) - { - add_checkpoint(cp.height, cp.blockId); - } - } - return true; -} - -bool Checkpoints::load_checkpoints_from_file(const std::string& fileName) { - std::ifstream file(fileName); - if (!file) { - logger(logging::ERROR, BRIGHT_RED) << "Could not load checkpoints file: " << fileName; - return false; - } - std::string indexString; - std::string hash; - uint32_t height; - while (std::getline(file, indexString, ','), std::getline(file, hash)) { - try { - height = std::stoi(indexString); - } catch (const std::invalid_argument &) { - logger(ERROR, BRIGHT_RED) << "Invalid checkpoint file format - " - << "could not parse height as a number"; - return false; - } - if (!add_checkpoint(height, hash)) { - return false; - } - } - logger(logging::INFO) << "Loaded " << m_points.size() << " checkpoints from " << fileName; - return true; -} - -void Checkpoints::set_testnet(bool testnet) { m_testnet = testnet; } - -} diff --git a/src/CryptoNoteCore/Checkpoints.h b/src/CryptoNoteCore/Checkpoints.h deleted file mode 100644 index 6de13aca..00000000 --- a/src/CryptoNoteCore/Checkpoints.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2011-2017 The Cryptonote developers -// Copyright (c) 2017-2018 The Circle Foundation & Conceal Devs -// Copyright (c) 2018-2023 Conceal Network & Conceal Devs -// -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#pragma once -#include -#include "CryptoNoteBasicImpl.h" -#include - -namespace cn -{ - class Checkpoints - { - public: - explicit Checkpoints(logging::ILogger& log); - - bool add_checkpoint(uint32_t height, const std::string& hash_str); - bool is_in_checkpoint_zone(uint32_t height) const; - bool load_checkpoints_from_file(const std::string& fileName); - bool load_checkpoints_from_dns(); - bool load_checkpoints(); - bool check_block(uint32_t height, const crypto::Hash& h) const; - bool check_block(uint32_t height, const crypto::Hash& h, bool& is_a_checkpoint) const; - bool is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const; - std::vector getCheckpointHeights() const; - void set_testnet(bool testnet); - - private: - bool m_testnet = false; - std::map m_points; - logging::LoggerRef logger; - }; -} diff --git a/src/CryptoNoteCore/CheckpointsList.cpp b/src/CryptoNoteCore/CheckpointsList.cpp new file mode 100644 index 00000000..65b9bec4 --- /dev/null +++ b/src/CryptoNoteCore/CheckpointsList.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2011-2017 The Cryptonote developers +// Copyright (c) 2017-2018 The Circle Foundation & Conceal Devs +// Copyright (c) 2018-2023 Conceal Network & Conceal Devs +// +// Copyright (c) 2016-2019, The Karbo developers + +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CheckpointList.h" +#include "../CryptoNoteConfig.h" +#include "Common/StringTools.h" +#include "Common/DnsTools.h" +#include "crypto/hash.h" + +using namespace logging; + +namespace cn { + void CheckpointList::init_targets(bool is_testnet, const std::string& save_file) + { + m_testnet = is_testnet; + m_save_file = save_file; + + for (const auto &cp : m_testnet ? cn::TESTNET_CHECKPOINTS : cn::CHECKPOINTS) + { + add_checkpoint_target(cp.height, cp.blockId); + } + + const char* domain; + if (m_testnet) + domain = TESTNET_DNS_CHECKPOINT_DOMAIN; + else + domain = DNS_CHECKPOINT_DOMAIN; + + std::vectorrecords; + + logger(logging::DEBUGGING) << "<< CheckpointList.cpp << " << "Fetching DNS checkpoint records from " << domain; + + if (!common::fetch_dns_txt(domain, records)) { + logger(logging::DEBUGGING) << "<< CheckpointList.cpp << " << "Failed to lookup DNS checkpoint records from " << domain; + } + + for (const auto& record : records) { + uint32_t height; + crypto::Hash hash = NULL_HASH; + std::stringstream ss; + size_t del = record.find_first_of(':'); + std::string height_str = record.substr(0, del), hash_str = record.substr(del + 1, 64); + ss.str(height_str); + ss >> height; + char c; + if (del == std::string::npos) continue; + if ((ss.fail() || ss.get(c)) || !common::podFromHex(hash_str, hash)) { + logger(logging::INFO) << "<< CheckpointList.cpp << " << "Failed to parse DNS checkpoint record: " << record; + continue; + } + + if (!(0 == m_targets.count(height))) { + logger(DEBUGGING) << "<< CheckpointList.cpp << " << "Checkpoint already exists for height: " << height << ". Ignoring DNS checkpoint."; + } else { + add_checkpoint_target(height, hash_str); + logger(DEBUGGING) << "<< CheckpointList.cpp << " << "Added DNS checkpoint target: " << height_str << ":" << hash_str; + } + } + } + + bool CheckpointList::add_checkpoint_target(uint32_t height, const std::string &hash_str) { + crypto::Hash h = NULL_HASH; + + if (!common::podFromHex(hash_str, h)) { + logger(ERROR) << "<< Checkpoints.cpp << " << "Incorrect hash in checkpoints"; + return false; + } + + if (!(0 == m_targets.count(height))) { + logger(DEBUGGING) << "Checkpoint already exists for height " << height; + return false; + } + + height += 1; + m_targets[height] = h; + m_valid_point_sizes.insert(height); + + return true; + } + + bool CheckpointList::set_checkpoint_list(std::vector&& points) + { + uint32_t point_size = points.size(); + if(m_valid_point_sizes.find(point_size) == m_valid_point_sizes.end()) + return false; + + crypto::Hash hv = crypto::cn_fast_hash(points.data(), points.size() * sizeof(crypto::Hash)); + if(hv != m_targets[point_size]) + { + logger(logging::ERROR) << "<< CheckpointList.cpp << " << "CheckpointList verification failed for height " << point_size-1 << + ". Expected hash: " << m_targets[point_size] << + ", Fetched hash: " << hv; + return false; + } + + m_points = std::move(points); + logger(logging::INFO) << "Loaded " << m_points.size() << " checkpoints from local index"; + + save_checkpoints(); + return true; + } + + bool CheckpointList::add_checkpoint_list(const std::vector& points) + { + uint32_t point_size = points.size() + m_points.size(); + if(m_valid_point_sizes.find(point_size) == m_valid_point_sizes.end()) + return false; + + /* This copy wouldn't be needed with three funciton hash */ + std::vector new_points(m_points); + new_points.insert(std::end(new_points), std::begin(points), std::end(points)); + + crypto::Hash hv = crypto::cn_fast_hash(new_points.data(), new_points.size() * sizeof(crypto::Hash)); + if(hv != m_targets[point_size]) + { + logger(logging::ERROR) << "<< CheckpointList.cpp << " << "CheckpointList verification failed for height " << point_size-1 << + ". Expected hash: " << m_targets[point_size] << + ", Fetched hash: " << hv; + return false; + } + + m_points = std::move(new_points); + logger(logging::INFO) << "Loaded " << points.size() << " checkpoints from p2p, total " << m_points.size(); + + save_checkpoints(); + return true; + } + + bool CheckpointList::load_checkpoints_from_file() + { + std::ifstream file(m_save_file, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + return false; + } + + uint64_t fsize = file.tellg(); + if(is_fsize_valid(fsize)) { + logger(logging::ERROR) << "<< CheckpointList.cpp << " << "Invalid file size" << fsize; + return false; + } + uint32_t point_size = fsize / sizeof(crypto::Hash); + + file.seekg(0, std::ios::beg); + + std::vector points(point_size); + if (!file.read(reinterpret_cast(points.data()), fsize)) { + logger(logging::ERROR) << "<< CheckpointList.cpp << " << "error reading file"; + return false; + } + + crypto::Hash hv = crypto::cn_fast_hash(points.data(), points.size() * sizeof(crypto::Hash)); + if(hv != m_targets[point_size]) + { + logger(logging::ERROR) << "<< CheckpointList.cpp << " << "CheckpointList verification (from file) failed for height " << point_size-1 + << ". Expected hash: " << m_targets[point_size] + << ", Fetched hash: " << hv; + return false; + } + + m_points = std::move(points); + logger(logging::INFO) << "Loaded " << m_points.size() << " checkpoints from disk " << m_save_file; + return true; + } + + bool CheckpointList::save_checkpoints() + { + std::ofstream file(m_save_file, std::ios::binary); + + if (!file.is_open()) { + logger(logging::ERROR) << "<< CheckpointList.cpp << " << "error opening file for write " << m_save_file; + return false; + } + + file.write(reinterpret_cast(m_points.data()), m_points.size() * sizeof(crypto::Hash)); + file.close(); + + if (!file) { + logger(logging::ERROR) << "<< CheckpointList.cpp << " << "error writing to file " << m_save_file; + return false; + } + } +} diff --git a/src/CryptoNoteCore/Core.cpp b/src/CryptoNoteCore/Core.cpp index b9b83c3c..ee34f853 100644 --- a/src/CryptoNoteCore/Core.cpp +++ b/src/CryptoNoteCore/Core.cpp @@ -87,10 +87,6 @@ void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { } } //----------------------------------------------------------------------------------- -void core::set_checkpoints(Checkpoints&& chk_pts) { - m_blockchain.setCheckpoints(std::move(chk_pts)); -} -//----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& /*desc*/) { } diff --git a/src/CryptoNoteCore/Core.h b/src/CryptoNoteCore/Core.h index 11164284..b5799213 100644 --- a/src/CryptoNoteCore/Core.h +++ b/src/CryptoNoteCore/Core.h @@ -118,7 +118,6 @@ namespace cn { uint64_t difficultyAtHeight(uint64_t height); void set_cryptonote_protocol(i_cryptonote_protocol *pprotocol); - void set_checkpoints(Checkpoints &&chk_pts); std::vector getPoolTransactions() override; bool getPoolTransaction(const crypto::Hash &tx_hash, Transaction &transaction) override; diff --git a/src/CryptoNoteCore/Currency.cpp b/src/CryptoNoteCore/Currency.cpp index b493e982..a46051c1 100644 --- a/src/CryptoNoteCore/Currency.cpp +++ b/src/CryptoNoteCore/Currency.cpp @@ -1392,6 +1392,7 @@ namespace cn blockIndexesFileName(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME); txPoolFileName(parameters::CRYPTONOTE_POOLDATA_FILENAME); blockchinIndicesFileName(parameters::CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME); + checkpointFileName(parameters::CRYPTONOTE_CHECKPOINT_FILENAME); testnet(false); } diff --git a/src/CryptoNoteCore/Currency.h b/src/CryptoNoteCore/Currency.h index a81f91c4..d70b1d40 100644 --- a/src/CryptoNoteCore/Currency.h +++ b/src/CryptoNoteCore/Currency.h @@ -148,6 +148,7 @@ namespace cn const std::string &blockIndexesFileName() const { return m_blockIndexesFileName; } const std::string &txPoolFileName() const { return m_txPoolFileName; } const std::string &blockchinIndicesFileName() const { return m_blockchinIndicesFileName; } + const std::string &checkpointFileName() const { return m_checkpointFileName; } bool isTestnet() const { return m_testnet; } @@ -290,6 +291,7 @@ namespace cn std::string m_blockIndexesFileName; std::string m_txPoolFileName; std::string m_blockchinIndicesFileName; + std::string m_checkpointFileName; static const std::vector REWARD_INCREASING_FACTOR; @@ -634,6 +636,11 @@ namespace cn m_currency.m_blockchinIndicesFileName = val; return *this; } + CurrencyBuilder &checkpointFileName(const std::string &val) + { + m_currency.m_checkpointFileName = val; + return *this; + } CurrencyBuilder &genesisCoinbaseTxHex(const std::string &val) { @@ -694,4 +701,4 @@ namespace cn } }; -} // namespace cn \ No newline at end of file +} // namespace cn diff --git a/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h b/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h index b9f62611..7bea804a 100644 --- a/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h @@ -225,6 +225,9 @@ namespace cn typedef NOTIFY_NEW_LITE_BLOCK_request request; }; + /************************************************************************/ + /* */ + /************************************************************************/ struct NOTIFY_MISSING_TXS_request { crypto::Hash blockHash; @@ -244,5 +247,38 @@ namespace cn const static int ID = BC_COMMANDS_POOL_BASE + 10; typedef NOTIFY_MISSING_TXS_request request; }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_REQUEST_CHECKPOINT_LIST_request + { + + void serialize(ISerializer &s) + { + + } + }; + + struct NOTIFY_REQUEST_CHECKPOINT_LIST + { + const static int ID = BC_COMMANDS_POOL_BASE + 11; + typedef NOTIFY_REQUEST_CHECKPOINT_LIST_request request; + }; + + struct NOTIFY_RESPONSE_CHECKPOINT_LIST_request + { + + void serialize(ISerializer &s) + { + + } + }; + + struct NOTIFY_RESPONSE_CHECKPOINT_LIST + { + const static int ID = BC_COMMANDS_POOL_BASE + 12; + typedef NOTIFY_RESPONSE_CHECKPOINT_LIST_request request; + }; } // namespace cn diff --git a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp index beea9661..92b9aa94 100644 --- a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp @@ -211,6 +211,11 @@ bool CryptoNoteProtocolHandler::process_payload_sync_data(const CORE_SYNC_DATA & { m_peersCount++; m_observerManager.notify(&ICryptoNoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + + // only if checkpoint is loaded + NOTIFY_REQUEST_CHECKPOINT_LIST::request req; + if (!post_notify(*m_p2p, req, context)) + return false; } return true; @@ -259,6 +264,8 @@ int CryptoNoteProtocolHandler::handleCommand(bool is_notify, int command, const HANDLE_NOTIFY(NOTIFY_NEW_TRANSACTIONS, &CryptoNoteProtocolHandler::handle_notify_new_transactions) HANDLE_NOTIFY(NOTIFY_REQUEST_GET_OBJECTS, &CryptoNoteProtocolHandler::handle_request_get_objects) HANDLE_NOTIFY(NOTIFY_RESPONSE_GET_OBJECTS, &CryptoNoteProtocolHandler::handle_response_get_objects) + HANDLE_NOTIFY(NOTIFY_REQUEST_CHECKPOINT_LIST, &CryptoNoteProtocolHandler::handle_request_checkpoint_list) + HANDLE_NOTIFY(NOTIFY_RESPONSE_CHECKPOINT_LIST, &CryptoNoteProtocolHandler::handle_response_checkpoint_list) HANDLE_NOTIFY(NOTIFY_REQUEST_CHAIN, &CryptoNoteProtocolHandler::handle_request_chain) HANDLE_NOTIFY(NOTIFY_RESPONSE_CHAIN_ENTRY, &CryptoNoteProtocolHandler::handle_response_chain_entry) HANDLE_NOTIFY(NOTIFY_REQUEST_TX_POOL, &CryptoNoteProtocolHandler::handle_request_tx_pool) @@ -1121,4 +1128,12 @@ int CryptoNoteProtocolHandler::doPushLiteBlock(NOTIFY_NEW_LITE_BLOCK::request ar return 1; } +int CryptoNoteProtocolHandler::handle_request_checkpoint_list(int command, NOTIFY_REQUEST_CHECKPOINT_LIST::request& arg, CryptoNoteConnectionContext& context) +{ +} + +int CryptoNoteProtocolHandler::handle_response_checkpoint_list(int command, NOTIFY_RESPONSE_CHECKPOINT_LIST::request& arg, CryptoNoteConnectionContext& context) +{ +} + }; // namespace cn diff --git a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h index ccb1d1bf..a1c2babe 100644 --- a/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h @@ -80,6 +80,8 @@ namespace cn int handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, CryptoNoteConnectionContext& context); int handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context); int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context); + int handle_request_checkpoint_list(int command, NOTIFY_REQUEST_CHECKPOINT_LIST::request& arg, CryptoNoteConnectionContext& context); + int handle_response_checkpoint_list(int command, NOTIFY_RESPONSE_CHECKPOINT_LIST::request& arg, CryptoNoteConnectionContext& context); int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, CryptoNoteConnectionContext& context); int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, CryptoNoteConnectionContext& context); int handle_request_tx_pool(int command, NOTIFY_REQUEST_TX_POOL::request &arg, CryptoNoteConnectionContext &context); diff --git a/src/Daemon/Daemon.cpp b/src/Daemon/Daemon.cpp index d805957a..ef7387e2 100644 --- a/src/Daemon/Daemon.cpp +++ b/src/Daemon/Daemon.cpp @@ -17,7 +17,7 @@ #include "Common/PathTools.h" #include "crypto/hash.h" #include "CryptoNoteConfig.h" -#include "CryptoNoteCore/Checkpoints.h" +#include "CryptoNoteCore/CheckpointList.h" #include "CryptoNoteCore/Core.h" #include "CryptoNoteCore/CoreConfig.h" #include "CryptoNoteCore/CryptoNoteTools.h" @@ -226,12 +226,6 @@ int main(int argc, char* argv[]) cn::Currency currency = currencyBuilder.currency(); cn::core ccore(currency, nullptr, logManager, vm["enable-blockchain-indexes"].as(), vm["enable-autosave"].as()); - cn::Checkpoints checkpoints(logManager); - checkpoints.set_testnet(coreConfig.testnet); - checkpoints.load_checkpoints(); - checkpoints.load_checkpoints_from_dns(); - ccore.set_checkpoints(std::move(checkpoints)); - NetNodeConfig netNodeConfig; netNodeConfig.init(vm); netNodeConfig.setTestnet(coreConfig.testnet);