Merge dev into upstream

This commit is contained in:
Doyle 2019-05-01 16:01:17 +10:00
parent fbf15ec6ac
commit cc0d51078c
34 changed files with 1034 additions and 617 deletions

28
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,28 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behaviour**
What is the expected behaviour if the steps above are followed:
**Current Behaviour**
What is the current behaviour if the steps to reproduce are followed:
**Screenshots or Logs**
If applicable, add screenshots or logs to help explain your problem.
**Additional Information (please complete the following information):**
- Operating System: [e.g. Ubuntu 16.04, Windows 10]
- Local or Remote node
- Version of Loki daemon, or most recent commit hash.

View File

@ -768,7 +768,6 @@ int main(int argc, char* argv[])
try
{
core.disable_dns_checkpoints(true);
#if defined(PER_BLOCK_CHECKPOINT)
const GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
#else

View File

@ -35,7 +35,9 @@
#include "string_tools.h"
#include "storages/portable_storage_template_helper.h" // epee json include
#include "serialization/keyvalue_serialization.h"
#include "cryptonote_core/service_node_rules.h"
#include <vector>
#include "syncobj.h"
using namespace epee;
@ -69,23 +71,135 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
//---------------------------------------------------------------------------
checkpoints::checkpoints()
{
}
//---------------------------------------------------------------------------
bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str)
{
crypto::hash h = crypto::null_hash;
bool r = epee::string_tools::hex_to_pod(hash_str, h);
bool r = epee::string_tools::hex_to_pod(hash_str, h);
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))
CRITICAL_REGION_LOCAL(m_lock);
if (m_points.count(height)) // return false if adding at a height we already have AND the hash is different
{
CHECK_AND_ASSERT_MES(h == m_points[height], false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
checkpoint_t const &checkpoint = m_points[height];
crypto::hash const &curr_hash = checkpoint.block_hash;
CHECK_AND_ASSERT_MES(h == curr_hash, false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
}
m_points[height] = h;
else
{
checkpoint_t checkpoint = {};
checkpoint.type = checkpoint_type::predefined_or_dns;
checkpoint.block_hash = h;
m_points[height] = checkpoint;
}
return true;
}
//---------------------------------------------------------------------------
static bool add_vote_if_unique(checkpoint_t &checkpoint, service_nodes::checkpoint_vote const &vote)
{
CHECK_AND_ASSERT_MES(checkpoint.block_hash == vote.block_hash,
false,
"Developer error: Add vote if unique should only be called when the vote hash and checkpoint hash match");
// TODO(doyle): Factor this out, a similar function exists in service node deregister
CHECK_AND_ASSERT_MES(vote.voters_quorum_index < service_nodes::QUORUM_SIZE,
false,
"Vote is indexing out of bounds");
const auto signature_it = std::find_if(checkpoint.signatures.begin(), checkpoint.signatures.end(), [&vote](service_nodes::voter_to_signature const &check) {
return vote.voters_quorum_index == check.quorum_index;
});
if (signature_it == checkpoint.signatures.end())
{
service_nodes::voter_to_signature new_voter_to_signature = {};
new_voter_to_signature.quorum_index = vote.voters_quorum_index;
new_voter_to_signature.signature = vote.signature;
checkpoint.signatures.push_back(new_voter_to_signature);
return true;
}
return false;
}
//---------------------------------------------------------------------------
bool checkpoints::add_checkpoint_vote(service_nodes::checkpoint_vote const &vote)
{
// TODO(doyle): Always accept votes. Later on, we should only accept votes up
// to a certain point, so that eventually once a certain number of blocks
// have transpired, we can go through all our "seen" votes and deregister
// anyone who has not participated in voting by that point.
#if 0
uint64_t newest_checkpoint_height = get_max_height();
if (vote.block_height < newest_checkpoint_height)
return true;
#endif
CRITICAL_REGION_LOCAL(m_lock);
std::array<int, service_nodes::QUORUM_SIZE> unique_vote_set = {};
auto pre_existing_checkpoint_it = m_points.find(vote.block_height);
if (pre_existing_checkpoint_it != m_points.end())
{
checkpoint_t &checkpoint = pre_existing_checkpoint_it->second;
if (checkpoint.type == checkpoint_type::predefined_or_dns)
return true;
if (checkpoint.block_hash == vote.block_hash)
{
bool added = add_vote_if_unique(checkpoint, vote);
return added;
}
for (service_nodes::voter_to_signature const &vote_to_sig : checkpoint.signatures)
{
if (vote_to_sig.quorum_index > unique_vote_set.size()) // TODO(doyle): Sanity check, remove once universal vote verifying function is written
return false;
++unique_vote_set[vote_to_sig.quorum_index];
}
}
// TODO(doyle): Double work. Factor into a generic vote checker as we already have one in service node deregister
std::vector<checkpoint_t> &candidate_checkpoints = m_staging_points[vote.block_height];
std::vector<checkpoint_t>::iterator curr_checkpoint = candidate_checkpoints.end();
for (auto it = candidate_checkpoints.begin(); it != candidate_checkpoints.end(); it++)
{
checkpoint_t const &checkpoint = *it;
if (checkpoint.block_hash == vote.block_hash)
curr_checkpoint = it;
for (service_nodes::voter_to_signature const &vote_to_sig : checkpoint.signatures)
{
if (vote_to_sig.quorum_index > unique_vote_set.size())
return false;
if (++unique_vote_set[vote_to_sig.quorum_index] > 1)
{
// NOTE: Voter is trying to vote twice
return false;
}
}
}
if (curr_checkpoint == candidate_checkpoints.end())
{
checkpoint_t new_checkpoint = {};
new_checkpoint.type = checkpoint_type::service_node;
new_checkpoint.block_hash = vote.block_hash;
candidate_checkpoints.push_back(new_checkpoint);
curr_checkpoint = (candidate_checkpoints.end() - 1);
}
if (add_vote_if_unique(*curr_checkpoint, vote))
{
if (curr_checkpoint->signatures.size() > service_nodes::MIN_VOTES_TO_CHECKPOINT) // TODO(doyle): Quorum SuperMajority variable
{
m_points[vote.block_height] = *curr_checkpoint;
candidate_checkpoints.erase(curr_checkpoint);
}
}
return true;
}
//---------------------------------------------------------------------------
@ -94,67 +208,80 @@ namespace cryptonote
return !m_points.empty() && (height <= (--m_points.end())->first);
}
//---------------------------------------------------------------------------
bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const
bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool* is_a_checkpoint) const
{
auto it = m_points.find(height);
is_a_checkpoint = it != m_points.end();
if(!is_a_checkpoint)
bool found = (it != m_points.end());
if (is_a_checkpoint) *is_a_checkpoint = found;
if(!found)
return true;
if(it->second == h)
{
MINFO("CHECKPOINT PASSED FOR HEIGHT " << height << " " << h);
return true;
}else
{
MWARNING("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH: " << it->second << ", FETCHED HASH: " << h);
return false;
}
checkpoint_t const &checkpoint = it->second;
bool result = checkpoint.block_hash == h;
if (result) MINFO ("CHECKPOINT PASSED FOR HEIGHT " << height << " " << h);
else MWARNING("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH " << checkpoint.block_hash << "FETCHED HASH: " << h);
return result;
}
//---------------------------------------------------------------------------
bool checkpoints::check_block(uint64_t height, const crypto::hash& h) const
{
bool ignored;
return check_block(height, h, ignored);
}
//---------------------------------------------------------------------------
//FIXME: is this the desired behavior?
bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const
{
if (0 == block_height)
return false;
auto it = m_points.upper_bound(blockchain_height);
// Is blockchain_height before the first checkpoint?
if (it == m_points.begin())
CRITICAL_REGION_LOCAL(m_lock);
std::map<uint64_t, checkpoint_t>::const_iterator it = m_points.upper_bound(blockchain_height);
if (it == m_points.begin()) // Is blockchain_height before the first checkpoint?
return true;
--it;
uint64_t checkpoint_height = it->first;
return checkpoint_height < block_height;
--it; // move the iterator to the first checkpoint that is <= my height
uint64_t sentinel_reorg_height = it->first;
if (it->second.type == checkpoint_type::service_node)
{
// NOTE: The current checkpoint is a service node checkpoint. Go back
// 1 checkpoint, which will either be another service node checkpoint or
// a predefined one.
// If it's a service node checkpoint, this is the 2nd newest checkpoint,
// so we can't reorg past that height. If it's predefined, that's ok as
// well, we can't reorg past that height so irrespective, always accept
// the height of this next checkpoint.
if (it == m_points.begin())
{
return true; // NOTE: Only one service node checkpoint recorded, we can override this checkpoint.
}
else
{
--it;
sentinel_reorg_height = it->first;
}
}
bool result = sentinel_reorg_height < block_height;
return result;
}
//---------------------------------------------------------------------------
uint64_t checkpoints::get_max_height() const
{
std::map< uint64_t, crypto::hash >::const_iterator highest =
std::max_element( m_points.begin(), m_points.end(),
( boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _1) <
boost::bind(&std::map< uint64_t, crypto::hash >::value_type::first, _2 ) ) );
return highest->first;
uint64_t result = 0;
if (m_points.size() > 0)
{
auto last_it = m_points.rbegin();
result = last_it->first;
}
return result;
}
//---------------------------------------------------------------------------
const std::map<uint64_t, crypto::hash>& checkpoints::get_points() const
{
return m_points;
}
bool checkpoints::check_for_conflicts(const checkpoints& other) const
{
for (auto& pt : other.get_points())
{
if (m_points.count(pt.first))
{
CHECK_AND_ASSERT_MES(pt.second == m_points.at(pt.first), false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
checkpoint_t const &our_checkpoint = m_points.at(pt.first);
checkpoint_t const &their_checkpoint = pt.second;
CHECK_AND_ASSERT_MES(our_checkpoint.block_hash == their_checkpoint.block_hash, false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
}
}
return true;
@ -212,33 +339,16 @@ namespace cryptonote
uint64_t height;
height = it->height;
if (height <= prev_max_height) {
LOG_PRINT_L1("ignoring checkpoint height " << height);
LOG_PRINT_L1("ignoring checkpoint height " << height);
} else {
std::string blockhash = it->hash;
LOG_PRINT_L1("Adding checkpoint height " << height << ", hash=" << blockhash);
ADD_CHECKPOINT(height, blockhash);
std::string blockhash = it->hash;
LOG_PRINT_L1("Adding checkpoint height " << height << ", hash=" << blockhash);
ADD_CHECKPOINT(height, blockhash);
}
++it;
}
return true;
}
bool checkpoints::load_checkpoints_from_dns(network_type nettype)
{
return true;
}
bool checkpoints::load_new_checkpoints(const std::string &json_hashfile_fullpath, network_type nettype, bool dns)
{
bool result;
result = load_checkpoints_from_json(json_hashfile_fullpath);
if (dns)
{
result &= load_checkpoints_from_dns(nettype);
}
return result;
}
}

View File

@ -30,16 +30,32 @@
#pragma once
#include <map>
#include <vector>
#include "misc_log_ex.h"
#include "crypto/hash.h"
#include "cryptonote_config.h"
#include "cryptonote_core/service_node_deregister.h"
#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false);
#define JSON_HASH_FILE_NAME "checkpoints.json"
namespace cryptonote
{
struct Blockchain;
enum struct checkpoint_type
{
predefined_or_dns,
service_node,
};
struct checkpoint_t
{
checkpoint_type type;
crypto::hash block_hash;
std::vector<service_nodes::voter_to_signature> signatures; // Only service node checkpoints use signatures
};
/**
* @brief A container for blockchain checkpoints
*
@ -50,12 +66,6 @@ namespace cryptonote
class checkpoints
{
public:
/**
* @brief default constructor
*/
checkpoints();
/**
* @brief adds a checkpoint to the container
*
@ -68,6 +78,8 @@ namespace cryptonote
*/
bool add_checkpoint(uint64_t height, const std::string& hash_str);
bool add_checkpoint_vote(service_nodes::checkpoint_vote const &vote);
/**
* @brief checks if there is a checkpoint in the future
*
@ -90,23 +102,19 @@ namespace cryptonote
*
* @param height the height to be checked
* @param h the hash to be checked
* @param is_a_checkpoint return-by-reference if there is a checkpoint at the given height
* @param blockchain the blockchain to query ancestor blocks from the current height
* @param is_a_checkpoint optional return-by-pointer if there is a checkpoint at the given height
*
* @return true if there is no checkpoint at the given height,
* true if the passed parameters match the stored checkpoint,
* false otherwise
*/
bool check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const;
/**
* @overload
*/
bool check_block(uint64_t height, const crypto::hash& h) const;
bool check_block(uint64_t height, const crypto::hash& h, bool *is_a_checkpoint = nullptr) const;
/**
* @brief checks if alternate chain blocks should be kept for a given height
*
* this basically says if the blockchain is smaller than the first
*m this basically says if the blockchain is smaller than the first
* checkpoint then alternate blocks are allowed. Alternatively, if the
* last checkpoint *before* the end of the current chain is also before
* the block to be added, then this is fine.
@ -131,7 +139,7 @@ namespace cryptonote
*
* @return a const reference to the checkpoints container
*/
const std::map<uint64_t, crypto::hash>& get_points() const;
std::map<uint64_t, checkpoint_t> get_points() const { return m_points; };
/**
* @brief checks if our checkpoints container conflicts with another
@ -153,20 +161,6 @@ namespace cryptonote
*/
bool init_default_checkpoints(network_type nettype);
/**
* @brief load new checkpoints
*
* Loads new checkpoints from the specified json file, as well as
* (optionally) from DNS.
*
* @param json_hashfile_fullpath path to the json checkpoints file
* @param nettype network type
* @param dns whether or not to load DNS checkpoints
*
* @return true if loading successful and no conflicts
*/
bool load_new_checkpoints(const std::string &json_hashfile_fullpath, network_type nettype=MAINNET, bool dns=true);
/**
* @brief load new checkpoints from json
*
@ -176,17 +170,10 @@ namespace cryptonote
*/
bool load_checkpoints_from_json(const std::string &json_hashfile_fullpath);
/**
* @brief load new checkpoints from DNS
*
* @param nettype network type
*
* @return true if loading successful and no conflicts
*/
bool load_checkpoints_from_dns(network_type nettype = MAINNET);
private:
std::map<uint64_t, crypto::hash> m_points; //!< the checkpoints container
std::unordered_map<uint64_t, std::vector<checkpoint_t>> m_staging_points; // Incomplete service node checkpoints being voted on
std::map<uint64_t, checkpoint_t> m_points; //!< the checkpoints container
mutable epee::critical_section m_lock;
};
}

View File

@ -265,6 +265,7 @@ namespace cryptonote
network_version_9_service_nodes, // Proof Of Stake w/ Service Nodes
network_version_10_bulletproofs, // Bulletproofs, Service Node Grace Registration Period, Batched Governance
network_version_11_infinite_staking,
network_version_12_checkpointing,
network_version_count,
};

View File

@ -126,7 +126,7 @@ static const hard_fork_record stagenet_hard_forks[] =
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool, service_nodes::service_node_list& service_node_list, service_nodes::deregister_vote_pool& deregister_vote_pool):
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
m_long_term_effective_median_block_weight(0),
m_long_term_block_weights_cache_tip_hash(crypto::null_hash),
@ -136,6 +136,7 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool, service_nodes::service_node_list
m_deregister_vote_pool(deregister_vote_pool),
m_btc_valid(false)
{
m_checkpoint_pool.reserve(service_nodes::QUORUM_SIZE * 4 /*blocks*/);
LOG_PRINT_L3("Blockchain::" << __func__);
}
//------------------------------------------------------------------
@ -1742,8 +1743,8 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return false;
}
bool is_a_checkpoint;
if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
bool is_a_checkpoint = false;
if(!m_checkpoints.check_block(bei.height, id, &is_a_checkpoint))
{
LOG_ERROR("CHECKPOINT VALIDATION FAILED");
bvc.m_verifivation_failed = true;
@ -3076,14 +3077,14 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false;
}
const std::shared_ptr<const service_nodes::quorum_state> quorum_state = m_service_node_list.get_quorum_state(deregister.block_height);
if (!quorum_state)
const std::shared_ptr<const service_nodes::quorum_uptime_proof> uptime_quorum = m_service_node_list.get_uptime_quorum(deregister.block_height);
if (!uptime_quorum)
{
MERROR_VER("Deregister TX could not get quorum for height: " << deregister.block_height);
return false;
}
if (!service_nodes::deregister_vote::verify_deregister(nettype(), deregister, tvc.m_vote_ctx, *quorum_state))
if (!service_nodes::deregister_vote::verify_deregister(nettype(), deregister, tvc.m_vote_ctx, *uptime_quorum))
{
tvc.m_verifivation_failed = true;
MERROR_VER("tx " << get_transaction_hash(tx) << ": deregister tx could not be completely verified reason: " << print_vote_verification_context(tvc.m_vote_ctx));
@ -3146,15 +3147,15 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
continue;
}
const std::shared_ptr<const service_nodes::quorum_state> existing_deregister_quorum_state = m_service_node_list.get_quorum_state(existing_deregister.block_height);
if (!existing_deregister_quorum_state)
const std::shared_ptr<const service_nodes::quorum_uptime_proof> existing_uptime_quorum = m_service_node_list.get_uptime_quorum(existing_deregister.block_height);
if (!existing_uptime_quorum)
{
MERROR_VER("could not get quorum state for recent deregister tx");
MERROR_VER("could not get uptime quorum for recent deregister tx");
continue;
}
if (existing_deregister_quorum_state->nodes_to_test[existing_deregister.service_node_index] ==
quorum_state->nodes_to_test[deregister.service_node_index])
if (existing_uptime_quorum->nodes_to_test[existing_deregister.service_node_index] ==
uptime_quorum->nodes_to_test[deregister.service_node_index])
{
MERROR_VER("Already seen this deregister tx (aka double spend)");
tvc.m_double_spend = true;
@ -3672,6 +3673,7 @@ leave:
bvc.m_verifivation_failed = true;
goto leave;
}
}
TIME_MEASURE_FINISH(longhash_calculating_time);
@ -4056,6 +4058,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
//------------------------------------------------------------------
bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
{
LOG_PRINT_L3("Blockchain::" << __func__);
crypto::hash id = get_block_hash(bl);
CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
@ -4090,28 +4093,25 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
// caller decide course of action.
void Blockchain::check_against_checkpoints(const checkpoints& points, bool enforce)
{
const auto& pts = points.get_points();
bool stop_batch;
CRITICAL_REGION_LOCAL(m_blockchain_lock);
stop_batch = m_db->batch_start();
const uint64_t blockchain_height = m_db->height();
for (const auto& pt : pts)
{
// if the checkpoint is for a block we don't have yet, move on
if (pt.first >= blockchain_height)
{
continue;
}
bool stop_batch = m_db->batch_start();
if (!points.check_block(pt.first, m_db->get_block_hash_from_height(pt.first)))
for (const auto& checkpoint_it : points.get_points())
{
uint64_t block_height = checkpoint_it.first;
checkpoint_t const &checkpoint = checkpoint_it.second;
if (block_height >= m_db->height()) // if the checkpoint is for a block we don't have yet, move on
break;
if (!points.check_block(block_height, m_db->get_block_hash_from_height(block_height), nullptr))
{
// if asked to enforce checkpoints, roll back to a couple of blocks before the checkpoint
if (enforce)
{
LOG_ERROR("Local blockchain failed to pass a checkpoint, rolling back!");
std::list<block> empty;
rollback_blockchain_switching(empty, pt.first - 2);
rollback_blockchain_switching(empty, block_height- 2);
}
else
{
@ -4119,6 +4119,7 @@ void Blockchain::check_against_checkpoints(const checkpoints& points, bool enfor
}
}
}
if (stop_batch)
m_db->batch_stop();
}
@ -4126,46 +4127,28 @@ void Blockchain::check_against_checkpoints(const checkpoints& points, bool enfor
// 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::update_checkpoints(const std::string& file_path, bool check_dns)
bool Blockchain::update_checkpoints(const std::string& file_path)
{
if (!m_checkpoints.load_checkpoints_from_json(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 && !m_offline)
{
if (!m_checkpoints.load_checkpoints_from_dns())
{
return false;
}
}
else if (check_dns && !m_offline)
{
checkpoints dns_points;
dns_points.load_checkpoints_from_dns();
if (m_checkpoints.check_for_conflicts(dns_points))
{
check_against_checkpoints(dns_points, false);
}
else
{
MERROR("One or more checkpoints fetched from DNS conflicted with existing checkpoints!");
}
}
return false;
check_against_checkpoints(m_checkpoints, true);
return true;
}
//------------------------------------------------------------------
void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints)
bool Blockchain::add_checkpoint_vote(service_nodes::checkpoint_vote const &vote)
{
m_enforce_dns_checkpoints = enforce_checkpoints;
}
crypto::hash const canonical_block_hash = get_block_id_by_height(vote.block_height);
if (vote.block_hash != canonical_block_hash)
{
// NOTE: Vote is not for a block on the canonical chain, check if it's part
// of an alternative chain
if (m_alternative_chains.find(vote.block_hash) == m_alternative_chains.end())
return false;
}
m_checkpoints.add_checkpoint_vote(vote);
return true;
}
//------------------------------------------------------------------
void Blockchain::block_longhash_worker(uint64_t height, const epee::span<const block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const
{
@ -4174,7 +4157,7 @@ void Blockchain::block_longhash_worker(uint64_t height, const epee::span<const b
for (const auto & block : blocks)
{
if (m_cancel)
break;
break;
crypto::hash id = get_block_hash(block);
crypto::hash pow = get_block_longhash(block, height++);
map.emplace(id, pow);
@ -4546,7 +4529,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
waiter.wait(&tpool);
if (m_cancel)
return false;
return false;
for (const auto & map : maps)
{
@ -4587,11 +4570,11 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs);
#define SCAN_TABLE_QUIT(m) \
do { \
MERROR_VER(m) ;\
m_scan_table.clear(); \
return false; \
} while(0); \
do { \
MERROR_VER(m) ;\
m_scan_table.clear(); \
return false; \
} while(0); \
// generate sorted tables for all amounts and absolute offsets
size_t tx_index = 0, block_index = 0;

View File

@ -92,6 +92,23 @@ namespace cryptonote
class Blockchain
{
public:
void debug__print_checkpoints()
{
const std::map<uint64_t, checkpoint_t> &checkpoint_map = m_checkpoints.get_points();
if (checkpoint_map.empty())
{
std::cout << "Checkpoint: None available" << std::endl;
}
else
{
for (auto const &it : checkpoint_map)
{
checkpoint_t const &checkpoint = it.second;
std::cout << "Checkpoint [" << it.first << "]" << ((checkpoint.type == checkpoint_type::service_node) ? "Service Node" : "Predefined") << std::endl;
}
}
}
/**
* @brief Now-defunct (TODO: remove) struct from in-memory blockchain
*/
@ -747,15 +764,23 @@ namespace cryptonote
void set_enforce_dns_checkpoints(bool enforce);
/**
* @brief loads new checkpoints from a file and optionally from DNS
* @brief loads new checkpoints from a file
*
* @param file_path the path of the file to look for and load checkpoints from
* @param check_dns whether or not to check for new DNS-based checkpoints
*
* @return false if any enforced checkpoint type fails to load, otherwise true
*/
bool update_checkpoints(const std::string& file_path, bool check_dns);
bool update_checkpoints(const std::string& file_path);
// TODO(doyle): CHECKPOINTING(doyle):
struct service_node_checkpoint_pool_entry
{
uint64_t height;
std::vector<service_nodes::checkpoint_vote> votes;
};
std::vector<service_node_checkpoint_pool_entry> m_checkpoint_pool;
bool add_checkpoint_vote(service_nodes::checkpoint_vote const &vote);
// user options, must be called before calling init()
@ -1136,8 +1161,6 @@ namespace cryptonote
std::vector<ValidateMinerTxHook*> m_validate_miner_tx_hooks;
checkpoints m_checkpoints;
bool m_enforce_dns_checkpoints;
HardFork *m_hardfork;
network_type m_nettype;

View File

@ -111,10 +111,6 @@ namespace cryptonote
"offline"
, "Do not listen for peers, nor connect to any"
};
const command_line::arg_descriptor<bool> arg_disable_dns_checkpoints = {
"disable-dns-checkpoints"
, "Do not retrieve checkpoints from DNS"
};
const command_line::arg_descriptor<size_t> arg_block_download_max_size = {
"block-download-max-size"
, "Set maximum size of block download queue in bytes (0 for default)"
@ -135,11 +131,6 @@ namespace cryptonote
, "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."
, 0
};
static const command_line::arg_descriptor<bool> arg_dns_checkpoints = {
"enforce-dns-checkpointing"
, "checkpoints from DNS server will be enforced"
, false
};
static const command_line::arg_descriptor<uint64_t> arg_fast_block_sync = {
"fast-block-sync"
, "Sync up most of the way by using embedded, known block hashes."
@ -230,9 +221,7 @@ namespace cryptonote
m_starter_message_showed(false),
m_target_blockchain_height(0),
m_checkpoints_path(""),
m_last_dns_checkpoints_update(0),
m_last_json_checkpoints_update(0),
m_disable_dns_checkpoints(false),
m_update_download(0),
m_nettype(UNDEFINED),
m_update_available(false),
@ -248,41 +237,18 @@ namespace cryptonote
else
m_pprotocol = &m_protocol_stub;
}
//-----------------------------------------------------------------------------------
void core::set_checkpoints(checkpoints&& chk_pts)
{
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()
{
if (m_nettype != MAINNET || m_disable_dns_checkpoints) return true;
if (m_nettype != MAINNET) return true;
if (m_checkpoints_updating.test_and_set()) return true;
bool res = true;
if (time(NULL) - m_last_dns_checkpoints_update >= 3600)
if (time(NULL) - m_last_json_checkpoints_update >= 600)
{
res = m_blockchain_storage.update_checkpoints(m_checkpoints_path, true);
m_last_dns_checkpoints_update = time(NULL);
res = m_blockchain_storage.update_checkpoints(m_checkpoints_path);
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);
}
m_checkpoints_updating.clear();
// if anything fishy happened getting new checkpoints, bring down the house
@ -319,7 +285,6 @@ namespace cryptonote
command_line::add_arg(desc, arg_stagenet_on);
command_line::add_arg(desc, arg_regtest_on);
command_line::add_arg(desc, arg_fixed_difficulty);
command_line::add_arg(desc, arg_dns_checkpoints);
command_line::add_arg(desc, arg_prep_blocks_threads);
command_line::add_arg(desc, arg_fast_block_sync);
command_line::add_arg(desc, arg_show_time_stats);
@ -329,7 +294,6 @@ namespace cryptonote
command_line::add_arg(desc, arg_no_fluffy_blocks);
command_line::add_arg(desc, arg_test_dbg_lock_sleep);
command_line::add_arg(desc, arg_offline);
command_line::add_arg(desc, arg_disable_dns_checkpoints);
command_line::add_arg(desc, arg_block_download_max_size);
command_line::add_arg(desc, arg_max_txpool_weight);
command_line::add_arg(desc, arg_service_node);
@ -362,28 +326,25 @@ namespace cryptonote
auto data_dir = boost::filesystem::path(m_config_folder);
if (m_nettype == MAINNET)
// Init Checkpoints
{
cryptonote::checkpoints checkpoints;
if (!checkpoints.init_default_checkpoints(m_nettype))
{
throw std::runtime_error("Failed to initialize checkpoints");
}
set_checkpoints(std::move(checkpoints));
m_blockchain_storage.set_checkpoints(std::move(checkpoints));
boost::filesystem::path json(JSON_HASH_FILE_NAME);
boost::filesystem::path checkpoint_json_hashfile_fullpath = data_dir / json;
set_checkpoints_file_path(checkpoint_json_hashfile_fullpath.string());
m_checkpoints_path = checkpoint_json_hashfile_fullpath.string();
}
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
m_pad_transactions = get_arg(vm, arg_pad_transactions);
m_offline = get_arg(vm, arg_offline);
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
MWARNING(arg_fluffy_blocks.name << " is obsolete, it is now default");
@ -719,9 +680,9 @@ namespace cryptonote
MGINFO("Loading checkpoints");
// load json & DNS checkpoints, and verify them
// load json 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.");
CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json conflicted with existing checkpoints.");
// DNS versions checking
if (check_updates_string == "disabled")
@ -1408,6 +1369,42 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::relay_checkpoint_votes()
{
const time_t now = time(nullptr);
// Get relayable votes
NOTIFY_NEW_CHECKPOINT_VOTE::request req = {};
std::vector<service_nodes::checkpoint_vote *> relayed_votes;
for (Blockchain::service_node_checkpoint_pool_entry &pool_entry: m_blockchain_storage.m_checkpoint_pool)
{
for (service_nodes::checkpoint_vote &vote : pool_entry.votes)
{
const time_t elapsed = now - vote.time_last_sent_p2p;
const time_t RELAY_THRESHOLD = 60 * 2;
if (elapsed > RELAY_THRESHOLD)
{
relayed_votes.push_back(&vote);
req.votes.push_back(vote);
}
}
}
// Relay and update timestamp of when we last sent the vote
if (!req.votes.empty())
{
cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
if (get_protocol()->relay_checkpoint_votes(req, fake_context))
{
for (service_nodes::checkpoint_vote *vote : relayed_votes)
vote->time_last_sent_p2p = now;
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
{
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce);
@ -1771,6 +1768,7 @@ namespace cryptonote
m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this));
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_deregisters_auto_relayer.do_call(boost::bind(&core::relay_deregister_votes, this));
m_checkpoint_auto_relayer.do_call(boost::bind(&core::relay_checkpoint_votes, this));
// m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this));
m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this));
@ -2085,9 +2083,14 @@ namespace cryptonote
return si.available;
}
//-----------------------------------------------------------------------------------------------
const std::shared_ptr<const service_nodes::quorum_state> core::get_quorum_state(uint64_t height) const
const std::shared_ptr<const service_nodes::quorum_uptime_proof> core::get_uptime_quorum(uint64_t height) const
{
return m_service_node_list.get_quorum_state(height);
return m_service_node_list.get_uptime_quorum(height);
}
//-----------------------------------------------------------------------------------------------
const std::shared_ptr<const service_nodes::quorum_checkpointing> core::get_checkpointing_quorum(uint64_t height) const
{
return m_service_node_list.get_checkpointing_quorum(height);
}
//-----------------------------------------------------------------------------------------------
bool core::is_service_node(const crypto::public_key& pubkey) const
@ -2135,8 +2138,8 @@ namespace cryptonote
return false;
}
const auto quorum_state = m_service_node_list.get_quorum_state(vote.block_height);
if (!quorum_state)
const auto uptime_quorum = m_service_node_list.get_uptime_quorum(vote.block_height);
if (!uptime_quorum)
{
vvc.m_verification_failed = true;
vvc.m_invalid_block_height = true;
@ -2146,7 +2149,7 @@ namespace cryptonote
cryptonote::transaction deregister_tx;
int hf_version = m_blockchain_storage.get_current_hard_fork_version();
bool result = m_deregister_vote_pool.add_vote(hf_version, vote, vvc, *quorum_state, deregister_tx);
bool result = m_deregister_vote_pool.add_vote(hf_version, vote, vvc, *uptime_quorum, deregister_tx);
if (result && vvc.m_full_tx_deregister_made)
{
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
@ -2165,6 +2168,82 @@ namespace cryptonote
return result;
}
//-----------------------------------------------------------------------------------------------
bool core::add_checkpoint_vote(const service_nodes::checkpoint_vote& vote, vote_verification_context &vvc)
{
// TODO(doyle): Not in this function but, need to add code for culling old
// checkpoints from the "staging" checkpoint pool.
// TODO(doyle): This is repeated logic for deregister votes and
// checkpointing votes, it is worth considering merging the two into
// a generic voting structure
// Check Vote Age
{
uint64_t const latest_height = std::max(get_current_blockchain_height(), get_target_blockchain_height());
if (vote.block_height >= latest_height)
return false;
uint64_t vote_age = latest_height - vote.block_height;
if (vote_age > ((service_nodes::CHECKPOINT_INTERVAL * 3) - 1))
return false;
}
// Validate Vote
{
const std::shared_ptr<const service_nodes::quorum_uptime_proof> state = get_uptime_quorum(vote.block_height);
if (!state)
{
// TODO(loki): Fatal error
LOG_ERROR("Quorum state for height: " << vote.block_height << " was not cached in daemon!");
return false;
}
if (vote.voters_quorum_index >= state->quorum_nodes.size())
{
LOG_PRINT_L1("TODO(doyle): CHECKPOINTING(doyle): Writeme");
return false;
}
// NOTE(loki): We don't validate that the hash belongs to a valid block
// just yet, just that the signature is valid.
crypto::public_key const &voters_pub_key = state->quorum_nodes[vote.voters_quorum_index];
if (!crypto::check_signature(vote.block_hash, voters_pub_key, vote.signature))
{
LOG_PRINT_L1("TODO(doyle): CHECKPOINTING(doyle): Writeme");
return false;
}
}
// TODO(doyle): CHECKPOINTING(doyle): We need to check the hash they're voting on matches across votes
// Get Matching Checkpoint
std::vector<Blockchain::service_node_checkpoint_pool_entry> &checkpoint_pool = m_blockchain_storage.m_checkpoint_pool;
auto it = std::find_if(checkpoint_pool.begin(), checkpoint_pool.end(), [&vote](Blockchain::service_node_checkpoint_pool_entry const &checkpoint) {
return (checkpoint.height == vote.block_height);
});
if (it == checkpoint_pool.end())
{
Blockchain::service_node_checkpoint_pool_entry pool_entry = {};
pool_entry.height = vote.block_height;
checkpoint_pool.push_back(pool_entry);
it = (checkpoint_pool.end() - 1);
}
// Add Vote if Unique to Checkpoint
Blockchain::service_node_checkpoint_pool_entry &pool_entry = (*it);
auto vote_it = std::find_if(pool_entry.votes.begin(), pool_entry.votes.end(), [&vote](service_nodes::checkpoint_vote const &preexisting_vote) {
return (preexisting_vote.voters_quorum_index == vote.voters_quorum_index);
});
if (vote_it == pool_entry.votes.end())
{
m_blockchain_storage.add_checkpoint_vote(vote);
pool_entry.votes.push_back(vote);
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_service_node_keys(crypto::public_key &pub_key, crypto::secret_key &sec_key) const
{
if (m_service_node)

View File

@ -82,6 +82,8 @@ namespace cryptonote
{
public:
void debug__print_checkpoints() { m_blockchain_storage.debug__print_checkpoints(); }
/**
* @brief constructor
*
@ -411,34 +413,6 @@ namespace cryptonote
*/
void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol);
/**
* @copydoc Blockchain::set_checkpoints
*
* @note see Blockchain::set_checkpoints()
*/
void set_checkpoints(checkpoints&& chk_pts);
/**
* @brief set the file path to read from when loading checkpoints
*
* @param path the path to set ours as
*/
void set_checkpoints_file_path(const std::string& path);
/**
* @brief set whether or not we enforce DNS checkpoints
*
* @param enforce_dns enforce DNS checkpoints or not
*/
void set_enforce_dns_checkpoints(bool enforce_dns);
/**
* @brief set whether or not to enable or disable DNS checkpoints
*
* @param disble whether to disable DNS checkpoints
*/
void disable_dns_checkpoints(bool disable = true) { m_disable_dns_checkpoints = disable; }
/**
* @copydoc tx_memory_pool::have_tx
*
@ -814,7 +788,16 @@ namespace cryptonote
* @return Null shared ptr if quorum has not been determined yet for height
*/
const std::shared_ptr<const service_nodes::quorum_state> get_quorum_state(uint64_t height) const;
const std::shared_ptr<const service_nodes::quorum_uptime_proof> get_uptime_quorum(uint64_t height) const;
/**
* @brief Get the deterministic list of service node's public keys for nodes responsible for checkpointing
*
* @param height Block height to deterministically recreate the quorum list from
* @return Null shared ptr if quorum has not been determined yet for height
*/
const std::shared_ptr<const service_nodes::quorum_checkpointing> get_checkpointing_quorum(uint64_t height) const;
/**
* @brief Get a non owning reference to the list of blacklisted key images
@ -830,14 +813,15 @@ namespace cryptonote
*/
std::vector<service_nodes::service_node_pubkey_info> get_service_node_list_state(const std::vector<crypto::public_key>& service_node_pubkeys) const;
/**
* @brief get whether `pubkey` is known as a service node
*
* @param pubkey the public key to test
*
* @return whether `pubkey` is known as a service node
*/
bool is_service_node(const crypto::public_key& pubkey) const;
/**
* @brief get whether `pubkey` is known as a service node
*
* @param pubkey the public key to test
*
* @return whether `pubkey` is known as a service node
*/
bool is_service_node(const crypto::public_key& pubkey) const;
/**
* @brief Add a vote to deregister a service node from network
*
@ -847,6 +831,16 @@ namespace cryptonote
*/
bool add_deregister_vote(const service_nodes::deregister_vote& vote, vote_verification_context &vvc);
/**
* @brief TODO(doyle): CHECKPOINTING(doyle):
*
* @param TODO(doyle): CHECKPOINTING(doyle):
* @return
*/
bool add_checkpoint_vote(const service_nodes::checkpoint_vote& vote, vote_verification_context &vvc);
/**
* @brief Get the keypair for this service node.
@ -1074,6 +1068,13 @@ namespace cryptonote
*/
bool relay_txpool_transactions();
/**
* @brief attempt to relay the pooled checkpoint votes
*
* @return true, necessary for binding this function to a periodic invoker
*/
bool relay_checkpoint_votes();
/**
* @brief checks DNS versions
*
@ -1133,7 +1134,6 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled
epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
epee::math_helper::once_a_time_seconds<60*2, false> m_deregisters_auto_relayer; //!< interval for checking re-relaying deregister votes
epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space
epee::math_helper::once_a_time_seconds<UPTIME_PROOF_BUFFER_IN_SECONDS, true> m_check_uptime_proof_interval; //!< interval for checking our own uptime proof
@ -1141,6 +1141,9 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate
epee::math_helper::once_a_time_seconds<60*60*5, true> m_blockchain_pruning_interval; //!< interval for incremental blockchain pruning
epee::math_helper::once_a_time_seconds<60*2, false> m_deregisters_auto_relayer;
epee::math_helper::once_a_time_seconds<60*2, false> m_checkpoint_auto_relayer;
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?
uint64_t m_target_blockchain_height; //!< blockchain height target
@ -1150,11 +1153,9 @@ namespace cryptonote
std::atomic<bool> m_update_available;
std::string m_checkpoints_path; //!< path to json checkpoints file
time_t m_last_dns_checkpoints_update; //!< time when dns checkpoints were last updated
time_t m_last_json_checkpoints_update; //!< time when json checkpoints were last updated
std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once
bool m_disable_dns_checkpoints;
bool m_service_node;
crypto::secret_key m_service_node_key;

View File

@ -90,16 +90,16 @@ namespace service_nodes
static bool verify_votes_helper(cryptonote::network_type nettype, const cryptonote::tx_extra_service_node_deregister& deregister,
cryptonote::vote_verification_context &vvc,
const service_nodes::quorum_state &quorum_state)
const service_nodes::quorum_uptime_proof &uptime_quorum)
{
if (deregister.service_node_index >= quorum_state.nodes_to_test.size())
if (deregister.service_node_index >= uptime_quorum.nodes_to_test.size())
{
vvc.m_service_node_index_out_of_bounds = true;
LOG_PRINT_L1("Service node index in deregister vote was out of bounds: " << deregister.service_node_index << ", expected to be in range of: [0, " << quorum_state.nodes_to_test.size() << ")");
LOG_PRINT_L1("Service node index in deregister vote was out of bounds: " << deregister.service_node_index << ", expected to be in range of: [0, " << uptime_quorum.nodes_to_test.size() << ")");
return false;
}
const std::vector<crypto::public_key>& quorum = quorum_state.quorum_nodes;
const std::vector<crypto::public_key>& quorum = uptime_quorum.quorum_nodes;
std::vector<int8_t> quorum_set;
std::vector<std::pair<crypto::public_key, crypto::signature>> keys_and_sigs;
@ -135,7 +135,7 @@ namespace service_nodes
bool deregister_vote::verify_deregister(cryptonote::network_type nettype, const cryptonote::tx_extra_service_node_deregister& deregister,
cryptonote::vote_verification_context &vvc,
const service_nodes::quorum_state &quorum_state)
const service_nodes::quorum_uptime_proof &uptime_quorum)
{
if (deregister.votes.size() < service_nodes::MIN_VOTES_TO_KICK_SERVICE_NODE)
{
@ -144,18 +144,18 @@ namespace service_nodes
return false;
}
bool result = verify_votes_helper(nettype, deregister, vvc, quorum_state);
bool result = verify_votes_helper(nettype, deregister, vvc, uptime_quorum);
return result;
}
bool deregister_vote::verify_vote(cryptonote::network_type nettype, const deregister_vote& v, cryptonote::vote_verification_context &vvc,
const service_nodes::quorum_state &quorum_state)
const service_nodes::quorum_uptime_proof &uptime_quorum)
{
cryptonote::tx_extra_service_node_deregister deregister;
deregister.block_height = v.block_height;
deregister.service_node_index = v.service_node_index;
deregister.votes.push_back(cryptonote::tx_extra_service_node_deregister::vote{ v.signature, v.voters_quorum_index });
return verify_votes_helper(nettype, deregister, vvc, quorum_state);
return verify_votes_helper(nettype, deregister, vvc, uptime_quorum);
}
void deregister_vote_pool::set_relayed(const std::vector<deregister_vote>& votes)
@ -217,10 +217,10 @@ namespace service_nodes
bool deregister_vote_pool::add_vote(const int hf_version,
const deregister_vote& new_vote,
cryptonote::vote_verification_context& vvc,
const service_nodes::quorum_state &quorum_state,
const service_nodes::quorum_uptime_proof &uptime_quorum,
cryptonote::transaction &tx)
{
if (!deregister_vote::verify_vote(m_nettype, new_vote, vvc, quorum_state))
if (!deregister_vote::verify_vote(m_nettype, new_vote, vvc, uptime_quorum))
{
LOG_PRINT_L1("Signature verification failed for deregister vote");
return false;

View File

@ -47,7 +47,22 @@ namespace cryptonote
namespace service_nodes
{
struct quorum_state;
struct quorum_uptime_proof;
struct checkpoint_vote
{
uint64_t block_height;
crypto::hash block_hash;
uint32_t voters_quorum_index;
crypto::signature signature;
uint64_t time_last_sent_p2p;
};
struct voter_to_signature
{
uint16_t quorum_index;
crypto::signature signature;
};
struct deregister_vote
{
@ -65,10 +80,10 @@ namespace service_nodes
static bool verify_deregister(cryptonote::network_type nettype, const cryptonote::tx_extra_service_node_deregister& deregister,
cryptonote::vote_verification_context& vvc,
const service_nodes::quorum_state &quorum);
const service_nodes::quorum_uptime_proof &quorum);
static bool verify_vote(cryptonote::network_type nettype, const deregister_vote& v, cryptonote::vote_verification_context &vvc,
const service_nodes::quorum_state &quorum);
const service_nodes::quorum_uptime_proof &quorum);
};
class deregister_vote_pool
@ -80,7 +95,7 @@ namespace service_nodes
bool add_vote(const int hf_version,
const deregister_vote& new_vote,
cryptonote::vote_verification_context& vvc,
const service_nodes::quorum_state &quorum_state,
const service_nodes::quorum_uptime_proof &uptime_quorum,
cryptonote::transaction &tx);
// TODO(loki): Review relay behaviour and all the cases when it should be triggered

View File

@ -56,12 +56,16 @@ namespace service_nodes
if (hf_version == cryptonote::network_version_10_bulletproofs)
return service_node_info::version_1_swarms;
return service_node_info::version_2_infinite_staking;
if (hf_version == cryptonote::network_version_11_infinite_staking)
return service_node_info::version_2_infinite_staking;
return service_node_info::version_3_checkpointing;
}
service_node_list::service_node_list(cryptonote::Blockchain& blockchain)
: m_blockchain(blockchain), m_hooks_registered(false), m_height(0), m_db(nullptr), m_service_node_pubkey(nullptr)
: m_blockchain(blockchain), m_hooks_registered(false), m_db(nullptr), m_service_node_pubkey(nullptr)
{
m_transient_state = {};
}
void service_node_list::register_hooks(service_nodes::quorum_cop &quorum_cop)
@ -93,18 +97,18 @@ namespace service_nodes
uint64_t current_height = m_blockchain.get_current_blockchain_height();
bool loaded = load();
if (loaded && m_height == current_height) return;
if (loaded && m_transient_state.height == current_height) return;
if (!loaded || m_height > current_height) clear(true);
if (!loaded || m_transient_state.height > current_height) clear(true);
LOG_PRINT_L0("Recalculating service nodes list, scanning blockchain from height " << m_height);
LOG_PRINT_L0("Recalculating service nodes list, scanning blockchain from height " << m_transient_state.height);
LOG_PRINT_L0("This may take some time...");
std::vector<std::pair<cryptonote::blobdata, cryptonote::block>> blocks;
while (m_height < current_height)
while (m_transient_state.height < current_height)
{
blocks.clear();
if (!m_blockchain.get_blocks(m_height, 1000, blocks))
if (!m_blockchain.get_blocks(m_transient_state.height, 1000, blocks))
{
MERROR("Unable to initialize service nodes list");
return;
@ -132,7 +136,7 @@ namespace service_nodes
std::vector<crypto::public_key> service_node_list::get_service_nodes_pubkeys() const
{
std::vector<crypto::public_key> result;
for (const auto& iter : m_service_nodes_infos)
for (const auto& iter : m_transient_state.service_nodes_infos)
if (iter.second.is_fully_funded())
result.push_back(iter.first);
@ -143,15 +147,21 @@ namespace service_nodes
return result;
}
const std::shared_ptr<const quorum_state> service_node_list::get_quorum_state(uint64_t height) const
const std::shared_ptr<const quorum_uptime_proof> service_node_list::get_uptime_quorum(uint64_t height) const
{
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
const auto &it = m_quorum_states.find(height);
if (it != m_quorum_states.end())
{
return it->second;
}
const auto &it = m_transient_state.quorum_states.find(height);
if (it != m_transient_state.quorum_states.end())
return it->second.uptime_proof;
return nullptr;
}
const std::shared_ptr<const quorum_checkpointing> service_node_list::get_checkpointing_quorum(uint64_t height) const
{
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
const auto &it = m_transient_state.quorum_states.find(height);
if (it != m_transient_state.quorum_states.end())
return it->second.checkpointing;
return nullptr;
}
@ -162,9 +172,9 @@ namespace service_nodes
if (service_node_pubkeys.empty())
{
result.reserve(m_service_nodes_infos.size());
result.reserve(m_transient_state.service_nodes_infos.size());
for (const auto &it : m_service_nodes_infos)
for (const auto &it : m_transient_state.service_nodes_infos)
{
service_node_pubkey_info entry = {};
entry.pubkey = it.first;
@ -177,8 +187,8 @@ namespace service_nodes
result.reserve(service_node_pubkeys.size());
for (const auto &it : service_node_pubkeys)
{
const auto &find_it = m_service_nodes_infos.find(it);
if (find_it == m_service_nodes_infos.end())
const auto &find_it = m_transient_state.service_nodes_infos.find(it);
if (find_it == m_transient_state.service_nodes_infos.end())
continue;
service_node_pubkey_info entry = {};
@ -206,12 +216,12 @@ namespace service_nodes
bool service_node_list::is_service_node(const crypto::public_key& pubkey) const
{
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
return m_service_nodes_infos.find(pubkey) != m_service_nodes_infos.end();
return m_transient_state.service_nodes_infos.find(pubkey) != m_transient_state.service_nodes_infos.end();
}
bool service_node_list::is_key_image_locked(crypto::key_image const &check_image, uint64_t *unlock_height, service_node_info::contribution_t *the_locked_contribution) const
{
for (const auto& pubkey_info : m_service_nodes_infos)
for (const auto& pubkey_info : m_transient_state.service_nodes_infos)
{
const service_node_info &info = pubkey_info.second;
for (const service_node_info::contributor_t &contributor : info.contributors)
@ -308,12 +318,12 @@ namespace service_nodes
return false;
}
const auto state = get_quorum_state(deregister.block_height);
const auto state = get_uptime_quorum(deregister.block_height);
if (!state)
{
// TODO(loki): Not being able to find a quorum is fatal! We want better caching abilities.
MERROR("Quorum state for height: " << deregister.block_height << ", was not stored by the daemon");
MERROR("Uptime quorum for height: " << deregister.block_height << ", was not stored by the daemon");
return false;
}
@ -325,8 +335,8 @@ namespace service_nodes
const crypto::public_key& key = state->nodes_to_test[deregister.service_node_index];
auto iter = m_service_nodes_infos.find(key);
if (iter == m_service_nodes_infos.end())
auto iter = m_transient_state.service_nodes_infos.find(key);
if (iter == m_transient_state.service_nodes_infos.end())
return false;
if (m_service_node_pubkey && *m_service_node_pubkey == key)
@ -338,7 +348,7 @@ namespace service_nodes
LOG_PRINT_L1("Deregistration for service node: " << key);
}
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, key, iter->second)));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, key, iter->second)));
int hard_fork_version = m_blockchain.get_hard_fork_version(block_height);
if (hard_fork_version >= cryptonote::network_version_11_infinite_staking)
@ -350,15 +360,15 @@ namespace service_nodes
key_image_blacklist_entry entry = {};
entry.key_image = contribution.key_image;
entry.unlock_height = block_height + staking_num_lock_blocks(m_blockchain.nettype());
m_key_image_blacklist.push_back(entry);
m_transient_state.key_image_blacklist.push_back(entry);
const bool adding_to_blacklist = true;
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_key_image_blacklist(block_height, entry, adding_to_blacklist)));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_key_image_blacklist(block_height, entry, adding_to_blacklist)));
}
}
}
m_service_nodes_infos.erase(iter);
m_transient_state.service_nodes_infos.erase(iter);
return true;
}
@ -371,7 +381,7 @@ namespace service_nodes
/// Gather existing swarms from infos
swarm_snode_map_t existing_swarms;
for (const auto& entry : m_service_nodes_infos) {
for (const auto& entry : m_transient_state.service_nodes_infos) {
const auto id = entry.second.swarm_id;
existing_swarms[id].push_back(entry.first);
}
@ -386,11 +396,11 @@ namespace service_nodes
for (const auto snode : snodes) {
auto& sn_info = m_service_nodes_infos.at(snode);
auto& sn_info = m_transient_state.service_nodes_infos.at(snode);
if (sn_info.swarm_id == swarm_id) continue; /// nothing changed for this snode
/// modify info and record the change
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(height, snode, sn_info)));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(height, snode, sn_info)));
sn_info.swarm_id = swarm_id;
}
@ -634,8 +644,8 @@ namespace service_nodes
if (hard_fork_version >= cryptonote::network_version_11_infinite_staking)
{
// NOTE(loki): Grace period is not used anymore with infinite staking. So, if someone somehow reregisters, we just ignore it
const auto iter = m_service_nodes_infos.find(key);
if (iter != m_service_nodes_infos.end())
const auto iter = m_transient_state.service_nodes_infos.find(key);
if (iter != m_transient_state.service_nodes_infos.end())
return false;
if (m_service_node_pubkey && *m_service_node_pubkey == key) MGINFO_GREEN("Service node registered (yours): " << key << " on height: " << block_height);
@ -646,8 +656,8 @@ namespace service_nodes
// NOTE: A node doesn't expire until registration_height + lock blocks excess now which acts as the grace period
// So it is possible to find the node still in our list.
bool registered_during_grace_period = false;
const auto iter = m_service_nodes_infos.find(key);
if (iter != m_service_nodes_infos.end())
const auto iter = m_transient_state.service_nodes_infos.find(key);
if (iter != m_transient_state.service_nodes_infos.end())
{
if (hard_fork_version >= cryptonote::network_version_10_bulletproofs)
{
@ -684,8 +694,8 @@ namespace service_nodes
}
}
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_new(block_height, key)));
m_service_nodes_infos[key] = info;
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_new(block_height, key)));
m_transient_state.service_nodes_infos[key] = info;
return true;
}
@ -705,8 +715,8 @@ namespace service_nodes
}
/// Service node must be registered
auto iter = m_service_nodes_infos.find(pubkey);
if (iter == m_service_nodes_infos.end())
auto iter = m_transient_state.service_nodes_infos.find(pubkey);
if (iter == m_transient_state.service_nodes_infos.end())
{
LOG_PRINT_L1("Contribution TX: Contribution received for service node: " << pubkey <<
", but could not be found in the service node list on height: " << block_height <<
@ -765,7 +775,7 @@ namespace service_nodes
// Successfully Validated
//
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, pubkey, info)));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, pubkey, info)));
if (new_contributor)
{
service_node_info::contributor_t new_contributor = {};
@ -838,28 +848,28 @@ namespace service_nodes
// Remove old rollback events
//
{
assert(m_height == block_height);
++m_height;
assert(m_transient_state.height == block_height);
++m_transient_state.height;
const size_t ROLLBACK_EVENT_EXPIRATION_BLOCKS = 30;
uint64_t cull_height = (block_height < ROLLBACK_EVENT_EXPIRATION_BLOCKS) ? block_height : block_height - ROLLBACK_EVENT_EXPIRATION_BLOCKS;
while (!m_rollback_events.empty() && m_rollback_events.front()->m_block_height < cull_height)
while (!m_transient_state.rollback_events.empty() && m_transient_state.rollback_events.front()->m_block_height < cull_height)
{
m_rollback_events.pop_front();
m_transient_state.rollback_events.pop_front();
}
m_rollback_events.push_front(std::unique_ptr<rollback_event>(new prevent_rollback(cull_height)));
m_transient_state.rollback_events.push_front(std::unique_ptr<rollback_event>(new prevent_rollback(cull_height)));
}
//
// Remove expired blacklisted key images
//
for (auto entry = m_key_image_blacklist.begin(); entry != m_key_image_blacklist.end();)
for (auto entry = m_transient_state.key_image_blacklist.begin(); entry != m_transient_state.key_image_blacklist.end();)
{
if (block_height >= entry->unlock_height)
{
const bool adding_to_blacklist = false;
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_key_image_blacklist(block_height, (*entry), adding_to_blacklist)));
entry = m_key_image_blacklist.erase(entry);
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_key_image_blacklist(block_height, (*entry), adding_to_blacklist)));
entry = m_transient_state.key_image_blacklist.erase(entry);
}
else
entry++;
@ -871,8 +881,8 @@ namespace service_nodes
size_t expired_count = 0;
for (const crypto::public_key& pubkey : update_and_get_expired_nodes(txs, block_height))
{
auto i = m_service_nodes_infos.find(pubkey);
if (i != m_service_nodes_infos.end())
auto i = m_transient_state.service_nodes_infos.find(pubkey);
if (i != m_transient_state.service_nodes_infos.end())
{
if (m_service_node_pubkey && *m_service_node_pubkey == pubkey)
{
@ -883,10 +893,10 @@ namespace service_nodes
LOG_PRINT_L1("Service node expired: " << pubkey << " at block height: " << block_height);
}
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, pubkey, i->second)));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, pubkey, i->second)));
expired_count++;
m_service_nodes_infos.erase(i);
m_transient_state.service_nodes_infos.erase(i);
}
}
@ -895,16 +905,16 @@ namespace service_nodes
//
{
crypto::public_key winner_pubkey = cryptonote::get_service_node_winner_from_tx_extra(block.miner_tx.extra);
if (m_service_nodes_infos.count(winner_pubkey) == 1)
if (m_transient_state.service_nodes_infos.count(winner_pubkey) == 1)
{
m_rollback_events.push_back(
m_transient_state.rollback_events.push_back(
std::unique_ptr<rollback_event>(
new rollback_change(block_height, winner_pubkey, m_service_nodes_infos[winner_pubkey])
new rollback_change(block_height, winner_pubkey, m_transient_state.service_nodes_infos[winner_pubkey])
)
);
// set the winner as though it was re-registering at transaction index=UINT32_MAX for this block
m_service_nodes_infos[winner_pubkey].last_reward_block_height = block_height;
m_service_nodes_infos[winner_pubkey].last_reward_transaction_index = UINT32_MAX;
m_transient_state.service_nodes_infos[winner_pubkey].last_reward_block_height = block_height;
m_transient_state.service_nodes_infos[winner_pubkey].last_reward_transaction_index = UINT32_MAX;
}
}
@ -935,8 +945,8 @@ namespace service_nodes
if (!cryptonote::get_service_node_pubkey_from_tx_extra(tx.extra, snode_key))
continue;
auto it = m_service_nodes_infos.find(snode_key);
if (it == m_service_nodes_infos.end())
auto it = m_transient_state.service_nodes_infos.find(snode_key);
if (it == m_transient_state.service_nodes_infos.end())
continue;
service_node_info &node_info = (*it).second;
@ -990,27 +1000,25 @@ namespace service_nodes
//
// Update Quorum
//
generate_quorums(block);
const size_t cache_state_from_height = (block_height < QUORUM_LIFETIME) ? 0 : block_height - QUORUM_LIFETIME;
store_quorum_state_from_rewards_list(block_height);
while (!m_quorum_states.empty() && m_quorum_states.begin()->first < cache_state_from_height)
{
m_quorum_states.erase(m_quorum_states.begin());
}
while (!m_transient_state.quorum_states.empty() && m_transient_state.quorum_states.begin()->first < cache_state_from_height)
m_transient_state.quorum_states.erase(m_transient_state.quorum_states.begin());
}
void service_node_list::blockchain_detached(uint64_t height)
{
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
while (!m_rollback_events.empty() && m_rollback_events.back()->m_block_height >= height)
while (!m_transient_state.rollback_events.empty() && m_transient_state.rollback_events.back()->m_block_height >= height)
{
rollback_event *event = &(*m_rollback_events.back());
rollback_event *event = &(*m_transient_state.rollback_events.back());
bool rollback_applied = true;
switch(event->type)
{
case rollback_event::change_type:
{
auto *rollback = reinterpret_cast<rollback_change *>(event);
m_service_nodes_infos[rollback->m_key] = rollback->m_info;
m_transient_state.service_nodes_infos[rollback->m_key] = rollback->m_info;
}
break;
@ -1018,15 +1026,15 @@ namespace service_nodes
{
auto *rollback = reinterpret_cast<rollback_new *>(event);
auto iter = m_service_nodes_infos.find(rollback->m_key);
if (iter == m_service_nodes_infos.end())
auto iter = m_transient_state.service_nodes_infos.find(rollback->m_key);
if (iter == m_transient_state.service_nodes_infos.end())
{
MERROR("Could not find service node pubkey in rollback new");
rollback_applied = false;
break;
}
m_service_nodes_infos.erase(iter);
m_transient_state.service_nodes_infos.erase(iter);
}
break;
@ -1037,23 +1045,23 @@ namespace service_nodes
auto *rollback = reinterpret_cast<rollback_key_image_blacklist *>(event);
if (rollback->m_was_adding_to_blacklist)
{
auto it = std::find_if(m_key_image_blacklist.begin(), m_key_image_blacklist.end(),
auto it = std::find_if(m_transient_state.key_image_blacklist.begin(), m_transient_state.key_image_blacklist.end(),
[rollback] (key_image_blacklist_entry const &a) {
return (rollback->m_entry.unlock_height == a.unlock_height && rollback->m_entry.key_image == a.key_image);
});
if (it == m_key_image_blacklist.end())
if (it == m_transient_state.key_image_blacklist.end())
{
LOG_PRINT_L1("Could not find blacklisted key image to remove");
rollback_applied = false;
break;
}
m_key_image_blacklist.erase(it);
m_transient_state.key_image_blacklist.erase(it);
}
else
{
m_key_image_blacklist.push_back(rollback->m_entry);
m_transient_state.key_image_blacklist.push_back(rollback->m_entry);
}
}
break;
@ -1072,13 +1080,13 @@ namespace service_nodes
break;
}
m_rollback_events.pop_back();
m_transient_state.rollback_events.pop_back();
}
while (!m_quorum_states.empty() && (--m_quorum_states.end())->first >= height)
m_quorum_states.erase(--m_quorum_states.end());
while (!m_transient_state.quorum_states.empty() && (--m_transient_state.quorum_states.end())->first >= height)
m_transient_state.quorum_states.erase(--m_transient_state.quorum_states.end());
m_height = height;
m_transient_state.height = height;
store();
}
@ -1125,7 +1133,7 @@ namespace service_nodes
}
else
{
for (auto it = m_service_nodes_infos.begin(); it != m_service_nodes_infos.end(); it++)
for (auto it = m_transient_state.service_nodes_infos.begin(); it != m_transient_state.service_nodes_infos.end(); it++)
{
crypto::public_key const &snode_key = it->first;
service_node_info &info = it->second;
@ -1161,7 +1169,7 @@ namespace service_nodes
std::vector<std::pair<cryptonote::account_public_address, uint64_t>> winners;
const service_node_info& info = m_service_nodes_infos.at(key);
const service_node_info& info = m_transient_state.service_nodes_infos.at(key);
const uint64_t remaining_portions = STAKING_PORTIONS - info.portions_for_operator;
@ -1185,7 +1193,7 @@ namespace service_nodes
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
auto oldest_waiting = std::pair<uint64_t, uint32_t>(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint32_t>::max());
crypto::public_key key = crypto::null_pkey;
for (const auto& info : m_service_nodes_infos)
for (const auto& info : m_transient_state.service_nodes_infos)
if (info.second.is_fully_funded())
{
auto waiting_since = std::make_pair(info.second.last_reward_block_height, info.second.last_reward_transaction_index);
@ -1260,63 +1268,98 @@ namespace service_nodes
return true;
}
void service_node_list::store_quorum_state_from_rewards_list(uint64_t height)
std::vector<size_t> generate_shuffled_service_node_index_list(std::vector<crypto::public_key> const &snode_list, crypto::hash const &block_hash, quorum_type type)
{
const crypto::hash block_hash = m_blockchain.get_block_id_by_height(height);
if (block_hash == crypto::null_hash)
std::vector<size_t> result(snode_list.size());
size_t index = 0;
for (size_t i = 0; i < snode_list.size(); i++) result[i] = i;
uint64_t seed = 0;
std::memcpy(&seed, block_hash.data, std::min(sizeof(seed), sizeof(block_hash.data)));
seed += static_cast<uint64_t>(type);
loki_shuffle(result, seed);
return result;
}
void service_node_list::generate_quorums(cryptonote::block const &block)
{
crypto::hash block_hash;
uint64_t const height = cryptonote::get_block_height(block);
if (!cryptonote::get_block_hash(block, block_hash))
{
MERROR("Block height: " << height << " returned null hash");
return;
}
std::vector<crypto::public_key> full_node_list = get_service_nodes_pubkeys();
std::vector<size_t> pub_keys_indexes(full_node_list.size());
std::vector<crypto::public_key> const snode_list = get_service_nodes_pubkeys();
for (int type_int = 0; type_int < static_cast<int>(quorum_type::count); type_int++)
{
size_t index = 0;
for (size_t i = 0; i < full_node_list.size(); i++) { pub_keys_indexes[i] = i; }
auto type = static_cast<quorum_type>(type_int);
std::vector<size_t> const pub_keys_indexes = generate_shuffled_service_node_index_list(snode_list, block_hash, type);
// Shuffle indexes
uint64_t seed = 0;
std::memcpy(&seed, block_hash.data, std::min(sizeof(seed), sizeof(block_hash.data)));
loki_shuffle(pub_keys_indexes, seed);
}
// Assign indexes from shuffled list into quorum and list of nodes to test
auto new_state = std::make_shared<quorum_state>();
{
std::vector<crypto::public_key>& quorum = new_state->quorum_nodes;
switch(type)
{
quorum.resize(std::min(full_node_list.size(), QUORUM_SIZE));
for (size_t i = 0; i < quorum.size(); i++)
case quorum_type::uptime_proof:
{
size_t node_index = pub_keys_indexes[i];
const crypto::public_key &key = full_node_list[node_index];
quorum[i] = key;
// Assign indexes from shuffled list into quorum and list of nodes to test
auto new_state = std::make_shared<quorum_uptime_proof>();
std::vector<crypto::public_key>& quorum = new_state->quorum_nodes;
{
quorum.resize(std::min(snode_list.size(), QUORUM_SIZE));
for (size_t i = 0; i < quorum.size(); i++)
{
size_t node_index = pub_keys_indexes[i];
const crypto::public_key &key = snode_list[node_index];
quorum[i] = key;
}
}
std::vector<crypto::public_key>& nodes_to_test = new_state->nodes_to_test;
{
size_t num_remaining_nodes = pub_keys_indexes.size() - quorum.size();
size_t num_nodes_to_test = std::max(num_remaining_nodes/NTH_OF_THE_NETWORK_TO_TEST,
std::min(MIN_NODES_TO_TEST, num_remaining_nodes));
nodes_to_test.resize(num_nodes_to_test);
const int pub_keys_offset = quorum.size();
for (size_t i = 0; i < nodes_to_test.size(); i++)
{
size_t node_index = pub_keys_indexes[pub_keys_offset + i];
const crypto::public_key &key = snode_list[node_index];
nodes_to_test[i] = key;
}
}
m_transient_state.quorum_states[height].uptime_proof = new_state;
}
break;
case quorum_type::checkpointing:
{
auto new_state = std::make_shared<quorum_checkpointing>();
std::vector<crypto::public_key>& quorum = new_state->quorum_nodes;
quorum.resize(std::min(snode_list.size(), QUORUM_SIZE));
for (size_t i = 0; i < quorum.size(); i++)
{
size_t node_index = pub_keys_indexes[i];
const crypto::public_key &key = snode_list[node_index];
quorum[i] = key;
}
m_transient_state.quorum_states[height].checkpointing = new_state;
}
break;
default:
{
assert("Loki Developer Error: Unhandled enum" == 0);
}
break;
}
std::vector<crypto::public_key>& nodes_to_test = new_state->nodes_to_test;
{
size_t num_remaining_nodes = pub_keys_indexes.size() - quorum.size();
size_t num_nodes_to_test = std::max(num_remaining_nodes/NTH_OF_THE_NETWORK_TO_TEST,
std::min(MIN_NODES_TO_TEST, num_remaining_nodes));
nodes_to_test.resize(num_nodes_to_test);
const int pub_keys_offset = quorum.size();
for (size_t i = 0; i < nodes_to_test.size(); i++)
{
size_t node_index = pub_keys_indexes[pub_keys_offset + i];
const crypto::public_key &key = full_node_list[node_index];
nodes_to_test[i] = key;
}
}
}
m_quorum_states[height] = new_state;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1344,7 +1387,8 @@ namespace service_nodes
bool service_node_list::store()
{
if (m_blockchain.get_current_hard_fork_version() < cryptonote::network_version_9_service_nodes)
int hf_version = m_blockchain.get_current_hard_fork_version();
if (hf_version < cryptonote::network_version_9_service_nodes)
return true;
CHECK_AND_ASSERT_MES(m_db != nullptr, false, "Failed to store service node info, m_db == nullptr");
@ -1352,23 +1396,34 @@ namespace service_nodes
{
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
quorum_state_for_serialization quorum;
for(const auto& kv_pair : m_quorum_states)
for(const auto& kv_pair : m_transient_state.quorum_states)
{
quorum.height = kv_pair.first;
quorum.state = *kv_pair.second;
quorum_for_serialization quorum = {};
quorum.version = get_min_service_node_info_version_for_hf(hf_version);
quorum.height = kv_pair.first;
quorum_manager const &manager = kv_pair.second;
if (manager.uptime_proof)
quorum.uptime_quorum = *manager.uptime_proof;
if (quorum.version >= service_node_info::version_3_checkpointing)
{
if (manager.checkpointing)
quorum.checkpointing_quorum = *manager.checkpointing;
}
data_to_store.quorum_states.push_back(quorum);
}
service_node_pubkey_info info;
for (const auto& kv_pair : m_service_nodes_infos)
for (const auto& kv_pair : m_transient_state.service_nodes_infos)
{
info.pubkey = kv_pair.first;
info.info = kv_pair.second;
data_to_store.infos.push_back(info);
}
for (const auto& event_ptr : m_rollback_events)
for (const auto& event_ptr : m_transient_state.rollback_events)
{
switch (event_ptr->type)
{
@ -1382,11 +1437,10 @@ namespace service_nodes
}
}
data_to_store.key_image_blacklist = m_key_image_blacklist;
data_to_store.key_image_blacklist = m_transient_state.key_image_blacklist;
}
data_to_store.height = m_height;
int hf_version = m_blockchain.get_hard_fork_version(m_height - 1);
data_to_store.height = m_transient_state.height;
data_to_store.version = get_min_service_node_info_version_for_hf(hf_version);
std::stringstream ss;
@ -1406,12 +1460,12 @@ namespace service_nodes
void service_node_list::get_all_service_nodes_public_keys(std::vector<crypto::public_key>& keys, bool fully_funded_nodes_only) const
{
keys.clear();
keys.resize(m_service_nodes_infos.size());
keys.resize(m_transient_state.service_nodes_infos.size());
size_t i = 0;
if (fully_funded_nodes_only)
{
for (const auto &it : m_service_nodes_infos)
for (const auto &it : m_transient_state.service_nodes_infos)
{
service_node_info const &info = it.second;
if (info.is_fully_funded())
@ -1420,7 +1474,7 @@ namespace service_nodes
}
else
{
for (const auto &it : m_service_nodes_infos)
for (const auto &it : m_transient_state.service_nodes_infos)
keys[i++] = it.first;
}
}
@ -1452,17 +1506,24 @@ namespace service_nodes
bool r = ::serialization::serialize(ba, data_in);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse service node data from blob");
m_height = data_in.height;
m_key_image_blacklist = data_in.key_image_blacklist;
m_transient_state.height = data_in.height;
m_transient_state.key_image_blacklist = data_in.key_image_blacklist;
for (const auto& quorum : data_in.quorum_states)
for (const auto& states : data_in.quorum_states)
{
m_quorum_states[quorum.height] = std::make_shared<quorum_state>(quorum.state);
if (states.uptime_quorum.quorum_nodes.size() > 0)
m_transient_state.quorum_states[states.height].uptime_proof = std::make_shared<quorum_uptime_proof>(states.uptime_quorum);
if (states.version >= service_node_info::version_3_checkpointing)
{
if (states.checkpointing_quorum.quorum_nodes.size() > 0)
m_transient_state.quorum_states[states.height].checkpointing = std::make_shared<quorum_checkpointing>(states.checkpointing_quorum);
}
}
for (const auto& info : data_in.infos)
{
m_service_nodes_infos[info.pubkey] = info.info;
m_transient_state.service_nodes_infos[info.pubkey] = info.info;
}
for (const auto& event : data_in.events)
@ -1472,28 +1533,28 @@ namespace service_nodes
const auto& from = boost::get<rollback_change>(event);
auto *i = new rollback_change();
*i = from;
m_rollback_events.push_back(std::unique_ptr<rollback_event>(i));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(i));
}
else if (event.type() == typeid(rollback_new))
{
const auto& from = boost::get<rollback_new>(event);
auto *i = new rollback_new();
*i = from;
m_rollback_events.push_back(std::unique_ptr<rollback_event>(i));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(i));
}
else if (event.type() == typeid(prevent_rollback))
{
const auto& from = boost::get<prevent_rollback>(event);
auto *i = new prevent_rollback();
*i = from;
m_rollback_events.push_back(std::unique_ptr<rollback_event>(i));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(i));
}
else if (event.type() == typeid(rollback_key_image_blacklist))
{
const auto& from = boost::get<rollback_key_image_blacklist>(event);
auto *i = new rollback_key_image_blacklist();
*i = from;
m_rollback_events.push_back(std::unique_ptr<rollback_event>(i));
m_transient_state.rollback_events.push_back(std::unique_ptr<rollback_event>(i));
}
else
{
@ -1502,8 +1563,8 @@ namespace service_nodes
}
}
MGINFO("Service node data loaded successfully, m_height: " << m_height);
MGINFO(m_service_nodes_infos.size() << " nodes and " << m_rollback_events.size() << " rollback events loaded.");
MGINFO("Service node data loaded successfully, height: " << m_transient_state.height);
MGINFO(m_transient_state.service_nodes_infos.size() << " nodes and " << m_transient_state.rollback_events.size() << " rollback events loaded.");
LOG_PRINT_L1("service_node_list::load() returning success");
return true;
@ -1511,9 +1572,7 @@ namespace service_nodes
void service_node_list::clear(bool delete_db_entry)
{
m_service_nodes_infos.clear();
m_rollback_events.clear();
m_transient_state = {};
if (m_db && delete_db_entry)
{
m_db->block_txn_start(false/*readonly*/);
@ -1521,15 +1580,13 @@ namespace service_nodes
m_db->block_txn_stop();
}
m_quorum_states.clear();
uint64_t hardfork_9_from_height = 0;
{
uint32_t window, votes, threshold;
uint8_t voting;
m_blockchain.get_hard_fork_voting_info(9, window, votes, threshold, hardfork_9_from_height, voting);
}
m_height = hardfork_9_from_height;
m_transient_state.height = hardfork_9_from_height;
}
size_t service_node_info::total_num_locked_contributions() const

View File

@ -33,22 +33,10 @@
#include "serialization/serialization.h"
#include "cryptonote_core/service_node_rules.h"
#include "cryptonote_core/service_node_deregister.h"
#include "cryptonote_core/service_node_quorum_cop.h"
namespace service_nodes
{
class quorum_cop;
struct quorum_state
{
std::vector<crypto::public_key> quorum_nodes;
std::vector<crypto::public_key> nodes_to_test;
BEGIN_SERIALIZE()
FIELD(quorum_nodes)
FIELD(nodes_to_test)
END_SERIALIZE()
};
struct service_node_info // registration information
{
struct contribution_t
@ -71,6 +59,7 @@ namespace service_nodes
version_0,
version_1_swarms,
version_2_infinite_staking,
version_3_checkpointing,
};
struct contributor_t
@ -198,9 +187,11 @@ namespace service_nodes
void update_swarms(uint64_t height);
/// Note(maxim): this should not affect thread-safety as the returned object is const
const std::shared_ptr<const quorum_state> get_quorum_state(uint64_t height) const;
const std::shared_ptr<const quorum_uptime_proof> get_uptime_quorum (uint64_t height) const;
const std::shared_ptr<const quorum_checkpointing> get_checkpointing_quorum(uint64_t height) const;
std::vector<service_node_pubkey_info> get_service_node_list_state(const std::vector<crypto::public_key> &service_node_pubkeys) const;
const std::vector<key_image_blacklist_entry> &get_blacklisted_key_images() const { return m_key_image_blacklist; }
const std::vector<key_image_blacklist_entry> &get_blacklisted_key_images() const { return m_transient_state.key_image_blacklist; }
void set_db_pointer(cryptonote::BlockchainDB* db);
void set_my_service_node_keys(crypto::public_key const *pub_key);
@ -283,16 +274,19 @@ namespace service_nodes
};
typedef boost::variant<rollback_change, rollback_new, prevent_rollback, rollback_key_image_blacklist> rollback_event_variant;
struct quorum_state_for_serialization
struct quorum_for_serialization
{
uint8_t version;
uint64_t height;
quorum_state state;
quorum_uptime_proof uptime_quorum;
quorum_checkpointing checkpointing_quorum;
BEGIN_SERIALIZE()
FIELD(version)
FIELD(height)
FIELD(state)
FIELD(uptime_quorum)
if (version >= service_node_info::version_3_checkpointing)
FIELD(checkpointing_quorum)
END_SERIALIZE()
};
@ -300,7 +294,7 @@ namespace service_nodes
{
uint8_t version;
uint64_t height;
std::vector<quorum_state_for_serialization> quorum_states;
std::vector<quorum_for_serialization> quorum_states;
std::vector<service_node_pubkey_info> infos;
std::vector<rollback_event_variant> events;
std::vector<key_image_blacklist_entry> key_image_blacklist;
@ -327,8 +321,7 @@ namespace service_nodes
std::vector<crypto::public_key> get_service_nodes_pubkeys() const;
bool contribution_tx_output_has_correct_unlock_time(const cryptonote::transaction& tx, size_t i, uint64_t block_height) const;
void store_quorum_state_from_rewards_list(uint64_t height);
void generate_quorums(cryptonote::block const &block);
bool is_registration_tx(const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, service_node_info& info) const;
std::vector<crypto::public_key> update_and_get_expired_nodes(const std::vector<cryptonote::transaction> &txs, uint64_t block_height);
@ -337,20 +330,20 @@ namespace service_nodes
bool load();
mutable boost::recursive_mutex m_sn_mutex;
std::unordered_map<crypto::public_key, service_node_info> m_service_nodes_infos;
std::list<std::unique_ptr<rollback_event>> m_rollback_events;
cryptonote::Blockchain& m_blockchain;
bool m_hooks_registered;
cryptonote::Blockchain& m_blockchain;
bool m_hooks_registered;
crypto::public_key const *m_service_node_pubkey;
cryptonote::BlockchainDB *m_db;
using block_height = uint64_t;
block_height m_height;
crypto::public_key const *m_service_node_pubkey;
cryptonote::BlockchainDB* m_db;
std::vector<key_image_blacklist_entry> m_key_image_blacklist;
std::map<block_height, std::shared_ptr<const quorum_state>> m_quorum_states;
struct
{
std::unordered_map<crypto::public_key, service_node_info> service_nodes_infos;
std::vector<key_image_blacklist_entry> key_image_blacklist;
std::map<block_height, quorum_manager> quorum_states;
std::list<std::unique_ptr<rollback_event>> rollback_events;
block_height height;
} m_transient_state;
};
bool reg_tx_extract_fields(const cryptonote::transaction& tx, std::vector<cryptonote::account_public_address>& addresses, uint64_t& portions_for_operator, std::vector<uint64_t>& portions, uint64_t& expiration_timestamp, crypto::public_key& service_node_key, crypto::signature& signature, crypto::public_key& tx_pub_key);

View File

@ -32,6 +32,7 @@
#include "cryptonote_config.h"
#include "cryptonote_core.h"
#include "version.h"
#include "common/loki.h"
#include "common/loki_integration_test_hooks.h"
@ -41,31 +42,36 @@
namespace service_nodes
{
quorum_cop::quorum_cop(cryptonote::core& core)
: m_core(core), m_last_height(0)
: m_core(core), m_uptime_proof_height(0)
{
init();
}
void quorum_cop::init()
{
m_last_height = 0;
m_uptime_proof_height = 0;
m_uptime_proof_seen.clear();
}
void quorum_cop::blockchain_detached(uint64_t height)
{
if (m_last_height >= height)
if (m_uptime_proof_height >= height)
{
LOG_ERROR("The blockchain was detached to height: " << height << ", but quorum cop has already processed votes up to " << m_last_height);
LOG_ERROR("The blockchain was detached to height: " << height << ", but quorum cop has already processed votes up to " << m_uptime_proof_height);
LOG_ERROR("This implies a reorg occured that was over " << REORG_SAFETY_BUFFER_IN_BLOCKS << ". This should never happen! Please report this to the devs.");
m_last_height = height;
m_uptime_proof_height = height;
}
}
void quorum_cop::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
{
uint64_t const height = cryptonote::get_block_height(block);
process_uptime_quorum(block);
process_checkpoint_quorum(block);
}
void quorum_cop::process_uptime_quorum(cryptonote::block const &block)
{
uint64_t const height = cryptonote::get_block_height(block);
if (m_core.get_hard_fork_version(height) < 9)
return;
@ -87,7 +93,6 @@ namespace service_nodes
}
uint64_t const latest_height = std::max(m_core.get_current_blockchain_height(), m_core.get_target_blockchain_height());
if (latest_height < service_nodes::deregister_vote::VOTE_LIFETIME_BY_HEIGHT)
return;
@ -95,20 +100,19 @@ namespace service_nodes
if (height < execute_justice_from_height)
return;
if (m_last_height < execute_justice_from_height)
m_last_height = execute_justice_from_height;
if (m_uptime_proof_height < execute_justice_from_height)
m_uptime_proof_height = execute_justice_from_height;
for (;m_last_height < (height - REORG_SAFETY_BUFFER_IN_BLOCKS); m_last_height++)
for (;m_uptime_proof_height < (height - REORG_SAFETY_BUFFER_IN_BLOCKS); m_uptime_proof_height++)
{
if (m_core.get_hard_fork_version(m_last_height) < 9)
if (m_core.get_hard_fork_version(m_uptime_proof_height) < 9)
continue;
const std::shared_ptr<const quorum_state> state = m_core.get_quorum_state(m_last_height);
const std::shared_ptr<const quorum_uptime_proof> state = m_core.get_uptime_quorum(m_uptime_proof_height);
if (!state)
{
// TODO(loki): Fatal error
LOG_ERROR("Quorum state for height: " << m_last_height << "was not cached in daemon!");
LOG_ERROR("Quorum state for height: " << m_uptime_proof_height << " was not cached in daemon!");
continue;
}
@ -116,6 +120,9 @@ namespace service_nodes
if (it == state->quorum_nodes.end())
continue;
//
// NOTE: I am in the quorum
//
size_t my_index_in_quorum = it - state->quorum_nodes.begin();
for (size_t node_index = 0; node_index < state->nodes_to_test.size(); ++node_index)
{
@ -128,7 +135,7 @@ namespace service_nodes
continue;
service_nodes::deregister_vote vote = {};
vote.block_height = m_last_height;
vote.block_height = m_uptime_proof_height;
vote.service_node_index = node_index;
vote.voters_quorum_index = my_index_in_quorum;
vote.signature = service_nodes::deregister_vote::sign_vote(vote.block_height, vote.service_node_index, my_pubkey, my_seckey);
@ -142,6 +149,57 @@ namespace service_nodes
}
}
void quorum_cop::process_checkpoint_quorum(cryptonote::block const &block)
{
uint64_t const height = cryptonote::get_block_height(block);
if (m_core.get_hard_fork_version(height) < cryptonote::network_version_12_checkpointing)
return;
crypto::public_key my_pubkey;
crypto::secret_key my_seckey;
if (!m_core.get_service_node_keys(my_pubkey, my_seckey))
return;
if (height % CHECKPOINT_INTERVAL != 0)
return;
const std::shared_ptr<const quorum_checkpointing> state = m_core.get_checkpointing_quorum(height);
if (!state)
{
// TODO(loki): Fatal error
LOG_ERROR("Quorum state for height: " << height << " was not cached in daemon!");
return;
}
auto it = std::find(state->quorum_nodes.begin(), state->quorum_nodes.end(), my_pubkey);
if (it == state->quorum_nodes.end())
return;
//
// NOTE: I am in the quorum, handle checkpointing
//
size_t my_index_in_quorum = it - state->quorum_nodes.begin();
service_nodes::checkpoint_vote vote = {};
if (!cryptonote::get_block_hash(block, vote.block_hash))
{
// TODO(loki): Fatal error
LOG_ERROR("Could not get block hash for block on height: " << height);
return;
}
vote.block_height = height;
vote.voters_quorum_index = my_index_in_quorum;
crypto::generate_signature(vote.block_hash, my_pubkey, my_seckey, vote.signature);
cryptonote::vote_verification_context vvc = {};
if (!m_core.add_checkpoint_vote(vote, vvc))
{
// TODO(doyle): CHECKPOINTING(doyle):
LOG_ERROR("Failed to add checkpoint vote reason: " << print_vote_verification_context(vvc, nullptr));
}
}
static crypto::hash make_hash(crypto::public_key const &pubkey, uint64_t timestamp)
{
char buf[44] = "SUP"; // Meaningless magic bytes

View File

@ -29,6 +29,7 @@
#pragma once
#include "blockchain.h"
#include "serialization/serialization.h"
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "cryptonote_basic/blobdatatype.h"
@ -45,6 +46,38 @@ namespace service_nodes
uint16_t version_major, version_minor, version_patch;
};
struct quorum_checkpointing
{
std::vector<crypto::public_key> quorum_nodes;
BEGIN_SERIALIZE()
FIELD(quorum_nodes)
END_SERIALIZE()
};
struct quorum_uptime_proof
{
std::vector<crypto::public_key> quorum_nodes;
std::vector<crypto::public_key> nodes_to_test;
BEGIN_SERIALIZE()
FIELD(quorum_nodes)
FIELD(nodes_to_test)
END_SERIALIZE()
};
struct quorum_manager
{
std::shared_ptr<const quorum_uptime_proof> uptime_proof;
std::shared_ptr<const quorum_checkpointing> checkpointing;
};
enum struct quorum_type
{
uptime_proof = 0,
checkpointing,
count,
};
class quorum_cop
: public cryptonote::Blockchain::BlockAddedHook,
public cryptonote::Blockchain::BlockchainDetachedHook,
@ -57,6 +90,9 @@ namespace service_nodes
void block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs) override;
void blockchain_detached(uint64_t height) override;
void process_uptime_quorum (cryptonote::block const &block);
void process_checkpoint_quorum(cryptonote::block const &block);
bool handle_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROOF::request &proof);
static const uint64_t REORG_SAFETY_BUFFER_IN_BLOCKS = 20;
@ -71,7 +107,7 @@ namespace service_nodes
private:
cryptonote::core& m_core;
uint64_t m_last_height;
uint64_t m_uptime_proof_height;
std::unordered_map<crypto::public_key, proof_info> m_uptime_proof_seen;
mutable epee::critical_section m_lock;

View File

@ -10,6 +10,7 @@ namespace service_nodes {
constexpr size_t QUORUM_SIZE = 10;
constexpr size_t QUORUM_LIFETIME = (6 * deregister_vote::DEREGISTER_LIFETIME_BY_HEIGHT);
constexpr size_t MIN_VOTES_TO_KICK_SERVICE_NODE = 7;
constexpr size_t MIN_VOTES_TO_CHECKPOINT = MIN_VOTES_TO_KICK_SERVICE_NODE;
constexpr size_t NTH_OF_THE_NETWORK_TO_TEST = 100;
constexpr size_t MIN_NODES_TO_TEST = 50;
constexpr size_t MAX_SWARM_SIZE = 10;
@ -32,9 +33,12 @@ namespace service_nodes {
constexpr int MAX_KEY_IMAGES_PER_CONTRIBUTOR = 1;
constexpr uint64_t QUEUE_SWARM_ID = 0;
constexpr uint64_t KEY_IMAGE_AWAITING_UNLOCK_HEIGHT = 0;
constexpr uint64_t CHECKPOINT_INTERVAL = 4;
using swarm_id_t = uint64_t;
constexpr swarm_id_t UNASSIGNED_SWARM_ID = UINT64_MAX;
static_assert(MIN_VOTES_TO_KICK_SERVICE_NODE <= QUORUM_SIZE, "The number of votes required to kick can't exceed the actual quorum size, otherwise we never kick.");
static_assert(MIN_VOTES_TO_CHECKPOINT <= QUORUM_SIZE, "The number of votes required to kick can't exceed the actual quorum size, otherwise we never kick.");
inline uint64_t staking_num_lock_blocks(cryptonote::network_type nettype)
{

View File

@ -347,4 +347,20 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
/************************************************************************/
/* */
/************************************************************************/
struct NOTIFY_NEW_CHECKPOINT_VOTE
{
const static int ID = BC_COMMANDS_POOL_BASE + 12;
struct request
{
std::vector<service_nodes::checkpoint_vote> votes;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(votes)
END_KV_SERIALIZE_MAP()
};
};
}

View File

@ -94,6 +94,7 @@ namespace cryptonote
HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, &cryptonote_protocol_handler::handle_request_fluffy_missing_tx)
HANDLE_NOTIFY_T2(NOTIFY_NEW_DEREGISTER_VOTE, &cryptonote_protocol_handler::handle_notify_new_deregister_vote)
HANDLE_NOTIFY_T2(NOTIFY_UPTIME_PROOF, &cryptonote_protocol_handler::handle_uptime_proof)
HANDLE_NOTIFY_T2(NOTIFY_NEW_CHECKPOINT_VOTE, &cryptonote_protocol_handler::handle_notify_new_checkpoint_vote)
END_INVOKE_MAP2()
bool on_idle();
@ -130,13 +131,33 @@ namespace cryptonote
int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context);
int handle_notify_new_deregister_vote(int command, NOTIFY_NEW_DEREGISTER_VOTE::request& arg, cryptonote_connection_context& context);
int handle_uptime_proof(int command, NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& context);
int handle_notify_new_checkpoint_vote(int command, NOTIFY_NEW_CHECKPOINT_VOTE::request& arg, cryptonote_connection_context& context);
//----------------- i_bc_protocol_layout ---------------------------------------
template<class T>
bool relay_on_public_network_generic(typename T::request& arg, cryptonote_connection_context& exclude_context)
{
LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exclude_context) << "] post relay " << typeid(T).name() << " -->");
std::string arg_buff;
epee::serialization::store_t_to_binary(arg, arg_buff);
std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections;
m_p2p->for_each_connection([this, &exclude_context, &connections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)
{
epee::net_utils::zone zone = context.m_remote_address.get_zone();
if (peer_id && exclude_context.m_connection_id != context.m_connection_id && zone == epee::net_utils::zone::public_)
connections.push_back({zone, context.m_connection_id});
return true;
});
return m_p2p->relay_notify_to_list(T::ID, epee::strspan<uint8_t>(arg_buff), std::move(connections));
}
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context);
virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context);
virtual bool relay_deregister_votes(NOTIFY_NEW_DEREGISTER_VOTE::request& arg, cryptonote_connection_context& exclude_context);
//----------------- uptime proof ---------------------------------------
virtual bool relay_uptime_proof(NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context);
virtual bool relay_checkpoint_votes(NOTIFY_NEW_CHECKPOINT_VOTE::request& arg, cryptonote_connection_context& exclude_context);
//----------------------------------------------------------------------------------
//bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context);
bool should_drop_connection(cryptonote_connection_context& context, uint32_t next_stripe);

View File

@ -798,6 +798,50 @@ namespace cryptonote
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_checkpoint_vote(int command, NOTIFY_NEW_CHECKPOINT_VOTE::request& arg, cryptonote_connection_context& context)
{
MLOG_P2P_MESSAGE("Received NOTIFY_NEW_CHECKPOINT_VOTE (" << arg.votes.size() << " txes)");
if(context.m_state != cryptonote_connection_context::state_normal)
return 1;
if(!is_synchronized())
{
LOG_DEBUG_CC(context, "Received new checkpoint vote while syncing, ignored");
return 1;
}
for(auto it = arg.votes.begin(); it != arg.votes.end();)
{
cryptonote::vote_verification_context vvc = {};
m_core.add_checkpoint_vote(*it, vvc);
if (vvc.m_verification_failed)
{
LOG_PRINT_CCONTEXT_L1("Checkpoint vote verification failed, dropping connection");
drop_connection(context, false /*add_fail*/, false /*flush_all_spans i.e. delete cached block data from this peer*/);
return 1;
}
if (vvc.m_added_to_pool)
{
it++;
}
else
{
it = arg.votes.erase(it);
}
}
if (arg.votes.size())
{
relay_checkpoint_votes(arg, context);
}
return 1;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context)
{
MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash);
@ -2249,20 +2293,22 @@ skip:
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::relay_deregister_votes(NOTIFY_NEW_DEREGISTER_VOTE::request& arg, cryptonote_connection_context& exclude_context)
{
LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exclude_context) << "] post relay " << typeid(NOTIFY_NEW_DEREGISTER_VOTE).name() << " -->");
std::string arg_buff;
epee::serialization::store_t_to_binary(arg, arg_buff);
std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections;
m_p2p->for_each_connection([this, &exclude_context, &connections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)
{
epee::net_utils::zone zone = context.m_remote_address.get_zone();
if (peer_id && exclude_context.m_connection_id != context.m_connection_id && zone == epee::net_utils::zone::public_)
connections.push_back({zone, context.m_connection_id});
return true;
});
return m_p2p->relay_notify_to_list(NOTIFY_NEW_DEREGISTER_VOTE::ID, epee::strspan<uint8_t>(arg_buff), std::move(connections));
bool result = relay_on_public_network_generic<NOTIFY_NEW_DEREGISTER_VOTE>(arg, exclude_context);
return result;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::relay_uptime_proof(NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)
{
bool result = relay_on_public_network_generic<NOTIFY_UPTIME_PROOF>(arg, exclude_context);
return result;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::relay_checkpoint_votes(NOTIFY_NEW_CHECKPOINT_VOTE::request& arg, cryptonote_connection_context& exclude_context)
{
bool result = relay_on_public_network_generic<NOTIFY_NEW_CHECKPOINT_VOTE>(arg, exclude_context);
return result;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@ -2335,25 +2381,6 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::relay_uptime_proof(NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)
{
LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exclude_context) << "] post relay " << typeid(NOTIFY_UPTIME_PROOF).name() << " -->");
std::string arg_buff;
epee::serialization::store_t_to_binary(arg, arg_buff);
std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections;
m_p2p->for_each_connection([this, &exclude_context, &connections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)
{
epee::net_utils::zone zone = context.m_remote_address.get_zone();
if (peer_id && exclude_context.m_connection_id != context.m_connection_id && zone == epee::net_utils::zone::public_)
connections.push_back({zone, context.m_connection_id});
return true;
});
return m_p2p->relay_notify_to_list(NOTIFY_UPTIME_PROOF::ID, epee::strspan<uint8_t>(arg_buff), std::move(connections));
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
std::string t_cryptonote_protocol_handler<t_core>::get_peers_overview() const
{
std::stringstream ss;

View File

@ -45,6 +45,7 @@ namespace cryptonote
virtual bool relay_uptime_proof(NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)=0;
//virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0;
virtual bool relay_deregister_votes(NOTIFY_NEW_DEREGISTER_VOTE::request& arg, cryptonote_connection_context& exclude_context)=0;
virtual bool relay_checkpoint_votes(NOTIFY_NEW_CHECKPOINT_VOTE::request& arg, cryptonote_connection_context& exclude_context)=0;
};
/************************************************************************/
@ -64,6 +65,10 @@ namespace cryptonote
{
return false;
}
virtual bool relay_checkpoint_votes(NOTIFY_NEW_CHECKPOINT_VOTE::request& arg, cryptonote_connection_context& exclude_context)
{
return false;
}
virtual bool relay_uptime_proof(NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)
{
return false;

View File

@ -57,6 +57,8 @@ public:
, cryptonote::core_rpc_server* rpc_server = NULL
);
bool print_checkpoints(const std::vector<std::string>& args) { m_executor.print_checkpoints(); return true; }
bool print_peer_list(const std::vector<std::string>& args);
bool print_peer_list_stats(const std::vector<std::string>& args);

View File

@ -353,6 +353,11 @@ t_command_server::t_command_server(
, std::bind(&t_command_parser_executor::check_blockchain_pruning, &m_parser, p::_1)
, "Check the blockchain pruning."
);
m_command_lookup.set_handler(
"print_checkpoints"
, std::bind(&t_command_parser_executor::print_checkpoints, &m_parser, p::_1)
, ""
);
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
m_command_lookup.set_handler(

View File

@ -68,6 +68,8 @@ public:
~t_rpc_command_executor();
bool print_checkpoints() { m_rpc_server->on_get_checkpoints(); return true; }
bool print_peer_list(bool white = true, bool gray = true, size_t limit = 0);
bool print_peer_list_stats();

View File

@ -2458,18 +2458,18 @@ namespace cryptonote
PERF_TIMER(on_get_quorum_state);
bool r;
const auto quorum_state = m_core.get_quorum_state(req.height);
r = (quorum_state != nullptr);
const auto uptime_quorum = m_core.get_uptime_quorum(req.height);
r = (uptime_quorum != nullptr);
if (r)
{
res.status = CORE_RPC_STATUS_OK;
res.quorum_nodes.reserve (quorum_state->quorum_nodes.size());
res.nodes_to_test.reserve(quorum_state->nodes_to_test.size());
res.quorum_nodes.reserve (uptime_quorum->quorum_nodes.size());
res.nodes_to_test.reserve(uptime_quorum->nodes_to_test.size());
for (const auto &key : quorum_state->quorum_nodes)
for (const auto &key : uptime_quorum->quorum_nodes)
res.quorum_nodes.push_back(epee::string_tools::pod_to_hex(key));
for (const auto &key : quorum_state->nodes_to_test)
for (const auto &key : uptime_quorum->nodes_to_test)
res.nodes_to_test.push_back(epee::string_tools::pod_to_hex(key));
}
else
@ -2504,9 +2504,9 @@ namespace cryptonote
res.quorum_entries.reserve(height_end - height_begin + 1);
for (auto h = height_begin; h <= height_end; ++h)
{
const auto quorum_state = m_core.get_quorum_state(h);
const auto uptime_quorum = m_core.get_uptime_quorum(h);
if (!quorum_state) {
if (!uptime_quorum) {
failed_height = h;
break;
}
@ -2516,13 +2516,13 @@ namespace cryptonote
auto &entry = res.quorum_entries.back();
entry.height = h;
entry.quorum_nodes.reserve(quorum_state->quorum_nodes.size());
entry.nodes_to_test.reserve(quorum_state->nodes_to_test.size());
entry.quorum_nodes.reserve(uptime_quorum->quorum_nodes.size());
entry.nodes_to_test.reserve(uptime_quorum->nodes_to_test.size());
for (const auto &key : quorum_state->quorum_nodes)
for (const auto &key : uptime_quorum->quorum_nodes)
entry.quorum_nodes.push_back(epee::string_tools::pod_to_hex(key));
for (const auto &key : quorum_state->nodes_to_test)
for (const auto &key : uptime_quorum->nodes_to_test)
entry.nodes_to_test.push_back(epee::string_tools::pod_to_hex(key));
}
@ -2700,6 +2700,9 @@ namespace cryptonote
res.as_json = cryptonote::obj_to_json_str(pubkey_info_list);
}
}
res.height = m_core.get_current_blockchain_height();
res.block_hash = string_tools::pod_to_hex(m_core.get_block_id_by_height(res.height - 1));
for (auto &pubkey_info : pubkey_info_list)
{
@ -2743,6 +2746,7 @@ namespace cryptonote
entry.staking_requirement = pubkey_info.info.staking_requirement;
entry.portions_for_operator = pubkey_info.info.portions_for_operator;
entry.operator_address = cryptonote::get_account_address_as_str(m_core.get_nettype(), false/*is_subaddress*/, pubkey_info.info.operator_address);
entry.swarm_id = pubkey_info.info.swarm_id;
res.service_node_states.push_back(entry);
}

View File

@ -269,6 +269,8 @@ namespace cryptonote
bool on_get_staking_requirement(const COMMAND_RPC_GET_STAKING_REQUIREMENT::request& req, COMMAND_RPC_GET_STAKING_REQUIREMENT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
//-----------------------
void on_get_checkpoints() { m_core.debug__print_checkpoints(); }
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
void on_relay_uptime_and_votes()
{

View File

@ -2744,6 +2744,7 @@ namespace cryptonote
uint64_t total_reserved; // The total amount of Loki in atomic units reserved in this Service Node.
uint64_t staking_requirement; // The staking requirement in atomic units that is required to be contributed to become a Service Node.
uint64_t portions_for_operator; // The operator percentage cut to take from each reward expressed in portions, see cryptonote_config.h's STAKING_PORTIONS.
uint64_t swarm_id; // The identifier of the Service Node's current swarm.
std::string operator_address; // The wallet address of the operator to which the operator cut of the staking reward is sent to.
BEGIN_KV_SERIALIZE_MAP()
@ -2759,16 +2760,22 @@ namespace cryptonote
KV_SERIALIZE(total_reserved)
KV_SERIALIZE(staking_requirement)
KV_SERIALIZE(portions_for_operator)
KV_SERIALIZE(swarm_id)
KV_SERIALIZE(operator_address)
END_KV_SERIALIZE_MAP()
};
std::vector<entry> service_node_states; // Array of service node registration information
uint64_t height; // Current block's height.
std::string block_hash; // Current block's hash.
std::string status; // Generic RPC error code. "OK" is the success value.
std::string as_json; // If `include_json` is set in the request, this contains the json representation of the `entry` data structure
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(service_node_states)
KV_SERIALIZE(height)
KV_SERIALIZE(block_hash)
KV_SERIALIZE(status)
KV_SERIALIZE(as_json)
END_KV_SERIALIZE_MAP()

View File

@ -2973,7 +2973,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height).block_hash);
m_blockchain.trim(checkpoint_height);
short_chain_history.clear();
get_short_chain_history(short_chain_history);

