From 6f2c2e1c27254116f979ec244cf3383d2ea87fad Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Wed, 24 Sep 2014 18:43:03 -0400 Subject: [PATCH 1/7] Adding an identical existing checkpoint should not error For checkpoints being read at runtime to work correctly, the checkpoint add code needs to not return false if a checkpoint is added that already exists. In this case, instead return false if the checkpoint is for a height that already has a checkpoint and the hashes are different. --- src/cryptonote_core/checkpoints.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp index c76a23841..8e4144a0a 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_core/checkpoints.cpp @@ -44,8 +44,13 @@ namespace cryptonote { crypto::hash h = null_hash; bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h); - CHECK_AND_ASSERT_MES(r, false, "WRONG HASH IN CHECKPOINTS!!!"); - CHECK_AND_ASSERT_MES(0 == m_points.count(height), false, "WRONG HASH IN CHECKPOINTS!!!"); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse checkpoint hash string into binary representation!"); + + // return false if adding at a height we already have AND the hash is different + if (m_points.count(height)) + { + 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; return true; } From 0e1449135d9de9fd05c393c4c85364a7241d98c1 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Thu, 25 Sep 2014 00:14:22 -0400 Subject: [PATCH 2/7] updated DNSResolver/things that use it for DNSSEC Note: DNSResolver does not yet *use* DNSSEC, but rather this commit is preparation for including DNSSEC validation. The function in src/wallet/wallet2.cpp that uses DNSResolver still needs its parameters updated accordingly. --- src/common/dns_utils.cpp | 12 +++++++++--- src/common/dns_utils.h | 8 +++++--- src/wallet/wallet2.cpp | 13 +++++++++---- tests/unit_tests/dns_resolver.cpp | 26 +++++++++++++++++--------- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 346761e74..0376ab9a9 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -118,9 +118,11 @@ DNSResolver::~DNSResolver() } } -std::vector DNSResolver::get_ipv4(const std::string& url) +std::vector DNSResolver::get_ipv4(const std::string& url, bool& dnssec_available, bool& dnssec_valid) { std::vector addresses; + dnssec_available = false; + dnssec_valid = false; char urlC[1000]; // waaaay too big, but just in case... strncpy(urlC, url.c_str(), 999); @@ -148,9 +150,11 @@ std::vector DNSResolver::get_ipv4(const std::string& url) return addresses; } -std::vector DNSResolver::get_ipv6(const std::string& url) +std::vector DNSResolver::get_ipv6(const std::string& url, bool& dnssec_available, bool& dnssec_valid) { std::vector addresses; + dnssec_available = false; + dnssec_valid = false; char urlC[1000]; // waaaay too big, but just in case... strncpy(urlC, url.c_str(), 999); @@ -178,9 +182,11 @@ std::vector DNSResolver::get_ipv6(const std::string& url) return addresses; } -std::vector DNSResolver::get_txt_record(const std::string& url) +std::vector DNSResolver::get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid) { std::vector records; + dnssec_available = false; + dnssec_valid = false; char urlC[1000]; // waaaay too big, but just in case... strncpy(urlC, url.c_str(), 999); diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index dd6946dc4..9958e4ca2 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -71,9 +71,11 @@ public: * * @param url A string containing a URL to query for * + * @param dnssec_available + * * @return vector of strings containing ipv4 addresses */ - std::vector get_ipv4(const std::string& url); + std::vector get_ipv4(const std::string& url, bool& dnssec_available, bool& dnssec_valid); /** * @brief gets ipv6 addresses from DNS query @@ -85,7 +87,7 @@ public: * * @return vector of strings containing ipv6 addresses */ - std::vector get_ipv6(const std::string& url); + std::vector get_ipv6(const std::string& url, bool& dnssec_available, bool& dnssec_valid); /** * @brief gets all TXT records from a DNS query for the supplied URL; @@ -96,7 +98,7 @@ public: * @return A vector of strings containing a TXT record; or an empty vector */ // TODO: modify this to accomodate DNSSEC - std::vector get_txt_record(const std::string& url); + std::vector get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid); /** * @brief Gets the singleton instance of DNSResolver diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3161f3b16..6fb672798 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -833,12 +833,17 @@ std::vector> split_amounts( */ std::vector wallet2::addresses_from_url(const std::string& url, bool& dnssec_valid) { - // TODO: update this correctly once DNSResolver::get_txt_record() supports it. - dnssec_valid = false; - std::vector addresses; // get txt records - auto records = tools::DNSResolver::instance().get_txt_record(url); + bool dnssec_available, dnssec_isvalid; + auto records = tools::DNSResolver::instance().get_txt_record(url, dnssec_available, dnssec_isvalid); + + // TODO: update this to allow for conveying that dnssec was not available + if (dnssec_available && dnssec_isvalid) + { + dnssec_valid = true; + } + else dnssec_valid = false; // for each txt record, try to find a monero address in it. for (auto& rec : records) diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index 27e981ef1..defce2c11 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -37,13 +37,15 @@ TEST(DNSResolver, IPv4Success) { tools::DNSResolver resolver; - auto ips = resolver.get_ipv4("example.com"); + bool avail, valid; + + auto ips = resolver.get_ipv4("example.com", avail, valid); ASSERT_EQ(1, ips.size()); ASSERT_STREQ("93.184.216.119", ips[0].c_str()); - ips = tools::DNSResolver::instance().get_ipv4("example.com"); + ips = tools::DNSResolver::instance().get_ipv4("example.com", avail, valid); ASSERT_EQ(1, ips.size()); @@ -55,11 +57,13 @@ TEST(DNSResolver, IPv4Failure) // guaranteed by IANA/ICANN/RFC to be invalid tools::DNSResolver resolver; - auto ips = resolver.get_ipv4("example.invalid"); + bool avail, valid; + + auto ips = resolver.get_ipv4("example.invalid", avail, valid); ASSERT_EQ(0, ips.size()); - ips = tools::DNSResolver::instance().get_ipv4("example.invalid"); + ips = tools::DNSResolver::instance().get_ipv4("example.invalid", avail, valid); ASSERT_EQ(0, ips.size()); } @@ -68,13 +72,15 @@ TEST(DNSResolver, IPv6Success) { tools::DNSResolver resolver; - auto ips = resolver.get_ipv6("example.com"); + bool avail, valid; + + auto ips = resolver.get_ipv6("example.com", avail, valid); ASSERT_EQ(1, ips.size()); ASSERT_STREQ("2606:2800:220:6d:26bf:1447:1097:aa7", ips[0].c_str()); - ips = tools::DNSResolver::instance().get_ipv6("example.com"); + ips = tools::DNSResolver::instance().get_ipv6("example.com", avail, valid); ASSERT_EQ(1, ips.size()); @@ -86,11 +92,13 @@ TEST(DNSResolver, IPv6Failure) // guaranteed by IANA/ICANN/RFC to be invalid tools::DNSResolver resolver; - auto ips = resolver.get_ipv6("example.invalid"); + bool avail, valid; + + auto ips = resolver.get_ipv6("example.invalid", avail, valid); ASSERT_EQ(0, ips.size()); - ips = tools::DNSResolver::instance().get_ipv6("example.invalid"); + ips = tools::DNSResolver::instance().get_ipv6("example.invalid", avail, valid); ASSERT_EQ(0, ips.size()); } @@ -98,7 +106,7 @@ TEST(DNSResolver, IPv6Failure) TEST(DNSResolver, GetTXTRecord) { - std::vector records = tools::DNSResolver::instance().get_txt_record("donate.monero.cc"); + std::vector records = tools::DNSResolver::instance().get_txt_record("donate.monero.cc", avail, valid); EXPECT_NE(0, records.size()); From 30caebfce3a2445b809125a541cc9958b07d02f5 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Thu, 25 Sep 2014 01:15:28 -0400 Subject: [PATCH 3/7] reload checkpoints file every ~hr and print if any fail also some other minor bug squashing and code formatting --- src/cryptonote_core/blockchain_storage.cpp | 21 ++++ src/cryptonote_core/blockchain_storage.h | 1 + src/cryptonote_core/checkpoints.cpp | 6 ++ src/cryptonote_core/checkpoints.h | 3 +- src/cryptonote_core/checkpoints_create.cpp | 110 +++++++++++++++++++++ src/cryptonote_core/checkpoints_create.h | 74 +------------- src/cryptonote_core/cryptonote_core.cpp | 16 +++ src/cryptonote_core/cryptonote_core.h | 8 ++ src/daemon/daemon.cpp | 3 +- src/p2p/net_node.inl | 4 +- tests/unit_tests/dns_resolver.cpp | 1 + 11 files changed, 174 insertions(+), 73 deletions(-) create mode 100644 src/cryptonote_core/checkpoints_create.cpp diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index e664a39c5..90820232c 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -47,6 +47,7 @@ #include "common/boost_serialization_helper.h" #include "warnings.h" #include "crypto/hash.h" +#include "cryptonote_core/checkpoints_create.h" //#include "serialization/json_archive.h" using namespace cryptonote; @@ -1773,3 +1774,23 @@ 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) +{ + // if a path is supplied, updated checkpoints from json. + // if not, probably fetch from DNS TXT Records. + if (file_path.size() > 0) + { + cryptonote::load_checkpoints_from_json(m_checkpoints, file_path); + } + + const auto& points = m_checkpoints.get_points(); + + for (const auto& pt : points) + { + 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."); + } + } +} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 0f7295516..b89db1825 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -180,6 +180,7 @@ 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); private: typedef std::unordered_map blocks_by_id_index; diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp index 8e4144a0a..41b41fac5 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_core/checkpoints.cpp @@ -98,6 +98,7 @@ namespace cryptonote uint64_t checkpoint_height = it->first; return checkpoint_height < block_height; } + //--------------------------------------------------------------------------- uint64_t checkpoints::get_max_height() { std::map< uint64_t, crypto::hash >::const_iterator highest = @@ -106,5 +107,10 @@ namespace cryptonote boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _2 ) ) ); return highest->first; } + //--------------------------------------------------------------------------- + const std::map& checkpoints::get_points() + { + return m_points; + } } diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h index 3dee48682..60147864d 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_core/checkpoints.h @@ -30,6 +30,7 @@ #pragma once #include +#include #include "cryptonote_basic_impl.h" @@ -45,7 +46,7 @@ namespace cryptonote bool check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const; bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const; uint64_t get_max_height(); - + const std::map& get_points(); private: std::map m_points; }; diff --git a/src/cryptonote_core/checkpoints_create.cpp b/src/cryptonote_core/checkpoints_create.cpp new file mode 100644 index 000000000..ff927396e --- /dev/null +++ b/src/cryptonote_core/checkpoints_create.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "checkpoints_create.h" +#include "storages/portable_storage_template_helper.h" // epee json include + +namespace cryptonote +{ + +struct t_hashline +{ + uint64_t height; + std::string hash; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + KV_SERIALIZE(hash) + END_KV_SERIALIZE_MAP() +}; + +struct t_hash_json { + std::vector hashlines; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(hashlines) + END_KV_SERIALIZE_MAP() +}; + +bool create_checkpoints(cryptonote::checkpoints& checkpoints) +{ + ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); + ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381"); + ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d"); + ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876"); + ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2"); + ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25"); + ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6"); + ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c"); + ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3"); + ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"); + ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2"); + ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb"); + ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063"); + ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553"); + ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61"); + ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf"); + ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131"); + ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8"); + + return true; +} + +bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath) +{ + boost::system::error_code errcode; + if (! (boost::filesystem::exists(json_hashfile_fullpath, errcode))) + { + LOG_PRINT_L0("Blockchain checkpoints file not found"); + return true; + } + + LOG_PRINT_L0("Adding checkpoints from blockchain hashfile"); + + uint64_t prev_max_height = checkpoints.get_max_height(); + LOG_PRINT_L0("Hard-coded max checkpoint height is " << prev_max_height); + t_hash_json hashes; + epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath); + for (std::vector::const_iterator it = hashes.hashlines.begin(); it != hashes.hashlines.end(); ) + { + uint64_t height; + height = it->height; + if (height <= prev_max_height) { + LOG_PRINT_L0("ignoring checkpoint height " << height); + } else { + std::string blockhash = it->hash; + LOG_PRINT_L0("Adding checkpoint height " << height << ", hash=" << blockhash); + ADD_CHECKPOINT(height, blockhash); + } + ++it; + } + + return true; +} + +} // namespace cryptonote diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index 7512ddd95..8e7a58228 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -36,77 +36,11 @@ #define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(checkpoints.add_checkpoint(h, hash), false); #define JSON_HASH_FILE_NAME "checkpoints.json" -struct t_hashline +namespace cryptonote { - uint64_t height; - std::string hash; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(height) - KV_SERIALIZE(hash) - END_KV_SERIALIZE_MAP() -}; -struct t_hash_json { - std::vector hashlines; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(hashlines) - END_KV_SERIALIZE_MAP() -}; + bool create_checkpoints(cryptonote::checkpoints& checkpoints); -namespace cryptonote { - inline bool create_checkpoints(cryptonote::checkpoints& checkpoints) - { - ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); - ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381"); - ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d"); - ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876"); - ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2"); - ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25"); - ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6"); - ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c"); - ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3"); - ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"); - ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2"); - ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb"); - ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063"); - ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553"); - ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61"); - ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf"); - ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131"); - ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8"); + bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath); - return true; - } - - inline bool load_checkpoins_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath) - { - boost::system::error_code errcode; - if (! (boost::filesystem::exists(json_hashfile_fullpath, errcode))) - { - LOG_PRINT_L0("Blockchain checkpoints file not found"); - return true; - } - - LOG_PRINT_L0("Adding checkpoints from blockchain hashfile"); - - uint64_t prev_max_height = checkpoints.get_max_height(); - LOG_PRINT_L0("Hard-coded max checkpoint height is " << prev_max_height); - t_hash_json hashes; - epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath); - for (std::vector::const_iterator it = hashes.hashlines.begin(); it != hashes.hashlines.end(); ) - { - uint64_t height; - height = it->height; - if (height <= prev_max_height) { - LOG_PRINT_L0("ignoring checkpoint height " << height); - } else { - std::string blockhash = it->hash; - LOG_PRINT_L0("Adding checkpoint height " << height << ", hash=" << blockhash); - ADD_CHECKPOINT(height, blockhash); - } - ++it; - } - - return true; - } -} +} // namespace cryptonote diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 964d61e2f..7586421b3 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -69,6 +69,21 @@ 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() + { + if (time(NULL) - m_last_checkpoints_update >= 3600) + { + m_blockchain_storage.update_checkpoints(m_checkpoints_path); + m_last_checkpoints_update = time(NULL); + } } //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& /*desc*/) @@ -418,6 +433,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) { + update_checkpoints(); bvc = boost::value_initialized(); if(block_blob.size() > get_max_block_size()) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index ba2aed015..81eddc657 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -30,6 +30,8 @@ #pragma once +#include + #include #include @@ -92,6 +94,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); bool get_pool_transactions(std::list& txs); size_t get_pool_transactions_count(); @@ -120,6 +123,8 @@ namespace cryptonote uint64_t get_target_blockchain_height() const; private: + void update_checkpoints(); + 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); @@ -155,6 +160,9 @@ namespace cryptonote std::atomic m_starter_message_showed; uint64_t m_target_blockchain_height; + + std::string m_checkpoints_path; + time_t m_last_checkpoints_update; }; } diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 5c209482e..5e4e9c266 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -206,7 +206,7 @@ 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_checkpoins_from_json(checkpoints, checkpoint_json_hashfile_fullpath.string().c_str()); + 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 @@ -216,6 +216,7 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Starting in testnet mode!"); } else { ccore.set_checkpoints(std::move(checkpoints)); + ccore.set_checkpoints_file_path(checkpoint_json_hashfile_fullpath.string()); } cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 08fd1d1e6..2953bdb7a 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -252,7 +252,9 @@ namespace nodetool // for some time yet. for (const std::string& addr_str : m_seed_nodes_list) { - std::vector addr_list = tools::DNSResolver::instance().get_ipv4(addr_str); + // TODO: care about dnssec avail/valid + bool avail, valid; + std::vector addr_list = tools::DNSResolver::instance().get_ipv4(addr_str, avail, valid); for (const std::string& a : addr_list) { append_net_address(m_seed_nodes, a + ":18080"); diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index defce2c11..d4b0efe43 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -105,6 +105,7 @@ TEST(DNSResolver, IPv6Failure) TEST(DNSResolver, GetTXTRecord) { + bool avail, valid; std::vector records = tools::DNSResolver::instance().get_txt_record("donate.monero.cc", avail, valid); From b261d9207ba5cdc0334fab403204971f79b6ca03 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Fri, 26 Sep 2014 01:01:58 -0400 Subject: [PATCH 4/7] 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); From 7568f89c5559a667c53dfaa6d1bef27fe3b4ae86 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Sat, 27 Sep 2014 17:46:12 -0400 Subject: [PATCH 5/7] Fixed segfault with checkpoints loading Bounds checking on blockchain_storage' m_blocks.size() when validating against checkpoints. Also moved initial json & DNS checkpoints load to after blockchain init. --- src/cryptonote_core/blockchain_storage.cpp | 6 ++++++ src/cryptonote_core/cryptonote_core.cpp | 4 ++++ src/daemon/daemon.cpp | 2 -- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 5dfda08ba..1165a9035 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -1793,6 +1793,12 @@ bool blockchain_storage::update_checkpoints(const std::string& file_path) for (const auto& pt : points) { + // if the checkpoint is for a block we don't have yet, move on + if (pt.first >= m_blocks.size()) + { + continue; + } + if (!m_checkpoints.check_block(pt.first, get_block_hash(m_blocks[pt.first].bl))) { // if we're enforcing dns checkpoints, roll back to a couple of blocks before the checkpoint diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 1dbc7bf52..d14be252b 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -150,6 +150,10 @@ namespace cryptonote r = m_blockchain_storage.init(m_config_folder, testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); + // load json & DNS checkpoints, and verify them + // with respect to what blocks we already have + update_checkpoints(); + r = m_miner.init(vm, testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 8e915fb1d..da09acfd4 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -221,8 +221,6 @@ int main(int argc, char* argv[]) } 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); From f0b4138f1f6ecb86c971dd7edf72a92d54e22721 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Mon, 29 Sep 2014 16:30:47 -0400 Subject: [PATCH 6/7] various changes to runtime checkpoint updating json checkpoints will be checked every 10 minutes, dns every 60. json checkpoints always enforced, dns still with flag. conflicting checkpoints is hard fail, but soft if dns enforce flag not set and dns checkpoints are wonky. --- src/cryptonote_core/blockchain_storage.cpp | 53 ++++++++++++++++++---- src/cryptonote_core/blockchain_storage.h | 3 +- src/cryptonote_core/checkpoints.cpp | 11 +++++ src/cryptonote_core/checkpoints.h | 1 + src/cryptonote_core/checkpoints_create.cpp | 43 ++++++++++++++++-- src/cryptonote_core/cryptonote_core.cpp | 22 ++++++--- src/cryptonote_core/cryptonote_core.h | 3 +- 7 files changed, 113 insertions(+), 23 deletions(-) diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 1165a9035..6f1b4121c 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -1782,16 +1782,11 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont return handle_block_to_main_chain(bl, id, bvc); } //------------------------------------------------------------------ -bool blockchain_storage::update_checkpoints(const std::string& file_path) +void blockchain_storage::check_against_checkpoints(checkpoints& points, bool enforce) { - if (!cryptonote::load_new_checkpoints(m_checkpoints, file_path)) - { - return false; - } + const auto& pts = points.get_points(); - const auto& points = m_checkpoints.get_points(); - - for (const auto& pt : points) + for (const auto& pt : pts) { // if the checkpoint is for a block we don't have yet, move on if (pt.first >= m_blocks.size()) @@ -1801,8 +1796,8 @@ bool 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 we're enforcing dns checkpoints, roll back to a couple of blocks before the checkpoint - if (m_enforce_dns_checkpoints) + // if asked to enforce checkpoints, roll back to a couple of blocks before the checkpoint + if (enforce) { LOG_ERROR("Checkpoint failed when adding new checkpoints, rolling back!"); std::list empty; @@ -1814,8 +1809,46 @@ bool blockchain_storage::update_checkpoints(const std::string& file_path) } } } +} +//------------------------------------------------------------------ +// returns false if any of the checkpoints loading returns false. +// That should happen only if a checkpoint is added that conflicts +// with an existing checkpoint. +bool blockchain_storage::update_checkpoints(const std::string& file_path, bool check_dns) +{ + if (!cryptonote::load_checkpoints_from_json(m_checkpoints, file_path)) + { + return false; + } + + // if we're checking both dns and json, load checkpoints from dns. + // if we're not hard-enforcing dns checkpoints, handle accordingly + if (m_enforce_dns_checkpoints && check_dns) + { + if (!cryptonote::load_checkpoints_from_dns(m_checkpoints)) + { + return false; + } + } + else if (check_dns) + { + checkpoints dns_points; + cryptonote::load_checkpoints_from_dns(dns_points); + if (m_checkpoints.check_for_conflicts(dns_points)) + { + check_against_checkpoints(dns_points, false); + } + else + { + LOG_PRINT_L0("One or more checkpoints fetched from DNS conflicted with existing checkpoints!"); + } + } + + check_against_checkpoints(m_checkpoints, true); + 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 1ab49a7bc..08c007a4a 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -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); - bool update_checkpoints(const std::string& file_path); + void check_against_checkpoints(checkpoints& points, bool enforce); + bool update_checkpoints(const std::string& file_path, bool check_dns); void set_enforce_dns_checkpoints(bool enforce_checkpoints); private: diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp index 41b41fac5..25759792b 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_core/checkpoints.cpp @@ -113,4 +113,15 @@ namespace cryptonote return m_points; } + bool checkpoints::check_for_conflicts(checkpoints& other) + { + for (auto& pt : other.get_points()) + { + if (m_points.count(pt.first)) + { + CHECK_AND_ASSERT_MES(pt.second == m_points[pt.first], false, "Checkpoint at given height already exists, and hash for new checkpoint was different!"); + } + } + return true; + } } diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h index 60147864d..132917228 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_core/checkpoints.h @@ -47,6 +47,7 @@ namespace cryptonote bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const; uint64_t get_max_height(); const std::map& get_points(); + bool check_for_conflicts(checkpoints& other); private: std::map m_points; }; diff --git a/src/cryptonote_core/checkpoints_create.cpp b/src/cryptonote_core/checkpoints_create.cpp index b81353539..808bc46a7 100644 --- a/src/cryptonote_core/checkpoints_create.cpp +++ b/src/cryptonote_core/checkpoints_create.cpp @@ -32,6 +32,7 @@ #include "common/dns_utils.h" #include "include_base_utils.h" #include +#include #include "storages/portable_storage_template_helper.h" // epee json include namespace cryptonote @@ -110,15 +111,47 @@ 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 load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints) { + static const std::vector dns_urls = { "checkpoints.moneropulse.se" + , "checkpoints.moneropulse.org" + , "checkpoints.moneropulse.net" + , "checkpoints.moneropulse.co" + }; bool avail, valid; - auto records = tools::DNSResolver::instance().get_txt_record(url, avail, valid); + std::vector records; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis(0, dns_urls.size() - 1); + int first_index = dis(gen); + + int cur_index = first_index; + do + { + records = tools::DNSResolver::instance().get_txt_record(dns_urls[cur_index], avail, valid); + if (records.size() == 0 || (avail && !valid)) + { + cur_index++; + if (cur_index == dns_urls.size()) + { + cur_index = 0; + } + continue; + } + break; + } while (cur_index != first_index); + + if (records.size() == 0) + { + LOG_PRINT_L1("Fetching checkpoints from DNS TXT records failed, no TXT records available."); + return true; + } if (avail && !valid) { - LOG_ERROR("DNSSEC present and failed validation for query to" << url); - return false; + LOG_PRINT_L0("DNSSEC present and failed validation for query last url, and all other urls either failed validation or returned no records"); + return true; } for (auto& record : records) @@ -154,7 +187,7 @@ bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, const std:: 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")); + return (load_checkpoints_from_json(checkpoints, json_hashfile_fullpath) && load_checkpoints_from_dns(checkpoints)); } } // namespace cryptonote diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index d14be252b..c83e6e0a0 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -56,7 +56,8 @@ namespace cryptonote m_starter_message_showed(false), m_target_blockchain_height(0), m_checkpoints_path(""), - m_last_checkpoints_update(0) + m_last_dns_checkpoints_update(0), + m_last_json_checkpoints_update(0) { set_cryptonote_protocol(pprotocol); } @@ -86,10 +87,16 @@ namespace cryptonote bool core::update_checkpoints() { bool res = true; - if (time(NULL) - m_last_checkpoints_update >= 3600) + if (time(NULL) - m_last_dns_checkpoints_update >= 3600) { - res = m_blockchain_storage.update_checkpoints(m_checkpoints_path); - m_last_checkpoints_update = time(NULL); + res = m_blockchain_storage.update_checkpoints(m_checkpoints_path, true); + m_last_dns_checkpoints_update = time(NULL); + m_last_json_checkpoints_update = time(NULL); + } + else if (time(NULL) - m_last_json_checkpoints_update >= 600) + { + res = m_blockchain_storage.update_checkpoints(m_checkpoints_path, false); + m_last_json_checkpoints_update = time(NULL); } return res; } @@ -152,7 +159,7 @@ namespace cryptonote // load json & DNS checkpoints, and verify them // with respect to what blocks we already have - update_checkpoints(); + CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); r = m_miner.init(vm, testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); @@ -445,7 +452,10 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) { - update_checkpoints(); + // load json & DNS checkpoints every 10min/hour respectively, + // and verify them with respect to what blocks we already have + CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); + bvc = boost::value_initialized(); if(block_blob.size() > get_max_block_size()) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index b3bfddff3..0c697df80 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -163,7 +163,8 @@ namespace cryptonote uint64_t m_target_blockchain_height; std::string m_checkpoints_path; - time_t m_last_checkpoints_update; + time_t m_last_dns_checkpoints_update; + time_t m_last_json_checkpoints_update; }; } From c0bdd511c3309e78488051f6a21b0b30ed25af44 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 30 Sep 2014 15:24:43 -0400 Subject: [PATCH 7/7] Daemon should now exit on conflicting checkpoints If the "enforce DNS checkpoints" flag is not enabled, it should not exit if DNS checkpoints conflict with the others, but should still print a warning to the user. --- src/cryptonote_core/cryptonote_core.cpp | 12 ++++++++++++ src/cryptonote_core/cryptonote_core.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c83e6e0a0..d9d8a7faf 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -41,6 +41,7 @@ using namespace epee; #include "cryptonote_config.h" #include "cryptonote_format_utils.h" #include "misc_language.h" +#include DISABLE_VS_WARNINGS(4355) @@ -98,6 +99,12 @@ namespace cryptonote res = m_blockchain_storage.update_checkpoints(m_checkpoints_path, false); m_last_json_checkpoints_update = time(NULL); } + + // if anything fishy happened getting new checkpoints, bring down the house + if (!res) + { + graceful_exit(); + } return res; } //----------------------------------------------------------------------------------- @@ -583,4 +590,9 @@ namespace cryptonote uint64_t core::get_target_blockchain_height() const { return m_target_blockchain_height; } + //----------------------------------------------------------------------------------------------- + void core::graceful_exit() + { + raise(SIGTERM); + } } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 0c697df80..10522451c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -145,6 +145,7 @@ namespace cryptonote bool handle_command_line(const boost::program_options::variables_map& vm, bool testnet); bool on_update_blocktemplate_interval(); bool check_tx_inputs_keyimages_diff(const transaction& tx); + void graceful_exit(); tx_memory_pool m_mempool;