Merge pull request #165
c0bdd51
Daemon should now exit on conflicting checkpoints (Thomas Winget)f0b4138
various changes to runtime checkpoint updating (Thomas Winget)7568f89
Fixed segfault with checkpoints loading (Thomas Winget)b261d92
DNS checkpoint updating added, and daemon flag to enforce them (Thomas Winget)30caebf
reload checkpoints file every ~hr and print if any fail (Thomas Winget)0e14491
updated DNSResolver/things that use it for DNSSEC (Thomas Winget)6f2c2e1
Adding an identical existing checkpoint should not error (Thomas Winget)
This commit is contained in:
commit
1cf22b27a6
14 changed files with 425 additions and 97 deletions
|
@ -118,9 +118,11 @@ DNSResolver::~DNSResolver()
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> DNSResolver::get_ipv4(const std::string& url)
|
||||
std::vector<std::string> DNSResolver::get_ipv4(const std::string& url, bool& dnssec_available, bool& dnssec_valid)
|
||||
{
|
||||
std::vector<std::string> 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<std::string> DNSResolver::get_ipv4(const std::string& url)
|
|||
return addresses;
|
||||
}
|
||||
|
||||
std::vector<std::string> DNSResolver::get_ipv6(const std::string& url)
|
||||
std::vector<std::string> DNSResolver::get_ipv6(const std::string& url, bool& dnssec_available, bool& dnssec_valid)
|
||||
{
|
||||
std::vector<std::string> 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<std::string> DNSResolver::get_ipv6(const std::string& url)
|
|||
return addresses;
|
||||
}
|
||||
|
||||
std::vector<std::string> DNSResolver::get_txt_record(const std::string& url)
|
||||
std::vector<std::string> DNSResolver::get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid)
|
||||
{
|
||||
std::vector<std::string> records;
|
||||
dnssec_available = false;
|
||||
dnssec_valid = false;
|
||||
char urlC[1000]; // waaaay too big, but just in case...
|
||||
|
||||
strncpy(urlC, url.c_str(), 999);
|
||||
|
|
|
@ -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<std::string> get_ipv4(const std::string& url);
|
||||
std::vector<std::string> 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<std::string> get_ipv6(const std::string& url);
|
||||
std::vector<std::string> 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<std::string> get_txt_record(const std::string& url);
|
||||
std::vector<std::string> get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid);
|
||||
|
||||
/**
|
||||
* @brief Gets the singleton instance of DNSResolver
|
||||
|
|
|
@ -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;
|
||||
|
@ -441,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)
|
||||
{
|
||||
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--)
|
||||
{
|
||||
|
@ -1773,3 +1781,75 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont
|
|||
|
||||
return handle_block_to_main_chain(bl, id, bvc);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
void blockchain_storage::check_against_checkpoints(checkpoints& points, bool enforce)
|
||||
{
|
||||
const auto& pts = points.get_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())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_checkpoints.check_block(pt.first, get_block_hash(m_blocks[pt.first].bl)))
|
||||
{
|
||||
// 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<block> empty;
|
||||
rollback_blockchain_switching(empty, pt.first - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Checkpoint failed when adding new checkpoints, this could be very bad.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -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,6 +180,9 @@ 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 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:
|
||||
typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
|
||||
|
@ -214,6 +217,8 @@ namespace cryptonote
|
|||
std::atomic<bool> m_is_in_checkpoint_zone;
|
||||
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 pop_block_from_blockchain();
|
||||
bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -93,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 =
|
||||
|
@ -101,5 +107,21 @@ namespace cryptonote
|
|||
boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _2 ) ) );
|
||||
return highest->first;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
const std::map<uint64_t, crypto::hash>& checkpoints::get_points()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "cryptonote_basic_impl.h"
|
||||
|
||||
|
||||
|
@ -45,7 +46,8 @@ 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<uint64_t, crypto::hash>& get_points();
|
||||
bool check_for_conflicts(checkpoints& other);
|
||||
private:
|
||||
std::map<uint64_t, crypto::hash> m_points;
|
||||
};
|
||||
|
|
193
src/cryptonote_core/checkpoints_create.cpp
Normal file
193
src/cryptonote_core/checkpoints_create.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
// 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 "common/dns_utils.h"
|
||||
#include "include_base_utils.h"
|
||||
#include <sstream>
|
||||
#include <random>
|
||||
#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<t_hashline> 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<t_hashline>::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;
|
||||
}
|
||||
|
||||
bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints)
|
||||
{
|
||||
static const std::vector<std::string> dns_urls = { "checkpoints.moneropulse.se"
|
||||
, "checkpoints.moneropulse.org"
|
||||
, "checkpoints.moneropulse.net"
|
||||
, "checkpoints.moneropulse.co"
|
||||
};
|
||||
bool avail, valid;
|
||||
std::vector<std::string> records;
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<int> 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_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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
|
@ -36,77 +36,13 @@
|
|||
#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<t_hashline> 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);
|
||||
bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints);
|
||||
bool load_new_checkpoints(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<t_hashline>::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
|
||||
|
|
|
@ -41,6 +41,7 @@ using namespace epee;
|
|||
#include "cryptonote_config.h"
|
||||
#include "cryptonote_format_utils.h"
|
||||
#include "misc_language.h"
|
||||
#include <csignal>
|
||||
|
||||
DISABLE_VS_WARNINGS(4355)
|
||||
|
||||
|
@ -54,7 +55,10 @@ namespace cryptonote
|
|||
m_miner(this),
|
||||
m_miner_address(boost::value_initialized<account_public_address>()),
|
||||
m_starter_message_showed(false),
|
||||
m_target_blockchain_height(0)
|
||||
m_target_blockchain_height(0),
|
||||
m_checkpoints_path(""),
|
||||
m_last_dns_checkpoints_update(0),
|
||||
m_last_json_checkpoints_update(0)
|
||||
{
|
||||
set_cryptonote_protocol(pprotocol);
|
||||
}
|
||||
|
@ -71,6 +75,39 @@ namespace cryptonote
|
|||
m_blockchain_storage.set_checkpoints(std::move(chk_pts));
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
void core::set_checkpoints_file_path(const std::string& path)
|
||||
{
|
||||
m_checkpoints_path = path;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
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_dns_checkpoints_update >= 3600)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// if anything fishy happened getting new checkpoints, bring down the house
|
||||
if (!res)
|
||||
{
|
||||
graceful_exit();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
void core::init_options(boost::program_options::options_description& /*desc*/)
|
||||
{
|
||||
}
|
||||
|
@ -127,6 +164,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
|
||||
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");
|
||||
|
||||
|
@ -418,6 +459,10 @@ namespace cryptonote
|
|||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate)
|
||||
{
|
||||
// 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<block_verification_context>();
|
||||
if(block_blob.size() > get_max_block_size())
|
||||
{
|
||||
|
@ -545,4 +590,9 @@ namespace cryptonote
|
|||
uint64_t core::get_target_blockchain_height() const {
|
||||
return m_target_blockchain_height;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
void core::graceful_exit()
|
||||
{
|
||||
raise(SIGTERM);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
|
@ -92,6 +94,8 @@ 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<transaction>& txs);
|
||||
size_t get_pool_transactions_count();
|
||||
|
@ -119,6 +123,8 @@ namespace cryptonote
|
|||
void set_target_blockchain_height(uint64_t target_blockchain_height);
|
||||
uint64_t get_target_blockchain_height() const;
|
||||
|
||||
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);
|
||||
|
@ -139,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;
|
||||
|
@ -155,6 +162,10 @@ namespace cryptonote
|
|||
std::atomic<bool> m_starter_message_showed;
|
||||
|
||||
uint64_t m_target_blockchain_height;
|
||||
|
||||
std::string m_checkpoints_path;
|
||||
time_t m_last_dns_checkpoints_update;
|
||||
time_t m_last_json_checkpoints_update;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace
|
|||
, "Run on testnet. The wallet must be launched with --testnet flag."
|
||||
, 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)
|
||||
|
@ -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,16 +208,19 @@ 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());
|
||||
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());
|
||||
}
|
||||
|
||||
cryptonote::t_cryptonote_protocol_handler<cryptonote::core> cprotocol(ccore, NULL);
|
||||
|
|
|
@ -252,7 +252,9 @@ namespace nodetool
|
|||
// for some time yet.
|
||||
for (const std::string& addr_str : m_seed_nodes_list)
|
||||
{
|
||||
std::vector<std::string> addr_list = tools::DNSResolver::instance().get_ipv4(addr_str);
|
||||
// TODO: care about dnssec avail/valid
|
||||
bool avail, valid;
|
||||
std::vector<std::string> 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");
|
||||
|
|
|
@ -833,12 +833,17 @@ std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
|
|||
*/
|
||||
std::vector<std::string> 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<std::string> 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)
|
||||
|
|
|
@ -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,19 +92,22 @@ 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());
|
||||
}
|
||||
|
||||
TEST(DNSResolver, GetTXTRecord)
|
||||
{
|
||||
bool avail, valid;
|
||||
|
||||
std::vector<std::string> records = tools::DNSResolver::instance().get_txt_record("donate.monero.cc");
|
||||
std::vector<std::string> records = tools::DNSResolver::instance().get_txt_record("donate.monero.cc", avail, valid);
|
||||
|
||||
EXPECT_NE(0, records.size());
|
||||
|
||||
|
|
Loading…
Reference in a new issue