View File

@ -109,6 +109,7 @@ namespace tests
uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes) { return 0; }
// TODO(loki): Write tests
bool add_deregister_vote(const service_nodes::deregister_vote& vote, cryptonote::vote_verification_context &vvc) { return false; }
bool add_checkpoint_vote(const service_nodes::checkpoint_vote& vote, cryptonote::vote_verification_context &vvc) { return false; }
bool pad_transactions() const { return false; }
uint32_t get_blockchain_pruning_seed() const { return 0; }
bool prune_blockchain(uint32_t pruning_seed) const { return true; }

View File

@ -50,7 +50,7 @@ struct gen_bp_tx_validation_base : public test_chain_unit_base
return !tvc.m_verifivation_failed && tx_added;
}
bool check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_idx, const std::vector<cryptonote::transaction>& /*txs*/)
bool check_tx_verification_context_array(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_idx, const std::vector<cryptonote::transaction>& /*txs*/)
{
size_t failed = 0;
for (const cryptonote::tx_verification_context &tvc: tvcs)

View File

@ -1759,3 +1759,21 @@ bool test_chain_unit_base::verify(const std::string& cb_name, cryptonote::core&
}
return cb_it->second(c, ev_index, events);
}
bool test_chain_unit_base::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/)
{
return !bvc.m_verifivation_failed;
}
bool test_chain_unit_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool /*tx_added*/, size_t /*event_index*/, const cryptonote::transaction& /*tx*/)
{
return !tvc.m_verifivation_failed;
}
bool test_chain_unit_base::check_tx_verification_context_array(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t /*tx_added*/, size_t /*event_index*/, const std::vector<cryptonote::transaction>& /*txs*/)
{
for (const cryptonote::tx_verification_context &tvc: tvcs)
if (tvc.m_verifivation_failed)
return false;
return true;
}

View File

@ -179,6 +179,10 @@ public:
void register_callback(const std::string& cb_name, verify_callback cb);
bool verify(const std::string& cb_name, cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events);
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/);
bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool /*tx_added*/, size_t /*event_index*/, const cryptonote::transaction& /*tx*/);
bool check_tx_verification_context_array(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t /*tx_added*/, size_t /*event_index*/, const std::vector<cryptonote::transaction>& /*txs*/);
private:
callbacks_map m_callbacks;
};
@ -790,78 +794,6 @@ uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cry
uint64_t get_unlocked_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx);
bool extract_hard_forks(const std::vector<test_event_entry>& events, v_hardforks_t& hard_forks);
//--------------------------------------------------------------------------
template<class t_test_class>
auto do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::transaction& tx, t_test_class& validator, int)
-> decltype(validator.check_tx_verification_context(tvc, tx_added, event_index, tx))
{
return validator.check_tx_verification_context(tvc, tx_added, event_index, tx);
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t /*event_index*/, const cryptonote::transaction& /*tx*/, t_test_class&, long)
{
// Default block verification context check
if (tvc.m_verifivation_failed)
throw std::runtime_error("Transaction verification failed");
return true;
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::transaction& tx, t_test_class& validator)
{
// SFINAE in action
return do_check_tx_verification_context(tvc, tx_added, event_index, tx, validator, 0);
}
//--------------------------------------------------------------------------
template<class t_test_class>
auto do_check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_index, const std::vector<cryptonote::transaction>& txs, t_test_class& validator, int)
-> decltype(validator.check_tx_verification_context(tvcs, tx_added, event_index, txs))
{
return validator.check_tx_verification_context(tvcs, tx_added, event_index, txs);
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool do_check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t /*event_index*/, const std::vector<cryptonote::transaction>& /*txs*/, t_test_class&, long)
{
// Default block verification context check
for (const cryptonote::tx_verification_context &tvc: tvcs)
if (tvc.m_verifivation_failed)
throw std::runtime_error("Transaction verification failed");
return true;
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool check_tx_verification_context(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_index, const std::vector<cryptonote::transaction>& txs, t_test_class& validator)
{
// SFINAE in action
return do_check_tx_verification_context(tvcs, tx_added, event_index, txs, validator, 0);
}
//--------------------------------------------------------------------------
template<class t_test_class>
auto do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator, int)
-> decltype(validator.check_block_verification_context(bvc, event_index, blk))
{
return validator.check_block_verification_context(bvc, event_index, blk);
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t /*event_index*/, const cryptonote::block& /*blk*/, t_test_class&, long)
{
// Default block verification context check
if (bvc.m_verifivation_failed)
throw std::runtime_error("Block verification failed");
return true;
}
//--------------------------------------------------------------------------
template<class t_test_class>
bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator)
{
// SFINAE in action
return do_check_block_verification_context(bvc, event_index, blk, validator, 0);
}
/************************************************************************/
/* */
/************************************************************************/
@ -917,7 +849,7 @@ public:
size_t pool_size = m_c.get_pool_transactions_count();
m_c.handle_incoming_tx(t_serializable_object_to_blob(tx), tvc, m_txs_keeped_by_block, false, false);
bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count();
bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator);
bool r = m_validator.check_tx_verification_context(tvc, tx_added, m_ev_index, tx);
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
return true;
}
@ -937,7 +869,7 @@ public:
size_t pool_size = m_c.get_pool_transactions_count();
m_c.handle_incoming_txs(tx_blobs, tvcs, m_txs_keeped_by_block, false, false);
size_t tx_added = m_c.get_pool_transactions_count() - pool_size;
bool r = check_tx_verification_context(tvcs, tx_added, m_ev_index, txs, m_validator);
bool r = m_validator.check_tx_verification_context_array(tvcs, tx_added, m_ev_index, txs);
CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed");
return true;
}
@ -948,7 +880,7 @@ public:
cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc);
m_c.handle_incoming_block(t_serializable_object_to_blob(b), &b, bvc);
bool r = check_block_verification_context(bvc, m_ev_index, b, m_validator);
bool r = m_validator.check_block_verification_context(bvc, m_ev_index, b);
CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed");
return r;
}
@ -981,8 +913,8 @@ public:
{
blk = cryptonote::block();
}
bool r = check_block_verification_context(bvc, m_ev_index, blk, m_validator);
CHECK_AND_NO_ASSERT_MES(r, false, "serialized block verification context check failed");
bool r = m_validator.check_block_verification_context(bvc, m_ev_index, blk);
CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed");
return true;
}
@ -1005,7 +937,7 @@ public:
tx = cryptonote::transaction();
}
bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator);
bool r = m_validator.check_tx_verification_context(tvc, tx_added, m_ev_index, tx);
CHECK_AND_NO_ASSERT_MES(r, false, "transaction verification context check failed");
return true;
}

View File

@ -691,9 +691,9 @@ bool sn_test_rollback::test_registrations(cryptonote::core& c, size_t ev_index,
tx_extra_service_node_deregister deregistration;
get_service_node_deregister_from_tx_extra(dereg_tx.extra, deregistration);
const auto quorum_state = c.get_quorum_state(deregistration.block_height);
CHECK_TEST_CONDITION(quorum_state);
const auto pk_a = quorum_state->nodes_to_test.at(deregistration.service_node_index);
const auto uptime_quorum = c.get_uptime_quorum(deregistration.block_height);
CHECK_TEST_CONDITION(uptime_quorum);
const auto pk_a = uptime_quorum->nodes_to_test.at(deregistration.service_node_index);
/// Check present
const bool found_a = contains(sn_list, pk_a);

View File

@ -91,6 +91,7 @@ public:
// TODO(loki): Write tests
bool add_deregister_vote(const service_nodes::deregister_vote& vote, cryptonote::vote_verification_context &vvc) { return true; }
bool add_checkpoint_vote(const service_nodes::checkpoint_vote& vote, cryptonote::vote_verification_context &vvc) { return true; }
};
typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_core>> Server;

View File

@ -123,7 +123,7 @@ TEST(service_nodes, vote_validation)
cryptonote::keypair service_node_voter = cryptonote::keypair::generate(hw::get_device("default"));
int voter_index = 0;
service_nodes::quorum_state state = {};
service_nodes::quorum_uptime_proof state = {};
{
state.quorum_nodes.resize(10);
state.nodes_to_test.resize(state.quorum_nodes.size());
@ -191,7 +191,7 @@ TEST(service_nodes, tx_extra_deregister_validation)
const size_t num_voters = 10;
cryptonote::keypair voters[num_voters] = {};
service_nodes::quorum_state state = {};
service_nodes::quorum_uptime_proof state = {};
{
state.quorum_nodes.resize(num_voters);
state.nodes_to_test.resize(num_voters);