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"
This commit is contained in:
Thomas Winget 2014-09-26 01:01:58 -04:00
parent 30caebfce3
commit b261d9207b
7 changed files with 107 additions and 17 deletions

View file

@ -442,6 +442,13 @@ difficulty_type blockchain_storage::get_difficulty_for_next_block()
bool blockchain_storage::rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height) bool blockchain_storage::rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height)
{ {
CRITICAL_REGION_LOCAL(m_blockchain_lock); 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 //remove failed subchain
for(size_t i = m_blocks.size()-1; i >=rollback_height; i--) 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); 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 (!cryptonote::load_new_checkpoints(m_checkpoints, file_path))
// if not, probably fetch from DNS TXT Records.
if (file_path.size() > 0)
{ {
cryptonote::load_checkpoints_from_json(m_checkpoints, file_path); return false;
} }
const auto& points = m_checkpoints.get_points(); 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))) 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<block> 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;
} }

View file

@ -78,7 +78,7 @@ namespace cryptonote
uint64_t already_generated_coins; 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); } 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(uint64_t start_index, uint64_t end_index);
void print_blockchain_index(); void print_blockchain_index();
void print_blockchain_outs(const std::string& file); 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: private:
typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index; typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
@ -215,6 +216,8 @@ namespace cryptonote
std::atomic<bool> m_is_in_checkpoint_zone; std::atomic<bool> m_is_in_checkpoint_zone;
std::atomic<bool> m_is_blockchain_storing; std::atomic<bool> m_is_blockchain_storing;
bool m_enforce_dns_checkpoints;
bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain); bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
bool pop_block_from_blockchain(); bool pop_block_from_blockchain();
bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count); bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count);

View file

@ -29,6 +29,9 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "checkpoints_create.h" #include "checkpoints_create.h"
#include "common/dns_utils.h"
#include "include_base_utils.h"
#include <sstream>
#include "storages/portable_storage_template_helper.h" // epee json include #include "storages/portable_storage_template_helper.h" // epee json include
namespace cryptonote namespace cryptonote
@ -107,4 +110,51 @@ bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::strin
return true; 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 } // namespace cryptonote

View file

@ -42,5 +42,7 @@ namespace cryptonote
bool create_checkpoints(cryptonote::checkpoints& checkpoints); bool create_checkpoints(cryptonote::checkpoints& checkpoints);
bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath); 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 } // namespace cryptonote

View file

@ -54,7 +54,9 @@ namespace cryptonote
m_miner(this), m_miner(this),
m_miner_address(boost::value_initialized<account_public_address>()), m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false), 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); set_cryptonote_protocol(pprotocol);
} }
@ -69,21 +71,27 @@ namespace cryptonote
void core::set_checkpoints(checkpoints&& chk_pts) void core::set_checkpoints(checkpoints&& chk_pts)
{ {
m_blockchain_storage.set_checkpoints(std::move(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) void core::set_checkpoints_file_path(const std::string& path)
{ {
m_checkpoints_path = 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) 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); m_last_checkpoints_update = time(NULL);
} }
return res;
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
void core::init_options(boost::program_options::options_description& /*desc*/) void core::init_options(boost::program_options::options_description& /*desc*/)

View file

@ -95,6 +95,7 @@ namespace cryptonote
void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol);
void set_checkpoints(checkpoints&& chk_pts); void set_checkpoints(checkpoints&& chk_pts);
void set_checkpoints_file_path(const std::string& path); void set_checkpoints_file_path(const std::string& path);
void set_enforce_dns_checkpoints(bool enforce_dns);
bool get_pool_transactions(std::list<transaction>& txs); bool get_pool_transactions(std::list<transaction>& txs);
size_t get_pool_transactions_count(); size_t get_pool_transactions_count();
@ -122,9 +123,9 @@ namespace cryptonote
void set_target_blockchain_height(uint64_t target_blockchain_height); void set_target_blockchain_height(uint64_t target_blockchain_height);
uint64_t get_target_blockchain_height() const; uint64_t get_target_blockchain_height() const;
private: bool update_checkpoints();
void 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, 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_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block);
bool add_new_block(const block& b, block_verification_context& bvc); bool add_new_block(const block& b, block_verification_context& bvc);

View file

@ -69,6 +69,7 @@ namespace
, "Run on testnet. The wallet must be launched with --testnet flag." , "Run on testnet. The wallet must be launched with --testnet flag."
, false , false
}; };
const command_line::arg_descriptor<bool> 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) 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_log_level);
command_line::add_arg(desc_cmd_sett, arg_console); 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_testnet_on);
command_line::add_arg(desc_cmd_sett, arg_dns_checkpoints);
cryptonote::core::init_options(desc_cmd_sett); cryptonote::core::init_options(desc_cmd_sett);
cryptonote::core_rpc_server::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"); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize checkpoints");
boost::filesystem::path json(JSON_HASH_FILE_NAME); boost::filesystem::path json(JSON_HASH_FILE_NAME);
boost::filesystem::path checkpoint_json_hashfile_fullpath = data_dir / json; 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 //create objects and link them
cryptonote::core ccore(NULL); 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) { if (testnet_mode) {
LOG_PRINT_L0("Starting in testnet mode!"); LOG_PRINT_L0("Starting in testnet mode!");
} else { } else {
ccore.set_checkpoints(std::move(checkpoints)); ccore.set_checkpoints(std::move(checkpoints));
ccore.set_checkpoints_file_path(checkpoint_json_hashfile_fullpath.string()); 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<cryptonote::core> cprotocol(ccore, NULL); cryptonote::t_cryptonote_protocol_handler<cryptonote::core> cprotocol(ccore, NULL);