From b261d9207ba5cdc0334fab403204971f79b6ca03 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Fri, 26 Sep 2014 01:01:58 -0400 Subject: [PATCH] DNS checkpoint updating added, and daemon flag to enforce them The daemon should now check for updated checkpoints from checkpoints.moneropulse.org as well as from the configured json file every ~1hr (and on launch). The daemon now has a flag to enable enforcing these checkpoints (rather than just printing a warning when they fail). TODO: an easily configurable list of DNS servers to check for checkpoints as opposed to the hard-coded "checkpoints.moneropulse.org" --- src/cryptonote_core/blockchain_storage.cpp | 32 +++++++++++--- src/cryptonote_core/blockchain_storage.h | 7 ++- src/cryptonote_core/checkpoints_create.cpp | 50 ++++++++++++++++++++++ src/cryptonote_core/checkpoints_create.h | 2 + src/cryptonote_core/cryptonote_core.cpp | 18 +++++--- src/cryptonote_core/cryptonote_core.h | 5 ++- src/daemon/daemon.cpp | 10 ++++- 7 files changed, 107 insertions(+), 17 deletions(-) diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 90820232c..5dfda08ba 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -442,6 +442,13 @@ difficulty_type blockchain_storage::get_difficulty_for_next_block() bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); + + // fail if rollback_height passed is too high + if (rollback_height > m_blocks.size()) + { + return true; + } + //remove failed subchain for(size_t i = m_blocks.size()-1; i >=rollback_height; i--) { @@ -1775,13 +1782,11 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont return handle_block_to_main_chain(bl, id, bvc); } //------------------------------------------------------------------ -void blockchain_storage::update_checkpoints(const std::string& file_path) +bool blockchain_storage::update_checkpoints(const std::string& file_path) { - // if a path is supplied, updated checkpoints from json. - // if not, probably fetch from DNS TXT Records. - if (file_path.size() > 0) + if (!cryptonote::load_new_checkpoints(m_checkpoints, file_path)) { - cryptonote::load_checkpoints_from_json(m_checkpoints, file_path); + return false; } const auto& points = m_checkpoints.get_points(); @@ -1790,7 +1795,22 @@ void blockchain_storage::update_checkpoints(const std::string& file_path) { if (!m_checkpoints.check_block(pt.first, get_block_hash(m_blocks[pt.first].bl))) { - LOG_ERROR("Checkpoint failed when adding new checkpoints from json file, this could be very bad."); + // if we're enforcing dns checkpoints, roll back to a couple of blocks before the checkpoint + if (m_enforce_dns_checkpoints) + { + LOG_ERROR("Checkpoint failed when adding new checkpoints, rolling back!"); + std::list empty; + rollback_blockchain_switching(empty, pt.first - 2); + } + else + { + LOG_ERROR("Checkpoint failed when adding new checkpoints, this could be very bad."); + } } } + return true; +} +void blockchain_storage::set_enforce_dns_checkpoints(bool enforce_checkpoints) +{ + m_enforce_dns_checkpoints = enforce_checkpoints; } diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index b89db1825..1ab49a7bc 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -78,7 +78,7 @@ namespace cryptonote uint64_t already_generated_coins; }; - blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false) + blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false) {}; bool init() { return init(tools::get_default_data_dir(), true); } @@ -180,7 +180,8 @@ namespace cryptonote void print_blockchain(uint64_t start_index, uint64_t end_index); void print_blockchain_index(); void print_blockchain_outs(const std::string& file); - void update_checkpoints(const std::string& file_path); + bool update_checkpoints(const std::string& file_path); + void set_enforce_dns_checkpoints(bool enforce_checkpoints); private: typedef std::unordered_map blocks_by_id_index; @@ -215,6 +216,8 @@ namespace cryptonote std::atomic m_is_in_checkpoint_zone; std::atomic m_is_blockchain_storing; + bool m_enforce_dns_checkpoints; + bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); bool pop_block_from_blockchain(); bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count); diff --git a/src/cryptonote_core/checkpoints_create.cpp b/src/cryptonote_core/checkpoints_create.cpp index ff927396e..b81353539 100644 --- a/src/cryptonote_core/checkpoints_create.cpp +++ b/src/cryptonote_core/checkpoints_create.cpp @@ -29,6 +29,9 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "checkpoints_create.h" +#include "common/dns_utils.h" +#include "include_base_utils.h" +#include #include "storages/portable_storage_template_helper.h" // epee json include namespace cryptonote @@ -107,4 +110,51 @@ bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::strin return true; } +bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, const std::string& url) +{ + bool avail, valid; + auto records = tools::DNSResolver::instance().get_txt_record(url, avail, valid); + + if (avail && !valid) + { + LOG_ERROR("DNSSEC present and failed validation for query to" << url); + return false; + } + + for (auto& record : records) + { + auto pos = record.find(":"); + if (pos != std::string::npos) + { + uint64_t height; + crypto::hash hash; + + // parse the first part as uint64_t, + // if this fails move on to the next record + std::stringstream ss(record.substr(0, pos)); + if (!(ss >> height)) + { + continue; + } + + // parse the second part as crypto::hash, + // if this fails move on to the next record + std::string hashStr = record.substr(pos + 1); + if (!epee::string_tools::parse_tpod_from_hex_string(hashStr, hash)) + { + continue; + } + + ADD_CHECKPOINT(height, hashStr); + } + } + return true; +} + +bool load_new_checkpoints(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath) +{ + // TODO: replace hard-coded url with const string or #define + return (load_checkpoints_from_json(checkpoints, json_hashfile_fullpath) && load_checkpoints_from_dns(checkpoints, "checkpoints.moneropulse.org")); +} + } // namespace cryptonote diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index 8e7a58228..92a970e9f 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -42,5 +42,7 @@ namespace cryptonote bool create_checkpoints(cryptonote::checkpoints& checkpoints); bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath); + bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints); + bool load_new_checkpoints(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath); } // namespace cryptonote diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 7586421b3..1dbc7bf52 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -54,7 +54,9 @@ namespace cryptonote m_miner(this), m_miner_address(boost::value_initialized()), m_starter_message_showed(false), - m_target_blockchain_height(0) + m_target_blockchain_height(0), + m_checkpoints_path(""), + m_last_checkpoints_update(0) { set_cryptonote_protocol(pprotocol); } @@ -69,21 +71,27 @@ namespace cryptonote void core::set_checkpoints(checkpoints&& chk_pts) { m_blockchain_storage.set_checkpoints(std::move(chk_pts)); - m_last_checkpoints_update = time(NULL); } //----------------------------------------------------------------------------------- void core::set_checkpoints_file_path(const std::string& path) { m_checkpoints_path = path; } - //----------------------------------------------------------------------------------------------- - void core::update_checkpoints() + //----------------------------------------------------------------------------------- + void core::set_enforce_dns_checkpoints(bool enforce_dns) { + m_blockchain_storage.set_enforce_dns_checkpoints(enforce_dns); + } + //----------------------------------------------------------------------------------------------- + bool core::update_checkpoints() + { + bool res = true; if (time(NULL) - m_last_checkpoints_update >= 3600) { - m_blockchain_storage.update_checkpoints(m_checkpoints_path); + res = m_blockchain_storage.update_checkpoints(m_checkpoints_path); m_last_checkpoints_update = time(NULL); } + return res; } //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& /*desc*/) diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 81eddc657..b3bfddff3 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -95,6 +95,7 @@ namespace cryptonote void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); void set_checkpoints(checkpoints&& chk_pts); void set_checkpoints_file_path(const std::string& path); + void set_enforce_dns_checkpoints(bool enforce_dns); bool get_pool_transactions(std::list& txs); size_t get_pool_transactions_count(); @@ -122,9 +123,9 @@ namespace cryptonote void set_target_blockchain_height(uint64_t target_blockchain_height); uint64_t get_target_blockchain_height() const; - private: - void update_checkpoints(); + bool update_checkpoints(); + private: bool add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); bool add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); bool add_new_block(const block& b, block_verification_context& bvc); diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 5e4e9c266..8e915fb1d 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -69,6 +69,7 @@ namespace , "Run on testnet. The wallet must be launched with --testnet flag." , false }; + const command_line::arg_descriptor arg_dns_checkpoints = {"enforce-dns-checkpointing", "checkpoints from DNS server will be enforced", false}; } bool command_line_preprocessor(const boost::program_options::variables_map& vm) @@ -135,6 +136,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_console); command_line::add_arg(desc_cmd_sett, arg_testnet_on); + command_line::add_arg(desc_cmd_sett, arg_dns_checkpoints); cryptonote::core::init_options(desc_cmd_sett); cryptonote::core_rpc_server::init_options(desc_cmd_sett); @@ -206,17 +208,21 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize checkpoints"); boost::filesystem::path json(JSON_HASH_FILE_NAME); boost::filesystem::path checkpoint_json_hashfile_fullpath = data_dir / json; - res = cryptonote::load_checkpoints_from_json(checkpoints, checkpoint_json_hashfile_fullpath.string().c_str()); - CHECK_AND_ASSERT_MES(res, 1, "Failed to load initial checkpoints"); //create objects and link them cryptonote::core ccore(NULL); + // tell core if we're enforcing dns checkpoints + bool enforce_dns = command_line::get_arg(vm, arg_dns_checkpoints); + ccore.set_enforce_dns_checkpoints(enforce_dns); + if (testnet_mode) { LOG_PRINT_L0("Starting in testnet mode!"); } else { ccore.set_checkpoints(std::move(checkpoints)); ccore.set_checkpoints_file_path(checkpoint_json_hashfile_fullpath.string()); + res = ccore.update_checkpoints(); + CHECK_AND_ASSERT_MES(res, 1, "Failed to load initial checkpoints"); } cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL);